DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • 4 Email Validation and Verification Techniques for App Developers
  • Doubly Linked List in Data Structures and Algorithms
  • Linked List in Data Structures and Algorithms
  • Own ChatGPT Bot in Telegram

Trending

  • Getting Started With Prometheus Workshop: Instrumenting Applications
  • Java Parallel GC Tuning
  • Choosing the Appropriate AWS Load Balancer: ALB vs. NLB
  • Deploy a Session Recording Solution Using Ansible and Audit Your Bastion Host
  1. DZone
  2. Software Design and Architecture
  3. Integration
  4. Stocks Notification: TradingView Webhook to Telegram

Stocks Notification: TradingView Webhook to Telegram

At the start of the year, I worked on a Telegram bot that sends market data of certain stocks for the previous day's close to a Telegram channel.

Han Chiang user avatar by
Han Chiang
·
Apr. 13, 23 · Tutorial
Like (1)
Save
Tweet
Share
3.31K Views

Join the DZone community and get the full member experience.

Join For Free

At the start of the year, I worked on a Telegram bot that sends market data of certain stocks for the previous day's close to a Telegram channel.

Features

  • Basic info such as closing price, EMA20, difference between closing price and EMA20
  • Overextension from EMA20 based on the median delta when stock reverse in the next few days

Example

Overview of the Workflow

  1. Create an indicator script in TradingView
  2. Add indicator to chart
  3. Set an alert using the indicator as condition, and configure webhook.
  4. Once an alert is fired, the server saves the data in Redis, and sends a notification to telegram before the market opens.

What's Not Covered

  • Infrastructure(set up server, HTTPS, etc), CICD, setting up an API server
  • Other data sources: VIX central

Create a Script in TradingView

PineScript Introduction

TradingView has its own programming language called PineScript, which allows us to create indicators and trading strategies.

Here are some recommended readings on PineScript:

  • Execution model explains how the script is executed on the chart, particularly the difference between historical and real-time bars.
  • Types and variable declaration.
  • Alerts to send data to a self-defined destination.

Create the Script

The script that I have created retrieves the closing price and the 1D Exponential Moving Average(EMA) 20 for some tickers.

It uses built-in functions ta.ema to get the EMA and request.security to get data for a particular ticker and timeframe, and alert.freq_once_per_bar_close to send the alert when the market closes.

Ticker Name

The ticker argument for request.security needs to be in the format <exchange><ticker>, which can be gotten from the address bar after searching for the ticker in tradingview.com.

For SPY, it is https://www.tradingview.com/symbols/AMEX-SPY/

Secret

There is a self-defined secret <your secret here>, which is used to authenticate the request in the server.

Shell
 
//@version=5

indicator("Market data notification")



ema20 = ta.ema(close, 20)



array_start = '['

array_end = ']'

json_start = '{'

json_end = '}'



build_json_array(res, symbol, timeframe, closee, ema20) =>

    res + json_start + '"symbol": ' + symbol + ', "timeframe": ' + timeframe + ', "close": ' + closee + ', "ema20": ' + ema20 + json_end + ','





string[] json_key_values = array.new_string(0)



array.push(json_key_values, '"secret": "<your secret here>"')



data_payload = '"data": ' + array_start

data_payload := build_json_array(data_payload, '"SPY"', '"1d"', str.tostring(request.security("AMEX:SPY", "1D", close)), str.tostring(request.security("AMEX:SPY", "1D", ema20)))

data_payload := build_json_array(data_payload, '"QQQ"', '"1d"', str.tostring(request.security("NASDAQ:QQQ", "1D", close)), str.tostring(request.security("NASDAQ:QQQ", "1D", ema20)))

data_payload := build_json_array(data_payload, '"DJIA"', '"1d"', str.tostring(request.security("AMEX:DJIA", "1D", close)), str.tostring(request.security("AMEX:DJIA", "1D", ema20)))

data_payload := build_json_array(data_payload, '"IWM"', '"1d"', str.tostring(request.security("AMEX:IWM", "1D", close)), str.tostring(request.security("AMEX:IWM", "1D", ema20)))

data_payload := build_json_array(data_payload, '"AAPL"', '"1d"', str.tostring(request.security("NASDAQ:AAPL", "1D", close)), str.tostring(request.security("NASDAQ:AAPL", "1D", ema20)))

data_payload := build_json_array(data_payload, '"AMD"', '"1d"', str.tostring(request.security("NASDAQ:AMD", "1D", close)), str.tostring(request.security("NASDAQ:AMD", "1D", ema20)))

data_payload := build_json_array(data_payload, '"AMZN"', '"1d"', str.tostring(request.security("NASDAQ:AMZN", "1D", close)), str.tostring(request.security("NASDAQ:AMZN", "1D", ema20)))

data_payload := build_json_array(data_payload, '"BABA"', '"1d"', str.tostring(request.security("NYSE:BABA", "1D", close)), str.tostring(request.security("NYSE:BABA", "1D", ema20)))

data_payload := build_json_array(data_payload, '"COIN"', '"1d"', str.tostring(request.security("NASDAQ:COIN", "1D", close)), str.tostring(request.security("NASDAQ:COIN", "1D", ema20)))

data_payload := build_json_array(data_payload, '"GOOGL"', '"1d"', str.tostring(request.security("NASDAQ:GOOGL", "1D", close)), str.tostring(request.security("NASDAQ:GOOGL", "1D", ema20)))

data_payload := build_json_array(data_payload, '"META"', '"1d"', str.tostring(request.security("NASDAQ:META", "1D", close)), str.tostring(request.security("NASDAQ:META", "1D", ema20)))

data_payload := build_json_array(data_payload, '"MSFT"', '"1d"', str.tostring(request.security("NASDAQ:MSFT", "1D", close)), str.tostring(request.security("NASDAQ:MSFT", "1D", ema20)))

data_payload := build_json_array(data_payload, '"NFLX"', '"1d"', str.tostring(request.security("NASDAQ:NFLX", "1D", close)), str.tostring(request.security("NASDAQ:NFLX", "1D", ema20)))

data_payload := build_json_array(data_payload, '"NVDA"', '"1d"', str.tostring(request.security("NASDAQ:NVDA", "1D", close)), str.tostring(request.security("NASDAQ:NVDA", "1D", ema20)))

data_payload := build_json_array(data_payload, '"TSLA"', '"1d"', str.tostring(request.security("NASDAQ:TSLA", "1D", close)), str.tostring(request.security("NASDAQ:TSLA", "1D", ema20)))

data_payload := build_json_array(data_payload, '"VIX"', '"1d"', str.tostring(request.security("TVC:VIX", "1D", close)), str.tostring(request.security("TVC:VIX", "1D", ema20)))



// remove trailing comma from array

data_payload := str.substring(data_payload, 0, str.length(data_payload) - 1)

data_payload := data_payload + array_end



array.push(json_key_values, data_payload)



array.push(json_key_values, '"test_mode": "false"')



result_json = json_start + array.join(json_key_values, ',') + json_end



alert(result_json, alert.freq_once_per_bar_close)

Add the Script in TradingView

Open the Pine Editor tab, add the script, and add it to the chart.

Set the Alert and Webhook

Set the alert using the indicator in the previous step as the condition, and configure webhook to send it to your server.


Note: Make sure the timeframe of the chart is set to daily(1D), so that the script will get triggered only once a day, when the market closes.

Data Storage: Redis

Redis is chosen because it fits my need for a key-value storage, and has the option to persist data.

Data Persistence

Redis offers 2 options: Redis Database(RDB) and Append Only File(AOF).

RDB is a snapshot of the data at specific intervals, while AOF is an append-only log of the write operations received by the server.

The tradeoffs of RDB and AOF complement each other, so it makes sense to use both.

For RDB, a snapshot will be created after 300 seconds and there is at least 1 change.

For AOF, write operations will be flushed to disk every second using the default fsync policy appendfsync everysec.

Data Structure

Sorted set is chosen to store the data because the elements are sorted by score, which is the timestamp, and supports finding elements by score quickly.

Receive Data from TradingView and Post to Telegram

We need a regular API server that can receive POST request from TradingView, and send it to telegram. I wrote it in Python, using the FastAPI framework, because it is just fast to develop with.

Webhook Authenticity Check

Since the webhook is a HTTP POST request that anyone can call, we need to ensure that it actually originates from tradingview, not someone else.

The first safeguard is to check the self-defined secret in the PineScript.

The second is to check if the request comes from one of TradingView's machines.

Create Telegram Bot

Set Up Bot and Channel 

Follow this guide to create a Telegram bot and note down the token, which is used to interact with the Telegram bot API.

Add the bot as an administrator to the channel so that it can send messages.

Send a Message to the Channel

The channel id of the channel is needed for the bot to send the message.

There are two ways of getting the channel id:

First, go to the channel on telegram web and look at the URL, which is something like https://web.telegram.org/k/#-YYYYYYYYY. The channel id will be -100YYYYYYYYYY.

The second method is to use a bot to tell you the channel id.

Use a Library to Send the Message

There are many client libraries for different languages. I chose python-telegram-bot.

Cron Job

Finally, the last piece of the puzzle is to schedule the message 30 minutes before the market opens(9.30AM local time) using a cron job.

This is the script for sending the message.

Timezone

One thing to note is that the timezone changes due to daylight savings time(DST). Without DST, the timezone is Pacific Standard Time(PST), which is UTC-5. With DST, it is Pacific Daylight Time(PDT) which is UTC-4.

On the application side, we just need to check against the local time:

Python
 
def should_run() -> bool:

    if config.get_is_testing_telegram():

        return True



    now = get_current_datetime()

    local = get_current_datetime()

    local = local.replace(hour=config.get_stocks_job_start_local_hour(), minute=config.get_stocks_job_start_local_minute())

    delta = now - local



    should_run = abs(delta.total_seconds()) <= config.get_job_delay_tolerance_second()

    print(

        f'local time: {local}, current time: {now}, local hour to run: {config.get_stocks_job_start_local_hour()}, local minute to run: {config.get_stocks_job_start_local_minute()}, current hour {now.hour}, current minute: {now.minute}, delta second: {delta.total_seconds()}, should run: {should_run}')

    return should_run

On the cron side, the script needs to run on the hour with and without DST.

This is the cron used to run the script at 9AM every day from monday to saturday:

Shell
 
# In UTC

0 13,14 * * 1-6

Local Development

Since a webhook requires a public HTTPS endpoint, we need a reverse proxy to tunnel traffic from the public to our local server.

Ngrok is simple and easy to use.

Summary

This is an overview of how to use push data from TradingView to our own server and send it to telegram.

TradingView supports more complex workflows such as executing trades based on certain strategies.

References

Telegram

My channel: https://t.me/+6RjlDOi8OyxkOGU1

Telegram bot API: https://core.telegram.org/bots/api

Python telegram bot: https://docs.python-telegram-bot.org/en/stable/

Code

GitHub link for backend: https://github.com/hanchiang/market-data-notification

GitHub link for infra setup and app automation: https://github.com/hanchiang/market-data-notification-infra

API server: https://github.com/hanchiang/market-data-notification/blob/master/src/server.py

Script for sending message: https://github.com/hanchiang/market-data-notification/blob/master/src/job/stocks.py

Redis

Persistence: https://redis.io/docs/management/persistence/

My persistence config: https://github.com/hanchiang/market-data-notification-infra/blob/master/images/scripts/install-redis.sh#L22-L25

TradingView PineScript

PineScript doc: https://www.tradingview.com/pine-script-docs/en/v5/index.html

PineScript reference: https://www.tradingview.com/pine-script-reference/v5/

My PineScript: https://www.tradingview.com/script/yKEUsKOl-Stock-data-alert-webhook-to-custom-API-server/

API Data storage Data structure Webhook

Published at DZone with permission of Han Chiang. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • 4 Email Validation and Verification Techniques for App Developers
  • Doubly Linked List in Data Structures and Algorithms
  • Linked List in Data Structures and Algorithms
  • Own ChatGPT Bot in Telegram

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: