For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

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 and safezero 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!


  • administrators

    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!


Log in to reply
 

});