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__()
vsnext()
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
-
-
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 generatedself.x
orself.y
or standard lines likeself.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 abool
.
One can generate a complete indicator just by using operations and logic in
__init__
. See for example an old friend of everybody likeMACD
(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 theZeroLagIndicator
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 innext
. -