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

Question regarding Smoothed Moving Average



  • Hi,

    I tried to implement a relative volatility indicator as follows:

    class RelativeVolatility(bt.Indicator):
        lines = ('rel_vol',)
        params = (('period', 10),)
    
        def __init__(self):
            std_dev = bt.ind.StdDev(self.data.close, period=self.p.period)
            self.u = bt.If(self.data.close > self.data.close(-1), std_dev, 0)
            self.d = bt.If(self.data.close < self.data.close(-1), std_dev, 0)
            self.nU = btind.SMA(self.u, period=14)
            self.nD = btind.SMA(self.d, period=14)
            self.l.rel_vol = 100. * self.nU / (self.nU + self.nD)
    

    The code works perfectly. Actually however i want to replace the simple moving average by a smoothed moving average. But as soon as i change btind.SMA to btind.SmoothedMovingAverage i get lists full or 'nan' values.

    Would be great if anyone here knows why that is ?

    Thanks!



  • @alain alright, i was able to implement the smoothing moving average inside the next function. it's just a bit less elegant.


  • administrators

    First and foremost ...

    See the top of the forum

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

    This really helps

    @alain said in Question regarding Smoothed Moving Average:

    But as soon as i change btind.SMA to btind.SmoothedMovingAverage i get lists full or 'nan' values.

    You don't show how you change it, you don't show how you use the indicator. Believe me: a simple replacement of SMA for SMMA WORKS.

    In any case, you may want to share your changes to get further help and to let others understand how you solved your problem.



  • how i solved the problem (just used next method to calculate the SMMA explicitly in each step):

    class RelativeVolatility(bt.Indicator):
        lines = ('rel_vol', 'signal', 'nU', 'nD',)
        plotlines = dict(nU=dict(_plotskip=True), nD=dict(_plotskip=True))
        params = (('period', 10), ('signal_line_top', 80), ('signal_line_bottom', 20),)
    
        def _plotinit(self):
            self.plotinfo.plothylines = [self.p.signal_line_top, self.p.signal_line_bottom]
    
        def __init__(self):
            std_dev = bt.ind.StdDev(self.data.close, period=self.p.period)
            self.u = bt.If(self.data.close > self.data.close(-1), std_dev, 0)
            self.d = bt.If(self.data.close < self.data.close(-1), std_dev, 0)
    
        def nextstart(self):
            self.l.nU[0] = 0
            self.l.nD[0] = 0
    
        def next(self):
    
            self.l.nU[0] = (13. * self.l.nU[-1] + self.u[0]) / 14.
            self.l.nD[0] = (13. * self.l.nD[-1] + self.d[0]) / 14.
    
            self.l.rel_vol[0] = 100. * self.l.nU[0] / (self.l.nU[0] + self.l.nD[0])
    

    I tried again to directly switch SMA to SMMA.. but it really doesn't work for me:

        def __init__(self):
            std_dev = bt.ind.StdDev(self.data.close, period=self.p.period)
            u = bt.If(self.data.close > self.data.close(-1), std_dev, 0)
            d = bt.If(self.data.close < self.data.close(-1), std_dev, 0)
            nU = btind.SMMA(u, period=14)
            nD = btind.SMMA(d, period=14)
            self.l.rel_vol = 100. * nU / (nU + nD)
    

    gives me lists full of nan's in nU and nD...


  • administrators

    Well ... after looking at this ... the actual problem is when putting stddev inside bt.If as one of the potential results. When such a behavior was introduced ... ooops, it will have to be traced. But you can still have your indicator developed in an "elegant" manner by creating a null delay version of the result (stddev) to be the one of the results of bt.If.

    There is trivial fix in the base code, but I will still try to understand the why and it can be generically fixed.

    The code (pass SMMA or EMA as parameter if you wish, which were the ones hitting the issue)

    class RelativeVolatility(bt.Indicator):
        alias = ('RV',)
        lines = ('rv', 'nU', 'nD',)
        params = dict(
            p1=10,
            p2=14,
            movav=bt.ind.EMA,
        )
    
        def __init__(self):
            stddev = bt.ind.StdDev(self.data, period=self.p.p1)
            u = bt.If(self.data > self.data(-1), stddev(0), 0.0)
            d = bt.If(self.data < self.data(-1), stddev(0), 0.0)
    
            self.l.nU = nU = self.p.movav(u, period=self.p.p2)
            self.l.nD = nD = self.p.movav(d, period=self.p.p2)
    
            self.l.rv = 100.0 * nU / (nU + nD)
    

    A chart ...

    0_1556311394632_fbc77a5a-d28c-4ace-bc10-d6d0afe0fe00-image.png


  • administrators

    See: Community - Release 1.9.72.122

    The trivial fix would have helped, but it wasn't the right one. The real fix was somewhere else, being also mostly trivial.

    The SMMA and EMA indicators rely on the previous value to calculate the new one. As soon as one value is NaN, the result will always be the same and NaN. Non-recursive indicators, mostly every other, will recover after a number of calcualtions which is what made it possible for the error to remain undetected for a long while.

    The code will work now without having to create null delay object as in

    class RelativeVolatility(bt.Indicator):
        alias = ('RV',)
        lines = ('rv', 'nU', 'nD',)
        params = dict(
            p1=10,
            p2=14,
            movav=bt.ind.EMA,
        )
    
        def __init__(self):
            stddev = bt.ind.StdDev(self.data, period=self.p.p1)
            u = bt.If(self.data > self.data(-1), stddev, 0.0)
            d = bt.If(self.data < self.data(-1), stddev, 0.0)
    
            self.l.nU = nU = self.p.movav(u, period=self.p.p2)
            self.l.nD = nD = self.p.movav(d, period=self.p.p2)
    
            self.l.rv = 100.0 * nU / (nU + nD)