Navigation

    Backtrader Community

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    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

    General Discussion
    4
    6
    508
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • J
      jabbarabdullah last edited by

      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!

      1 Reply Last reply Reply Quote 0
      • J
        jabbarabdullah last edited by

        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!

        1 Reply Last reply Reply Quote 0
        • B
          backtrader administrators last edited by

          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.

          J M 2 Replies Last reply Reply Quote 0
          • J
            jabbarabdullah @backtrader last edited by

            @backtrader Hi Admin,

            Appreciate the prompt response. What do you mean by low variability of data?

            Thanks!

            1 Reply Last reply Reply Quote 0
            • M
              Mcruzg @backtrader last edited by

              @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?

              1 Reply Last reply Reply Quote 0
              • vladisld
                vladisld last edited by

                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 :-).

                1 Reply Last reply Reply Quote 1
                • 1 / 1
                • First post
                  Last post
                Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors