This post provides a complete example script for live trading on Bitfinex (without leverage). It uses
That’s it! Happy Trading.
tv2bt
so that you can mix and match between using Tradingview and Backtrader! This means that it also follows on from our last post which introduced thetv2bt
bridge. So if you have not read that yet, you should start there to learn about tv2bt
and how to set it up. To whet your appetite and encourage you to click that linktv2bt
provides a way to use Tradingview’s alert system with Backtrader to trigger trades on live exchanges.
For those looking for leveraged trading, be patient, an example of that will come in the future!
Requirements
The code in this article requires that you havebt-ccxt-store
installed. You also must have a quite recent version for it to work correctly. If you have not updated since November the 20th 2019, then go and grab the latest source code from here:
https://github.com/Dave-Vallance/bt-ccxt-store
Scope
As mentioned in the introduction, we will be trading without leverage. To speak more simply, that means just buying and selling coins. It is important to note that when we do this, we are not really in a “position” as most people would see it. We are just swapping assets. With currency pairs, you are always simultaneously long and short. For example, if you use USD to buy some Euro’s then you are long EURO and Short USD at the same time. When you sell your Euro’s, the opposite is true. See the following article for a good explanation: https://www.investopedia.com/terms/c/counter-currency.asp Having said all that, in the example code below, Backtrader will still report being in a position when we buy the base currency. In other words, our position size will be greater than zero. When we sell the currency pair, it will go back to zero. However, for the reasons mentioned above, we are technically not in a position and you will not see an open position in Bitfinex. You will just see your wallet balances change.The Code
''' Author: www.backtest-rookies.com MIT License Copyright (c) 2019 backtest-rookies.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ''' import backtrader as bt from datetime import datetime from tv2bt import TVFeed from ccxtbt import CCXTStore apikey = 'INSERT YOUR KEY' secret = 'INSERT YOUR SECRET' class TVTest(bt.Strategy): ''' Simple strat to test TV Feed. ''' params = ( ('perc_size', 0.7), # 10% ('fixed_qty', 12) ) def __init__(self): self.data_info = dict() for i, d in enumerate(self.datas): # Tracking the last bar is useful when we have data coming in # at different times. self.data_info[d._name] = dict() self.data_info[d._name]['last bar'] = 0 # When we are not using leverage, there is no concept of a # position. So we create a dict to store holdings of both # the base and quote currencies. ticker_components = d._name.split('/') self.data_info[d._name]['base'] = ticker_components[0] self.data_info[d._name]['counter'] = ticker_components[1] for comp in ticker_components: self.data_info[d._name][comp] = dict() self.data_info[d._name][comp]['cash'] = 0 self.data_info[d._name][comp]['value'] = 0 def next(self): print('='*80) print(' '*36,'NEXT') print('='*80) for i, d in enumerate(self.datas): dn = d._name dt = d.datetime.datetime() bar = len(d) pos = self.getposition(d).size base = self.data_info[dn]['base'] counter = self.data_info[dn]['counter'] print('Position : {}'.format(pos)) # Check we have a new bar and are not repeating an old one. if bar > self.data_info[dn]['last bar']: print('DATE : {}'.format(dt)) print('ASSET : {}'.format(dn)) print('BAR : {}'.format(bar)) print('Open : {}'.format(d.open[0])) print('High : {}'.format(d.high[0])) print('Low : {}'.format(d.low[0])) print('Close : {}'.format(d.close[0])) print('Signal : {}'.format(d.signal[0])) print('-'*80) # Save the last bar processed self.data_info[dn]['last bar'] = bar # Get our balances only if we want to go buy/sell. # Otherwise, the request slows things down. if d.signal in [1,-1]: ticker_components = dn.split('/') for comp in ticker_components: # Get our cash and value to enter a position cash, value = self.broker.get_wallet_balance(comp) print('{} : {} in Cash'.format(comp, cash)) print('{} : {} in Value'.format(comp, value)) self.data_info[dn][comp]['cash'] = cash self.data_info[dn][comp]['value'] = value # Buy the base currency! if d.signal == 1: qty = (self.data_info[dn][counter]['value'] * self.p.perc_size) / d.close[0] print('Action : Buy | Qty: {}'.format(qty)) self.buy(d, size=qty) # Sell the base currency! if d.signal == -1: if cash > 0: print('Action : Sell | Qty: {}'.format(pos)) self.sell(d, size=pos) def notify_data(self, data, status, *args, **kwargs): print('DATA NOTIF: {}: {}'.format(data._getstatusname(status), ','.join(args))) def notify_order(self, order): dt = order.data.datetime.datetime() dn = order.data._name print('='*33, 'NOTIFY ORDER', '='*33) if order.status == order.Submitted: print('Date : {}'.format(dt)) print('Ticker : {}'.format(dn)) print('Notify : Order Submitted') if order.status == order.Accepted: print('Date : {}'.format(dt)) print('Ticker : {}'.format(dn)) print('Notify : Order Accepted') if order.status == order.Completed: print('Date : {}'.format(dt)) print('Ticker : {}'.format(dn)) print('Notify : Order Completed') if order.status == order.Canceled: print('Date : {}'.format(dt)) print('Ticker : {}'.format(dn)) print('Notify : Order Canceled') if order.status == order.Rejected: print('Date : {}'.format(dt)) print('Ticker : {}'.format(dn)) print('Notify : Order Rejected') # Example Alerts # -------------- # 1. DATA/OHLC # The ticker should be the same as you use for your broker to # make life easier. Don't blindly copy the Tradingview Ticker. # The rest of the string below should be copied in as it. Tradingview # will replace the values inside {{}} with the actual values. ''' {'symbol':'[INSERT TICKER]', 'DT':'{{time}}', 'O':{{open}}, 'H':{{high}}, 'L':{{low}}, 'C':{{close}}, 'V':{{volume}}, 'action':0} ''' # 2. SIGNALS # 3 Types of signal are currently supported. 'long', 'short' and 'flat' # It is expected that you handle them appropriately and according to your # taste in backtrader ''' {'symbol':'[INSERT TICKER]', 'action':1} ''' print('='*80) print('Starting Example Strategy') print('All data feeds must have one bar of data before before you will see any output \n' 'on the console. Please be patient...') print('For instructions how to use, see:') print('='*80) debug = False # Create an instance of cerebro cerebro = bt.Cerebro() # Get Data data = TVFeed(dataname='BTC/USD', debug=debug) # Add the data feeds cerebro.adddata(data) #cerebro.adddata(data2) # Set Config config = {'apiKey': apikey, 'secret': secret, 'enableRateLimit': True, 'rateLimit': 3000 } # Add our strategy cerebro.addstrategy(TVTest) print('Getting Store') # Create data feeds store = CCXTStore(exchange='bitfinex', currency='USD', config=config, retries=5, debug=False) print('Getting Broker') broker = store.getbroker() cerebro.setbroker(broker) # Run the strategy cerebro.run()
Testing
Before testing anything, you will need to insert your own Bitfinex API key and secret into the script. You can do that near the top of the script.apikey = 'INSERT YOUR KEY' secret = 'INSERT YOUR SECRET'After this, testing the code should be done in a controlled way. As such, it is recommended that you test the script using
fire_alerts.py
from the tv2bt
repository. It will be much quicker than setting up alerts on Tradingview and waiting for them to fire. Once you confirm that it is working, then setup Tradingivew to send alerts to your server.
https://github.com/Dave-Vallance/tv2bt
When buying coins, the script uses a percentage of your available counter currency (e.g USD) to calculate the qty
. We then use that quantity to buy the base currency (e.g BTC). This is important to point out for two reasons
- You must have some counter currency available to purchase the base currency. If you do not, you will get an error.
- You must provide a fake close price using
fire_alerts.py
that is close to the current price. This is so that the right QTY size can be calculated for the amount of cash in your account.
fire_alerts.py
has recently been updated to support sending OHLCV
data. So be sure to grab an updated version from the repository.
Once you fire a signal to purchase or sell, then you should follow it up with a second signal to receive the order notification that it was completed. So if the first alert has an action
of 1 (to buy), then follow up with an action
of 0. Alternatively, you can load up Bitfinex in your web browser and monitor the order history.
Successful Buy Order
If all goes well, you can expect to see some output like this:
Successful Sell Order
And like this for a sell order:
Find This Post Useful?
If this post saved you time and effort, please consider support the site! There are many ways to support us and some won’t even cost you a penny.
Brave
Backtest Rookies is a registered with Brave publisher!
Brave users can drop us a tip.
Alternatively, support us by switching to Brave using this referral link and we will receive some BAT!
Tradingview
Referral Link
Enjoying the content and thinking of subscribing to Tradingview? Support this site by clicking the referral link before you sign up!
PayPal
BTC
3HxNVyh5729ieTfPnTybrtK7J7QxJD9gjD
ETH
0x9a2f88198224d59e5749bacfc23d79507da3d431
LTC
M8iUBnb8KamHR18gpgi2sSG7jopddFJi8S