Fundamental concept I am missing with indicator development: (Adaptive LRSI Filter)



  • I've spent a good part of the day trying to get this working and seem to continue to miss some fundamental concepts about indicator development. When something needs to be in __init__() vs next() or something.

    I have the following bit of code that does not quite accomplish the goal. Help would be very much appreciated.

    class AdaptiveLaguerreFilter(btind.PeriodN):
        '''
        // Adaptive Laguerre Filter, from John Ehlers
        // Link : http://www.mesasoftware.com/Papers/Time%20Warp%20Without%20Space%20Travel.exe
        // Another work from Ehlers : http://www.mesasoftware.com/technicalpapers.htm
        //
        // Description :
        // Laguerre Filtering, (alpha is automaticaly adapted depending the error of filtering)
        function ALFilter(price, length, medianlong) {
            result=price;
            L0 = price;
            L1 = price;
            L2 = price;
            L3 = price;
            coef=0.5;
            Diff=0;
            HH=0.1;
            LL=0;
            alpha=0.5;
    
            for(i = 1+length; i < BarCount; i++) {
                Diff[i] = abs(price[i] - result[i-1]);
                HH[i] = Diff[i];
                LL[i] = Diff[i];
    
                for(j = 0; j < (length-1); j++) {
                    if (Diff[i-j] > HH[i]) HH[i] = Diff[i-j];
                    if (Diff[i-j] < LL[i]) LL[i] = Diff[i-j];
                }
    
                if ( (i > length) AND (HH[i] - LL[i] != 0) ) {
                    coeftemp=(Diff - LL) / (HH - LL);
                    mlen = medianlong;
                    for(k = mlen - 1; k >= 0; k--) temparray[k] = coeftemp[i + k - (mlen - 1)];
                    temp=0;
                    for(k = mlen - 1; k > 0; k--) {
                        for (j = mlen - 1; j > 0; j--) {
                            if (temparray[j-1] > temparray[j]) {
                                temp = temparray[j-1];
                                temparray[j-1] = temparray[j];
                                temparray[j] = temp;
                            }
                        }
                    }
                    coef[i] = temparray[(mlen/2)-0.5];
                    //----- End median calculation
                } // end main IF
    
                alpha=coef[i];
                L0[i] = alpha*price[i] + (1 - alpha)*L0[i-1];
                L1[i] = -(1 - alpha)*L0[i] + L0[i-1] + (1 - alpha)*L1[i-1];
                L2[i] = -(1 - alpha)*L1[i] + L1[i-1] + (1 - alpha)*L2[i-1];
                L3[i] = -(1 - alpha)*L2[i] + L2[i-1] + (1 - alpha)*L3[i-1];
                result[i] = (L0[i] + 2*L1[i] + 2*L2[i] + L3[i]) / 6;
            }// end main  FOR
            return result;
        }
        '''
        alias = ('ALF', 'ALRSI', 'AdaptiveLRSI')
        lines = ('alf',)
        params = (('period', 20),
                  ('medianperiod', 5),)
        plotinfo = dict(subplot=False)
    
        def __init__(self):
            self.l0, self.l1, self.l2, self.l3 = 0.0, 0.0, 0.0, 0.0
            self.diff = abs(self.data - self.l.alf(-1))
            super(AdaptiveLaguerreFilter, self).__init__()
    
        def next(self):
            l0_1 = self.l0  # cache previous intermediate values
            l1_1 = self.l1
            l2_1 = self.l2
            alpha = 0.5
            llv, hhv = 0.0, 0.0
    
            llv = btind.Lowest(self.diff, period=self.p.period)
            hhv = btind.Highest(self.diff, period=self.p.period)
            alpha = btind.Mean((self.diff[0] - llv) / (hhv - llv),
                               period=self.p.medianperiod)
    
            self.l0 = l0 = (alpha * self.data[0]) + ((1.0 - alpha) * l0_1)
            self.l1 = l1 = (-(1 - alpha) * l0) + l0_1 + (1 - alpha) * l1_1
            self.l2 = l2 = (-(1 - alpha) * l1) + l1_1 + (1 - alpha) * l2_1
            self.l3 = l3 = (-(1 - alpha) * l2) + l2_1 + (1 - alpha) * self.l3
            self.l.alf[0] = (l0 + (2 * l1) + (2 * l2) + l3) / 6
    


  • Here is a hack to get this working, but I must be missing something. Looking at some of the other adaptive indicators, it seems it should be easier.

        alias = ('ALF', 'ALRSI', 'AdaptiveLRSI')
        lines = ('alf',)
        params = (('period', 20),
                  ('medianperiod', 5),)
        plotinfo = dict(subplot=False)
    
        def __init__(self):
            self.l0, self.l1, self.l2, self.l3 = 0.0, 0.0, 0.0, 0.0
            self.diff = abs(self.data - self.l.alf(-1))
            self.llv = btind.Lowest(self.diff, period=self.p.period)
            self.hhv = btind.Highest(self.diff, period=self.p.period)
            self.alpha = btind.Mean((self.diff - self.llv) / (self.hhv - self.llv),
                                    period=self.p.medianperiod)
            super(AdaptiveLaguerreFilter, self).__init__()
    
        def next(self):
            l0_1 = self.l0  # cache previous intermediate values
            l1_1 = self.l1
            l2_1 = self.l2
    
            alpha = self.alpha.av[0] if not math.isnan(self.alpha.av[0]) else 0.5
    
            self.l0 = l0 = (alpha * self.data) + ((1.0 - alpha) * l0_1)
            self.l1 = l1 = (-(1 - alpha) * l0) + l0_1 + (1 - alpha) * l1_1
            self.l2 = l2 = (-(1 - alpha) * l1) + l1_1 + (1 - alpha) * l2_1
            self.l3 = l3 = (-(1 - alpha) * l2) + l2_1 + (1 - alpha) * self.l3
            self.l.alf[0] = (l0 + (2 * l1) + (2 * l2) + l3) / 6
    
    

  • administrators

    • In __init__

      Operations like self.x = (self.data.high + self.data.low) / 2.0 generate a lazily evaluated lines object.

      Even this generates that: self.y = self.data.close > self.data.open

    • In next

      Here the [] operator is used to access the values provided by lines objects like the generated self.x or self.y or standard lines like self.data.high

      if self.data.close[0] > self.data.open[0]:
          do_something()
      

      Thanks to operator overloading the following is equivalent also in next

      if self.data.close > self.data.open:
          do_something()
      

      But unlike in __init__, this comparison generates a bool.

    One can generate a complete indicator just by using operations and logic in __init__. See for example an old friend of everybody like MACD (removing documentation and plotting preparation boilerplate)

    class MACD(bt.Indicator):
        lines = ('macd', 'signal',)
        params = (
            ('period_me1', 12),
            ('period_me2', 26), ('period_signal', 9),
            ('movav', MovAv.Exponential),
        )
    
        def __init__(self):
            super(MACD, self).__init__()
            me1 = self.p.movav(self.data, period=self.p.period_me1)
            me2 = self.p.movav(self.data, period=self.p.period_me2)
            self.lines.macd = me1 - me2
            self.lines.signal = self.p.movav(self.lines.macd, period=self.p.period_signal)
    

    No need to use scalar operations/logic in next. Everything is defined in terms of lines objects.

    Some indicators may need next. Some help from things defined in __init__ can be used. For example the ZeroLagIndicator

    class ZeroLagIndicator(MovingAverageBase):
        alias = ('ZLIndicator', 'ZLInd', 'EC', 'ErrorCorrecting',)
        lines = ('ec',)
        params = (
            ('gainlimit', 50),
            ('_movav', MovAv.EMA),
        )
    
        def __init__(self):
            self.ema = MovAv.EMA(period=self.p.period)
            self.limits = [-self.p.gainlimit, self.p.gainlimit + 1]
    
            # To make mixins work - super at the end for cooperative inheritance
            super(ZeroLagIndicator, self).__init__()
    
        def next(self):
            leasterror = MAXINT  # 1000000 in original code
            bestec = ema = self.ema[0]  # seed value 1st time for ec
            price = self.data[0]
            ec1 = self.lines.ec[-1]
            alpha, alpha1 = self.ema.alpha, self.ema.alpha1
    
            for value1 in range(*self.limits):
                gain = value1 / 10
                ec = alpha * (ema + gain * (price - ec1)) + alpha1 * ec1
                error = abs(price - ec)
                if error < leasterror:
                    leasterror = error
                    bestec = ec
    
            self.lines.ec[0] = bestec
    

    Here the base ema for the calculations is defined in __init__ but the core of the operations which is a loop over several values is best done in next.


Log in to reply
 

Looks like your connection to Backtrader Community was lost, please wait while we try to reconnect.