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)
from datetime import datetime

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]):
#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()

#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
)

# 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)
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
``````

• 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)
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!

• 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.

});