Skip to content

haufung80/tradingview-webhooks-bot

Β 
Β 

Repository files navigation

demopic

tvwb_demo.webm

πŸ“€ Live Demo πŸ–₯

There's now a live demo available at http://tvwb.robswc.me.
Feel free to check it out or send it some webhooks!

DigitalOcean Referral Badge

The What πŸ”¬

Tradingview-webhooks-bot (TVWB) is a small, Python-based framework that allows you to extend or implement your own logic using data from Tradingview's webhooks. TVWB is not a trading library, it's a framework for building your own trading logic.

The How πŸ—

TVWB is fundamentally a set of components with a webapp serving as the GUI. TVWB was built with event-driven architecture in mind that provides you with the building blocks to extend or implement your own custom logic. TVWB uses Flask to handle the webhooks and provides you with a simple API to interact with the data.

Quickstart πŸ“˜

Installation

Hosting


Ensure you're in the src directory. When running the following commands, if you installed manually.
If you used docker, start the tvwb.py shell with docker-compose run app shell (in the project root directory) and omit the python3 tvwb.py portion of the commands.


Creating an action

python3 tvwb.py action:create NewAction --register

This creates an action and automatically registers it with the app. Learn more on registering here.

Note, action and event names should always be in PascalCase.

You can also check out some "pre-made" community actions!

Linking an action to an event

python3 tvwb.py action:link NewAction WebhookReceived

This links an action to the WebhookReceived event. The WebhookReceived event is fired when a webhook is received by the app and is currently the only default event.

Editing an action

Navigate to src/components/actions/NewAction.py and edit the run method. You will see something similar to the following code. Feel free to delete the "Custom run method" comment and replace it with your own logic. Below is an example of how you can access the webhook data.

class NewAction(Action):
    def __init__(self):
        super().__init__()

    def run(self, *args, **kwargs):
        super().run(*args, **kwargs)  # this is required
        """
        Custom run method. Add your custom logic here.
        """
        data = self.validate_data()  # always get data from webhook by calling this method!
        print('Data from webhook:', data)

Install initial dependency

(Ubuntu 20.04)
sudo apt update
sudo apt install python3-pip
git clone https://github.com/haufung80/tradingview-webhooks-bot.git
cd ~/tradingview-webhooks-bot/src
pip install -r requirements.txt
(create .env and config.ini to /src)
(set up postgresql in .env and create tradingview-webhooks-bot database)
alembic upgrade head

Running the app

python3 tvwb.py start

Sending a webhook

Navigate to http://localhost:5000. Ensure you see the WebhookReceived Event. Click "details" to expand the event box. Find the "Key" field and note the value. This is the key you will use to send a webhook to the app. Copy the JSON data below, replacing "YOUR_KEY_HERE" with the key you copied.

{
    "key": "YOUR_KEY_HERE",
    "message": "I'm a webhook!"
}

The key field is required, as it both authenticates the webhook and tells the app which event to fire. Besides that, you can send any data you want. The data will be available to your action via the validate_data() method. (see above, editing action)

On tradingview, create a new webhook with the above JSON data and send it to http://ipaddr:5000/webhook. You should see the data from the webhook printed to the console.

FAQs

So how do I actually trade?

To actually submit trades, you will have to use a library like ccxt for crypto currency. For other brokers, usually there are SDKs or APIs available. The general workflow would look something like: webhook signal -> tvwb (use ccxt here) -> broker. Your trade submission would take place within the run method of a custom action.

The tvwb.py shell

You can use the tvwb.py shell command to open a python shell with the app context. This allows you to interact with the app without having to enter python3 tvwb.py every time.

Running Docker on Windows/Mac?

Thanks to @khamarr3524 for pointing out there are some docker differences when running on Windows or Mac. I've added OS-specific docker-compose.yml files to accomodate these differences. One should be able to run their respective OS's docker-compose.yml file without issue now!

How do I get more help?

At the moment, the wiki is under construction. However, you may still find some good info on there. For additional assistance you can DM me on Twitter or join the Discord. I will try my best to get back to you!

Command to create new event and register it, new action and register it and link them together?

action:create FutuOrderExecute --register

event:create FutuWebhook --register

event:register FutuWebhook

action:link FutuOrderExecute FutuWebhook

SQLAlchemy ORM Migration

reference: https://dev.to/chrisjryan/database-migration-with-python-3gmg

Order Execution Logic

Open position:

get the price from source, create limit order, create order history, update strategy active order

Close position:

first, check current position from order id, get execution details, update order history for the current position.

if the order is not filled:

  1. cancel the order, create order history

if the order is filled:

  1. create selling order, get selling order execution detail, create order history

if the order is partially filled:

  1. cancel the order, create order history
  2. create selling order, get selling order execution detail, create order history

Deployment

I. Insert strategy from jupyter to dev DB

  1. Copy the strategy from table, header as well
  2. remove the parameter column
  3. add a column of is_lev, determine if to use leverage or not
  4. reformat the header as below
backtest_period,wfe,sr,l_sr,b_sr,win_rate,trd_num,sim_ret,lev_ret,bnh_ret,sim_mdd,lev_mdd,bnh_mdd,sim_add,lev_add,bnh_add,expos,leverage,position_size,strategy_id,strategy_name,direction,timeframe,symbol
  1. import with following command
\copy public.strategy (backtest_period,wfe,sr,l_sr,b_sr,win_rate,trd_num,sim_ret,lev_ret,bnh_ret,sim_mdd,lev_mdd,bnh_mdd,sim_add,lev_add,bnh_add,expos,leverage,position_size,strategy_id,strategy_name,direction,timeframe,symbol) FROM '/Users/thfwork/Desktop/Algo Trading/tradingview-webhooks-bot/strategy_backup/strategy_bck.csv' CSV HEADER;

II. Create Alert in tradingview

  1. modify strategy code accordingly for the strategy
  2. add alert, enable webhook with URL pointing http://192.53.123.86/webhook
  3. Message sample is as follow, Alert name is same as strategy_id
  4. for pair trading, symbol must follow "ETHUSDT/BTCUSDT" where ETHUSDT and BTCUSDT both are legit exchange symbol, program will split with "/"
{
    "key": "BybitWebhook:xxxxx",
    "data": {
            "strategy_id": "SMA_CROSSOVER_LONG_1D_BTC",
            "action": "{{strategy.order.action}}",
            "price": "{{strategy.order.price}}",
            "timestamp": "{{timenow}}",
            "symbol": "{{ticker}}",
            "exchange": "{{exchange}}",
            "source": "tradingview",
            "position_size": "{{strategy.position_size}}",
            "noofcontracts": "{{strategy.order.contracts}}",
            "orderid": "{{strategy.order.id}}",
            "comment": "{{strategy.order.comment}}",
            "alert_msg": "{{strategy.order.alert_message}}",
            "market_position": "{{strategy.market_position}}",
            "market_position_size": "{{strategy.market_position_size}}",
            "prev_market_position": "{{strategy.prev_market_position}}",
            "prev_market_position_size": "{{strategy.prev_market_position_size}}"
        }
}

{
    "key": "BybitWebhook:xxxxx",
    "data": {
            "strategy_id": "RSI_REVERSION_LONG_1H_SOL",
            "action": "buy",
            "price": "153.18",
            "timestamp": "2024-10-17T09:00:04Z",
            "symbol": "SOLUSDT.P",
            "exchange": "BYBIT",
            "source": "tradingview",
            "position_size": "21863.714",
            "noofcontracts": "21863.714",
            "orderid": "long",
            "comment": "long",
            "alert_msg": "",
            "market_position": "long",
            "market_position_size": "21863.714",
            "prev_market_position": "flat",
            "prev_market_position_size": "0"
        }
}

III. Strategy deployment

  1. Set Strategy as Active, Config Strategy to use personal_acc
update strategy set active = true where strategy_name in (
'MACD_CROSSOVER_LONG_1D'
);
update strategy set personal_acc = true where symbol in ('XTZ','STETH', 'XEC', 'ONE', 'TRU', 'QI', 'NEO', 'WBTC', 'HNT', 'VET', 'WEMIX', 'CRO', 'SC', 'ROSE', 'QNT', 'KCS');
  1. export the table from production to /strategy_backup/strategy_{yyyymmdd}.csv
  2. pull and make change in local for same files
  3. (run SQLAlchemy ORM Migration if there is schema change)
  4. push to prod
  5. run command
psql 'postgresql://postgres:XXXX@localhost/tradingview-webhooks-bot'
delete from strategy; \g
\copy strategy from '/root/tradingview-webhooks-bot/strategy_backup/strategy_bck.csv' delimiter ',' CSV HEADER;
  1. Allocate fund in strategy_management
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BYBIT'), strategy_id, false,  200, 200, 'BYBIT', true from strategy where symbol in ('CRO','YFI')  and (direction = 'short' or direction = 'SHORT') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BYBIT'));
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BITGET'), strategy_id, false,  0, 0, 'BITGET', false from strategy where symbol in ('CRO','YFI') and (direction = 'short' or direction = 'SHORT') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BITGET'));
#
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BYBIT'), strategy_id, false,  200, 200, 'BYBIT', true from strategy where symbol in ('WBTC','STETH','BTC', 'ETH', 'SOL') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BYBIT'));
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BITGET'), strategy_id, false,  0, 0, 'BITGET', false from strategy where symbol in ('WBTC','STETH','BTC', 'ETH', 'SOL') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BITGET'));
#
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BYBIT'), strategy_id, false,  0, 0, 'BYBIT', false from strategy where symbol in ('GNO','OSMO','RIF') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BYBIT'));
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BITGET'), strategy_id, false,  200, 200, 'BITGET', true from strategy where symbol in ('GNO','OSMO','RIF') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BITGET'));
#
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BYBIT'), strategy_id, false,  200, 200, 'BYBIT', true from strategy where symbol in ('QI', 'KCS', 'XMR') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BYBIT'));
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BITGET'), strategy_id, false,  0, 0, 'BITGET', false from strategy where symbol in ('QI', 'KCS', 'XMR') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BITGET'));
#
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BYBIT'), strategy_id, false,  100, 100, 'BYBIT', true from strategy where symbol in ('JASMY','TRU', 'KAVA', 'SEI', 'HNT', 'VET', 'WEMIX', 'RUNE', 'CAKE', 'ROSE', 'QNT') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BYBIT'));
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BITGET'), strategy_id, false,  100, 100, 'BITGET', true from strategy where symbol in ('JASMY','TRU', 'KAVA', 'SEI', 'HNT', 'VET', 'WEMIX', 'RUNE', 'CAKE', 'ROSE', 'QNT') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BITGET'));
#
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BYBIT'), strategy_id, false,  200, 200, 'BYBIT', true from strategy where symbol in ('SC') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BYBIT'));
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BITGET'), strategy_id, false,  0, 0, 'BITGET', false from strategy where symbol in ('SC') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BITGET'));
#
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BYBIT'), strategy_id, false,  0, 0, 'BYBIT', false from strategy where symbol in ('RAY') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BYBIT'));
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BITGET'), strategy_id, false, 200, 200, 'BITGET', true from strategy where symbol in ('RAY') and strategy_id not in (select strategy_id from strategy_management where exchange = 'BITGET'));

insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BYBIT'), strategy_id, false,  100, 100, 'BYBIT', true from strategy where active is true and strategy_id not in (select strategy_id from strategy_management where exchange = 'BYBIT'));
#insert into strategy_management(strat_mgmt_id, strategy_id, active_order, fund, init_fund, exchange, active) (select concat(strategy_id,'_BITGET'), strategy_id, false,  100, 100, 'BITGET', true from strategy where strategy_id not in (select strategy_id from strategy_management where exchange = 'BITGET'));

add strategy query

select strategy_id, position_size, sim_mdd, strategy_name, direction, symbol, active, leverage, timeframe, id from strategy order by strategy_id desc

Code deployment

  1. push change to prod
  2. kill the cmd and run startup.sh

DB schema deployment

  1. modify ./src/model/model.py
  2. generate a change file
alembic revision --autogenerate -m "commit message"
  1. (PLEASE CHECK THE CHANGE FILE BEFORE PROCEED)
  2. update database
alembic upgrade head
  1. push change to prod
  2. kill the cmd and run startup.sh

Fund deployment

Possible Max Drawdown of my account

select exchange, sum(sm.init_fund*(-sim_mdd/100)) from strategy s, strategy_management sm where s.strategy_id = sm.strategy_id and sim_mdd is not null group by exchange;
select s.personal_acc, sum(sm.init_fund*(-sim_mdd/100)) from strategy s, strategy_management sm where s.strategy_id = sm.strategy_id and exchange = 'BYBIT' and sim_mdd is not null group by personal_acc;

Required fund for my account

select exchange, sum(sm.fund*position_size*(expos/100)) from strategy s, strategy_management sm where s.strategy_id = sm.strategy_id and sim_mdd is not null group by exchange;
select s.personal_acc, sum(sm.init_fund*(-sim_mdd/100)) from strategy s, strategy_management sm where s.strategy_id = sm.strategy_id and exchange = 'BYBIT' and sim_mdd is not null group by personal_acc;

run test

thfwork@TangdeMBP features % pytest --html=report.html

check debug log

  1. error is output to a txt in server

  2. Useful DB query

select timestamp, o.exchange, strategy_id, action, price, symbol, error, error_stack, message_payload from alert_history a, order_execution_error o where a.id = o.alert_id and source = 'tradingview' order by a.id desc
delete from strategy_management where strategy_id in ('MACD_CROSSOVER_4H_APT',
'MACD_CROSSOVER_4H_WLD')
select distinct(strategy_id) from strategy_management where active_order = false and strategy_id in (
'SMA_CROSSOVER_SHORT_1D_FLOW') order by strategy_id desc

About

a framework πŸ— for trading with tradingview webhooks!

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 56.5%
  • HTML 36.3%
  • CSS 4.7%
  • JavaScript 1.3%
  • Gherkin 0.7%
  • Mako 0.3%
  • Other 0.2%