ZeroDivisionError not on RSI
-
Hi all,
I have codes below that works fine in backtesting but cannot execute in live trade using TWS. it returned the zero error as follows:
File "C:\Users\User\Anaconda3\lib\site-packages\backtrader\linebuffer.py", line 744, in next self[0] = self.operation(self.a[0], self.b[0]) ZeroDivisionError: float division by zero
However, i have set the
safediv=True
on the RSI in it still doesn't work. This strategy is the combination of RSI, ADX and Bollinger Band.appreciate if you can take a look at my code and let me know what I missed.
from __future__ import (absolute_import, division, print_function, unicode_literals) import backtrader as bt import datetime as dt import pytz datalist = ['XXXX', 'YYYY'] #Update data names csh=100000 #Change this accordingly!!! port_start_cash=100000 #Change this accordingly!!! SL=0.025 #stop loss pct TP=0.01 #take profit pct prop=1/len(datalist) #proportion of portfolio batch = 5 #rounding entrytime = dt.time(9,30) # start trade time exittime = dt.time(15,55) # ending trade time stakesize=10 lwbnd=50 #RSI Lower bound upbnd=55 #RSI Upper bound commis=1 #commission TPpct=TP*100 #TP in pctge SLpct=SL*100 #SL in pctge class MainSt(bt.Strategy): #Strategy set up data_live = False #start with data not live def notify_data(self, data, status, *args, **kwargs): #notify when data turn to live print('*' * 5, '%s DATA NOTIF: %s' % (data._name,data._getstatusname(status)), *args) if status == data.LIVE: self.data_live = True def log(self, txt, dt=None, vlm=None): #self log of data dt = dt or self.datas[0].datetime.datetime(0) vlm = vlm or self.data.volume[0] print('%s) %s, %s' % (len(self), dt.isoformat(), txt)) def __init__(self): #pre defined indicators self.order = {} self.buyprice = {} self.buycomm = {} self.bar_executed = {} self.inds = dict() self.o = dict() self.lendata = dict() for i, d in enumerate(self.datas): #iterate the data name self.order[d] = None self.buyprice[d] = None self.buycomm[d] = None self.bar_executed[d] = None self.inds[d] = dict() self.inds[d]['sma'] = bt.indicators.SimpleMovingAverage(d.close, period=30) #sma self.inds[d]['rsi'] = bt.indicators.RelativeStrengthIndex(d.close, period=9, safediv=True, upperband=upbnd,lowerband=lwbnd, plotname= 'rsi:'+d._name) #rsi self.inds[d]['ADX'] = bt.indicators.AverageDirectionalMovementIndex(d, period=9, plot=False) self.inds[d]['PDI'] = bt.indicators.PlusDirectionalIndicator(d, period=9, plot=False) self.inds[d]['MDI'] = bt.indicators.MinusDirectionalIndicator(d, period=9, plot=False) self.inds[d]['SD'] = bt.indicators.StandardDeviation(d, period=30, safepow=True) self.inds[d]['TopBB'] = self.inds[d]['sma']+2.5*self.inds[d]['SD'] self.inds[d]['BotBB'] = self.inds[d]['sma']-2.5*self.inds[d]['SD'] def notify_order(self, order): #notify when order happen if order.status in [order.Submitted, order.Accepted]: return if order.status in [order.Completed]: if order.isbuy(): #when buy order created in next self.log('%s: BUY EXECUTED, Price: %.2f, Cost: %.2f, Size: %.2f, Comm %.2f' % (order.data._name, order.executed.price, order.executed.value, order.executed.size, order.executed.comm)) self.buyprice = order.executed.price self.buycomm = order.executed.comm self.buy_executed_price = order.executed.price self.order = None self.bar_executed = len(self) else: #when sell order created in next # Sell self.log('%s: SELL EXECUTED, Price: %.2f, Cost: %.2f, Size: %.2f, Comm %.2f' % (order.data._name, order.executed.price, order.executed.value, order.executed.size, order.executed.comm)) self.sell_executed_price = order.executed.price self.last_size = order.executed.size self.order = None self.bar_executed = len(self) self.bar_executed = len(self) self.last_executed_price = order.executed.price elif order.status in [order.Canceled, order.Margin, order.Rejected]: return ''' self.log('%s Order Canceled/Margin/Rejected' % (order.data._name)) ''' # Write down: no pending order[order.data._name] self.order = None def notify_trade(self, trade): #notify trade info if not trade.isclosed: return self.log('%s: OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade.data._name, trade.pnl, trade.pnlcomm)) def next_open(self): #define the buy criterias here for i, d in enumerate(self.datas): pv = self.broker.getvalue() #get broker value cashd = self.broker.getcash() #get broker cash port_gaind = pv - port_start_cash #calculate total portfolio gain target = csh * prop # Ideal total value of the position price = d.close[0] shares_ideal = target / price # How many shares are needed to get target batches = int(shares_ideal / batch) # How many batches is this trade? shares = batches * batch if not self.data_live: # make sure live then only trade!!! return if not self.getposition(d).size: #make sure there is no position to start buy if (port_gaind <= -csh*SL): #check if portfolio value less then portfolio stop loss, don't buy return if (port_gaind >= csh*TP): #check if portfolio value less then portfolio stop loss, don't buy return if (d.datetime.time(0) >= exittime): # Don't buy when already end of day return if ((self.inds[d]['rsi'][0] < lwbnd)and(1<(self.inds[d]['MDI'][0]/self.inds[d]['PDI'][0])<1.3)and(30<self.inds[d]['ADX'][0]<40)and(d.datetime.time(0) >= entrytime)): #buy if RSI lower than lower bound and entry time is as defined print('%s pv: %.2f, cash:%.2f, rsi: %.2f, ADX: %.2f, PDI: %.2f, MDI: %.2f'% (d._name, pv, cashd, self.inds[d]['rsi'][0], self.inds[d]['ADX'][0], self.inds[d]['PDI'][0], self.inds[d]['MDI'][0])) self.log('%s: BUY CREATE, %.2f, VLM BOUGHT: %.2f' % (d._name, d.close[0], shares)) self.order = self.buy(data=d, size = shares, coo=True, coc=False) #buy data d shares using maximum allocated size def next(self): #define the sell criterias here for i, d in enumerate(self.datas): pv = self.broker.getvalue() #get broker value cashd = self.broker.getcash() #get broker cash port_gain = pv - port_start_cash #calculate total portfolio gain target = csh * prop # Ideal total value of the position price = d.close[0] shares_ideal = target / price # How many shares are needed to get target batches = int(shares_ideal / batch) # How many batches is this trade? shares = batches * batch ''' self.log('%s, Close: %.2f, pv: %.2f, cash: %.2f, rsi: %.2f, ADX: %.2f, PDI/MDI: %.2f, MDI/PDI: %.2f' % (d._name, d.close[0], pv, cashd, self.inds[d]['rsi'][0], self.inds[d]['ADX'][0], self.inds[d]['PDI'][0]/self.inds[d]['MDI'][0], self.inds[d]['MDI'][0]/self.inds[d]['PDI'][0])) ''' if not self.data_live: # make sure live then only trade!!! return if self.getposition(d).size: #make sure there is position to start sell if (d.close[0] >= (self.getposition(d).price*(1+.005))): #if curent close price >= the SU price, sell position print('%s pv: %.2f, cash:%.2f, rsi: %.2f, ADX: %.2f, PDI: %.2f, MDI: %.2f'% (d._name, pv, cashd, self.inds[d]['rsi'][0], self.inds[d]['ADX'][0], self.inds[d]['PDI'][0], self.inds[d]['MDI'][0])) self.log('%s: SELL CREATE (>%.2fpct), %.2f, VLM BOUGHT: %.2f' % (d._name, 0.5, d.close[0], self.getposition(d).size)) self.order = self.sell(data=d, size=abs(self.getposition(d).size), coo=False, coc=True) self.order = self.buy(data=d, size = shares, coo=True, coc=False) else: if ((d.close[0]<=self.inds[d]['BotBB'][0])): print('%s pv: %.2f, cash:%.2f, rsi: %.2f, ADX: %.2f, PDI: %.2f, MDI: %.2f'% (d._name, pv, cashd, self.inds[d]['rsi'][0], self.inds[d]['ADX'][0], self.inds[d]['PDI'][0], self.inds[d]['MDI'][0])) self.log('%s: SELL CREATE (Price<BotBB), %.2f, VLM BOUGHT: %.2f' % (d._name, d.close[0], self.getposition(d).size)) self.order = self.sell(data=d, size = abs(self.getposition(d).size), coo=False, coc=True) else: if ((d.close[0]>=self.inds[d]['TopBB'][0])): print('%s pv: %.2f, cash:%.2f, rsi: %.2f, ADX: %.2f, PDI: %.2f, MDI: %.2f'% (d._name, pv, cashd, self.inds[d]['rsi'][0], self.inds[d]['ADX'][0], self.inds[d]['PDI'][0], self.inds[d]['MDI'][0])) self.log('%s: SELL CREATE (Price>TopBB), %.2f, VLM BOUGHT: %.2f' % (d._name, d.close[0], self.getposition(d).size)) self.order = self.sell(data=d, size = abs(self.getposition(d).size), coo=False, coc=True) else: if (d.datetime.time(0) >= exittime): #exit trade when end of day arrive print('%s pv: %.2f, cash:%.2f, rsi: %.2f'% (d._name, pv, cashd, self.inds[d]['rsi'][0])) self.log('%s: EOD STOP, %.2f, VLM BOUGHT: %.2f' % (d._name, d.close[0], self.getposition(d).size)) self.order = self.sell(data=d, size=abs(self.getposition(d).size), coo=False, coc=True) else: if (port_gain >= csh*TP): #exit trade when portfolio gain >= take profit print('%s pv: %.2f, cash:%.2f, rsi: %.2f'% (d._name, pv, cashd, self.inds[d]['rsi'][0])) self.log('%s: TAKE PROFIT VAL CREATE (>%.2fpct), %.2f, VLM BOUGHT: %.2f' % (d._name, TPpct, d.close[0], self.getposition(d).size)) self.order = self.sell(data=d, size=abs(self.getposition(d).size), coo=False, coc=True) else: if (port_gain <= -csh*SL): #exit trade for stop loss print('%s pv: %.2f, cash:%.2f, rsi: %.2f'% (d._name, pv, cashd, self.inds[d]['rsi'][0])) self.log('%s: STOPLOSS VAL CREATE (<%.2fpct), %.2f, VLM BOUGHT: %.2f' % (d._name, SLpct, d.close[0], self.getposition(d).size)) self.order = self.sell(data=d, size=abs(self.getposition(d).size), coo=False, coc=True) def run(args=None): #run the engine cerebro = bt.Cerebro(cheat_on_open=True) #set COO to true to buy on same len store = bt.stores.IBStore(host='127.0.0.1', port=7496, reconnect=5) #live port is 7496 and paper port is 7497 cerebro.broker = store.getbroker() #connect to broker #must use this to send trade to TWS for i in datalist: #iterate the data in datalist data = store.getdata(dataname=i, timeframe=bt.TimeFrame.Ticks, tz = pytz.timezone('US/Eastern')) cerebro.resampledata(data, timeframe=bt.TimeFrame.Seconds, compression=15) #resample all data to live trade cerebro.addstrategy(MainSt) #add strategy here cerebro.run() #run everything # Plot the result # Print out the starting conditions if __name__ == '__main__': #execute run here run()
Thank you in advance!
-
Okay, I have narrowed down the problem to:
self.inds[d]['ADX'] = bt.indicators.AverageDirectionalMovementIndex(d, period=9, plot=False) self.inds[d]['PDI'] = bt.indicators.PlusDirectionalIndicator(d, period=9, plot=False) self.inds[d]['MDI'] = bt.indicators.MinusDirectionalIndicator(d, period=9, plot=False)
I believe due to the internal calculation of the indicators, zero division error is triggered.
I read as many postings as possible in this forum but I can't find a solution to this problem.
safediv
andsafezero
parameters only available to RSI and stochastic hence, I cant solve this ADX zero division error.Kindly appreciate your guidance to overcome this issue especially on live trading.
Thanks!
-
The problem is the low variability of the data, which triggers those exceptions. But no, the aforementioned indicators do not contain code to handle exceptions.
-
@backtrader Hi Admin,
Appreciate the prompt response. What do you mean by low variability of data?
Thanks!
-
@backtrader Hello! I'm very interested in including the code to handle these exceptions, particularly I think that adding a safediv parameter to the adx indicator might be very useful. Would it be possible to send you a pull request? How would that work?
-
Please create a regular PR in the main Backtrader GitHub repository: https://github.com/mementum/backtrader
It would be great if the test could be provided validating the fix. Ideally the test should fail before and pass after applying the fix :-).