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

Custom Indicator RVI; float division by zero



  • So I am trying to build the relative volatility index indicator in backtrader. However, I get an error:

    dst[i] = op(srca[i], srcb[i])                                                                                       ZeroDivisionError: float division by zero   
    

    I understand that there are multiple points in my indicator logic where a division by zero could occur, such as rviHi declaration. However, I can't seem to find a way to fix this. Can anyone with knowledge of the relative volatility index check if my math/formula is right? Or is this a logic/programming error I committed?

    Code:

    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    import backtrader as bt
    from datetime import datetime
    from backtrader.indicators import EMA
    from backtrader.indicators import StdDev
    import backtrader.indicators as btind
    
    class RVI(bt.Indicator):
        lines = ('dummyline',)
    
        params = (('value', 5),)
    
        def __init__(self):
            stDevHi = StdDev(self.data.high, period=10)
            stDevLo = StdDev(self.data.low, period=10)
            avgStDevHiUp = bt.If(self.data.high  > self.data.high(-1),EMA(stDevHi, period = 14), 0)
    
            avgStDevHiDown= bt.If(self.data.high  < self.data.high(-1),EMA(stDevHi, period = 14), 0)
    
    
            avgStDevLoUp = bt.If(self.data.low  > self.data.low(-1),EMA(stDevLo, period = 14), 0)
    
     
            avgStDevLoDown = bt.If(self.data.low  < self.data.low(-1),EMA(stDevLo, period = 14), 0)
    
    
            rviHi = bt.If((avgStDevHiDown + avgStDevHiUp) == 0, 50, (100* avgStDevHiUp) / (avgStDevHiUp + avgStDevHiDown))
    
    
            rviLo = bt.If((avgStDevLoUp + avgStDevLoDown) == 0,50, (100* avgStDevLoUp) / (avgStDevLoUp + avgStDevLoDown))
    
    
            self.lines.dummyline = (rviHi + rviLo) / 2
    class firstStrategy(bt.Strategy):
    
        def __init__(self):
            self.rsi_small = RVI(self.data)
            self.sma = btind.EMA(self.data, period = 20)
    
        def next(self):
            if not self.position:
                if (self.sma < self.data.close and self.rsi_small > self.rsi_small[-1]):
                    self.buy(size=10)
                    #print('{},BUY,{}'.format(self.datetime.datetime(), self.data.close[0]))
                    #print(self.datetime.date())
                if (self.sma > self.data.close and self.rsi_small < self.rsi_small[-1]):
                    self.sell(size=10)
                    #print('{},SELL,{}'.format(self.datetime.datetime(), self.data.close[0]))
                    #print(self.datetime.date())
            else:
                if self.rsi_small < self.rsi_small[-1] and self.data.close < self.sma and self.position.size > 0:
                    self.close(size=10)
                if self.rsi_small > self.rsi_small[-1] and self.data.close > self.sma and self.position.size < 0:
                    self.close(size=10)
    
    
    #Variable for our starting cash
    startcash = 10000
    
    #Create an instance of cerebro
    cerebro = bt.Cerebro()
    
    #Add our strategy
    cerebro.addstrategy(firstStrategy)
    
    #Get Apple data from Yahoo Finance.
    #data = bt.feeds.Quandl(
    #    dataname='AAPL',
    #    fromdate = datetime(2016,1,1),
    #    todate = datetime(2017,1,1),
    #    buffered= True
    #    )
    data = bt.feeds.YahooFinanceData(
        dataname='AAPL',
        fromdate = datetime(2000,1,1),
        todate = datetime(2020,1,1),
        timeframe = bt.TimeFrame.Days,
        buffered= True
        )
    #Add the data to Cerebro
    cerebro.adddata(data)
    
    
    
    # Set our desired cash start
    cerebro.broker.setcash(startcash)
    
    # Run over everything
    cerebro.run()
    
    #Get final portfolio Value
    portvalue = cerebro.broker.getvalue()
    pnl = portvalue - startcash
    
    #Print out the final result
    print('Final Portfolio Value: ${}'.format(portvalue))
    print('P/L: ${}'.format(pnl))
    
    #Finally plot the end results
    cerebro.plot(style='candlestick')
    

    Thanks for any help



  • Have a look here. bt.DivByZero



  • Thank you, thats exactly what I needed. Didn't realize backtrader had this function.



  • Wherever you see this error, you can add Python exception handler. For example, in my case, I modified the code as follows in file */backtrader/linebuffer.py. Look for the def as shown below:

    def _once_op(self, start, end):
        ...
        for i in range(start, end):
            dst[i] = op(srca[i], srcb[i])
    

    Replace it as follows:

    def _once_op(self, start, end):
        ...
        for i in range(start, end):
            try:
                dst[i] = op(srca[i], srcb[i])
            except ZeroDevisionError:
                dst[i] = float(f'nan')
    

    You may replace all such occurrences if you get this error.



  • Referencing RSI in backtrader and simplfied the design a bit, see if it helps

    import backtrader as bt
    
    class RVI(bt.Indicator):
        lines = ('rvi','upDaySTD','downDaySTD')
        params = (('period', 14),
        ('movav', bt.ind.MovAv.Smoothed),
        ('upperband', 70.0),
        ('lowerband', 30.0),
        ('safehigh', 100.0),
        ('safelow', 50.0),
        )
    
        plotinfo = dict(subplot=True)
        plotlines = dict(
            upDaySTD=dict(_plotskip=True),
            downDaySTD=dict(_plotskip=True),
        )
    
        def _plotinit(self):
            self.plotinfo.plotyhlines = [self.p.upperband, self.p.lowerband]
    
        def __init__(self):
            super(RVI, self).__init__()
            self.lines.upDaySTD=bt.If(self.data-self.data(-1)<0,bt.ind.StdDev(self.data, period=self.p.period),0)
            self.lines.downDaySTD=bt.If(self.data-self.data(-1)>0,bt.ind.StdDev(self.data, period=self.p.period),0)
            maup = self.p.movav(self.lines.upDaySTD, period=self.p.period)
            madown = self.p.movav(self.lines.downDaySTD, period=self.p.period)
            highrs = self._rscalc(self.p.safehigh)
            lowrs = self._rscalc(self.p.safelow)
            rv = bt.ind.DivZeroByZero(maup, madown, highrs, lowrs)
            self.l.rvi = 100.0 - 100.0 / (1.0 + rv)
    
        def _rscalc(self, rsi):
            try:
                rs = (-100.0 / (rsi - 100.0)) - 1.0
            except ZeroDivisionError:
                return float('inf')
            return rs
    


  • @barton05 said in Custom Indicator RVI; float division by zero:

    Referencing RSI in backtrader and simplfied the design a bit, see if it helps

    import backtrader as bt
    
    class RVI(bt.Indicator):
        lines = ('rvi','upDaySTD','downDaySTD')
        params = (('period', 14),
        ('movav', bt.ind.MovAv.Smoothed),
        ('upperband', 70.0),
        ('lowerband', 30.0),
        ('safehigh', 100.0),
        ('safelow', 50.0),
        )
    
        plotinfo = dict(subplot=True)
        plotlines = dict(
            upDaySTD=dict(_plotskip=True),
            downDaySTD=dict(_plotskip=True),
        )
    
        def _plotinit(self):
            self.plotinfo.plotyhlines = [self.p.upperband, self.p.lowerband]
    
        def __init__(self):
            super(RVI, self).__init__()
            self.lines.upDaySTD=bt.If(self.data-self.data(-1)<0,bt.ind.StdDev(self.data, period=self.p.period),0)
            self.lines.downDaySTD=bt.If(self.data-self.data(-1)>0,bt.ind.StdDev(self.data, period=self.p.period),0)
            maup = self.p.movav(self.lines.upDaySTD, period=self.p.period)
            madown = self.p.movav(self.lines.downDaySTD, period=self.p.period)
            highrs = self._rscalc(self.p.safehigh)
            lowrs = self._rscalc(self.p.safelow)
            rv = bt.ind.DivZeroByZero(maup, madown, highrs, lowrs)
            self.l.rvi = 100.0 - 100.0 / (1.0 + rv)
    
        def _rscalc(self, rsi):
            try:
                rs = (-100.0 / (rsi - 100.0)) - 1.0
            except ZeroDivisionError:
                return float('inf')
            return rs
    

    Oh wow the one you made is much cleaner and better than the one I made. Thanks for the help!



  • @rpriyadarshi said in Custom Indicator RVI; float division by zero:

    Wherever you see this error, you can add Python exception handler. For example, in my case, I modified the code as follows in file */backtrader/linebuffer.py. Look for the def as shown below:

    def _once_op(self, start, end):
        ...
        for i in range(start, end):
            dst[i] = op(srca[i], srcb[i])
    

    Replace it as follows:

    def _once_op(self, start, end):
        ...
        for i in range(start, end):
            try:
                dst[i] = op(srca[i], srcb[i])
            except ZeroDevisionError:
                dst[i] = float(f'nan')
    

    You may replace all such occurrences if you get this error.

    Thank You very much. Try/Catch completely slipped my mind.


Log in to reply
 

});