Laguerre RSI
-
Attempting to write this indicator and while I think I am close, I'm not understanding why the appearance of the graphs in matplotlib vs. the other App I am looking at for comparison are not the same. Perhaps I can be educated. :-)
I also could use some guidance as to how to decide whether to develop these indicators using the
__init__()
and LineBuffers or innext():
.Here is current BT code for Laguerre RSI:
#!/usr/bin/env python # -*- coding: utf-8; py-indent-offset:4 -*- from __future__ import (absolute_import, division, print_function, unicode_literals) import backtrader as bt class LaguerreRSI(bt.Indicator): alias = ('LRSI',) lines = ('lrsi',) params = (('gamma', 0.5),) plotinfo = dict( plotymargin=0.15, plotyticks=[0.0, 0.2, 0.5, 0.8, 1.0] ) def __init__(self): self.addminperiod(6) self.l0 = [0] self.l1 = [0] self.l2 = [0] self.l3 = [0] self.tp = (self.data.high + self.data.low) / 2 super(LaguerreRSI, self).__init__() def next(self): self.l0[0] = ((1 - self.p.gamma) * self.tp[0] + self.p.gamma * self.l0[-1]) self.l1[0] = ((-self.p.gamma * self.l0[0]) + self.l0[-1] + (self.p.gamma * self.l1[-1])) self.l2[0] = ((-self.p.gamma * self.l1[0]) + self.l1[-1] + (self.p.gamma * self.l2[-1])) self.l3[0] = ((-self.p.gamma * self.l2[0]) + self.l2[-1] + (self.p.gamma * self.l3[-1])) cd = 0 cu = 0 if self.l0[0] >= self.l1[0]: cu = self.l0[0] - self.l1[0] else: cd = self.l1[0] - self.l0[0] if self.l1[0] >= self.l2[0]: cu = cu + self.l1[0] - self.l2[0] else: cd = cd + self.l2[0] - self.l1[0] if self.l2[0] >= self.l3[0]: cu = cu + self.l2[0] - self.l3[0] else: cd = cd + self.l3[0] - self.l2[0] self.lines.lrsi[0] = cu / (cu + cd)
Here is some C# code for Laguerre that I am working from:
using System; using cAlgo.API; using cAlgo.API.Indicators; namespace cAlgo.Indicators { [Indicator(IsOverlay = false, AccessRights = AccessRights.None)] public class NewIndicator : Indicator { [Parameter(DefaultValue = 0.2)] public double gamma { get; set; } [Output("Laguerre RSI",Color = Colors.Yellow)] public IndicatorDataSeries laguerrersi { get; set; } [Output("Overbought",Color = Colors.Turquoise)] public IndicatorDataSeries overbought { get; set; } [Output("oversold",Color = Colors.Red)] public IndicatorDataSeries oversold { get; set; } private IndicatorDataSeries price; private IndicatorDataSeries L0; private IndicatorDataSeries L1; private IndicatorDataSeries L2; private IndicatorDataSeries L3; double cu; double cd; protected override void Initialize() { price = CreateDataSeries(); L0 = CreateDataSeries(); L1 = CreateDataSeries(); L2 = CreateDataSeries(); L3 = CreateDataSeries(); } public override void Calculate(int index) { overbought[index] = 0.8; oversold[index] = 0.2; price[index] = (MarketSeries.High[index]+MarketSeries.Low[index])/2; if(index<=6) { L0[index] = (1-gamma)*price[index]; L1[index] = -gamma*L0[index] + L0[index-1]; L2[index] = -gamma*L1[index] + L1[index-1]; L3[index] = -gamma*L2[index] + L2[index-1]; } if(index>6) { L0[index] = (1-gamma)*price[index] + gamma*L0[index-1]; L1[index] = -gamma*L0[index] + L0[index-1] + gamma*L1[index-1]; L2[index] = -gamma*L1[index] + L1[index-1] + gamma*L2[index-1]; L3[index] = -gamma*L2[index] + L2[index-1] + gamma*L3[index-1]; } // laguerrersi[index] = L0[index]; cu=0; cd=0; if(L0[index]>=L1[index]) { cu = L0[index]-L1[index]; } else { cd = L1[index] - L0[index]; } if(L1[index]>=L2[index]) { cu = cu+ L1[index]-L2[index]; } else { cd = cd + L2[index] - L1[index]; } if(L2[index]>=L3[index]) { cu = cu + L2[index] - L3[index]; } else { cd = cd + L3[index] - L2[index]; } if(cu+cd!=0) { laguerrersi[index] = cu / (cu+cd); } } } }
Here is the chart I am seeing:
Here is a look at the indicator in another system:
-
gamma
is0.2
in theC#
code and0.5
in python, unless you are changing things later inC#
(the backtrader chart shows thatgamma
had a value of0.5
during execution)To have equivalente code to
C#
you would also need to defineprenext
which is run before the minimum period (6
) is met. There theC#
calculations forif (index < 6)
would be performed.Some thumb counting would also indicate that the minimum period in python should probably be
7
. The reason being that6
in theC#
code is referring to a zero-based index, whereas the minimum period is referred to the actuallen
. -
@RandyT said in Laguerre RSI:
self.l0 = [0] self.l1 = [0] self.l2 = [0] self.l3 = [0]
There seems to be no
append
to this. References to[-1]
will be the same as references to[0]
No need for them to be lines objects, because only 2 positions are used. The quickest solution for it would be something like:
self.l0 = [0, 0] self.l1 = [0, 0] self.l2 = [0, 0] self.l3 = [0, 0]
-
The two example charts are both running with gamma = 0.5. That dark example chart is actually from Amibroker. Did not share that code as it is not very similar in logic. Can share that if useful.
I'll give your other example a try. I was there on one iteration but had some other issue.
The matplotlib chart actually looks like there is some smoothing happening. Not sure if there is a more responsive plotting model option that would make the difference?
The best approach here would probably be to see if I can get Amibroker to give me the values for the LRSI indicator.
-
Thanks for the guidance. While the code looks a bit hacky to me at this stage, the results look pretty legit. I'll submit a PR for this.
#!/usr/bin/env python # -*- coding: utf-8; py-indent-offset:4 -*- ############################################################################### # # Copyright (C) 2015, 2016, 2017 Daniel Rodriguez # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################### from __future__ import (absolute_import, division, print_function, unicode_literals) import backtrader as bt class LaguerreRSI(bt.Indicator): alias = ('LRSI',) lines = ('lrsi',) params = (('gamma', 0.5),) plotinfo = dict( plotymargin=0.15, plotyticks=[0.0, 0.2, 0.5, 0.8, 1.0] ) def __init__(self): self.addminperiod(6) self.l0 = [0, 0] self.l1 = [0, 0] self.l2 = [0, 0] self.l3 = [0, 0] super(LaguerreRSI, self).__init__() def next(self): tp = (self.data.high + self.data.low) / 2 self.l0.insert(0, ((1 - self.p.gamma) * tp + self.p.gamma * self.l0[0])) self.l1.insert(0, (-self.p.gamma * self.l0[0] + self.l0[1] + self.p.gamma * self.l1[0])) self.l2.insert(0, (-self.p.gamma * self.l1[0] + self.l1[1] + self.p.gamma * self.l2[0])) self.l3.insert(0, (-self.p.gamma * self.l2[0] + self.l2[1] + self.p.gamma * self.l3[0])) del self.l0[2:] del self.l1[2:] del self.l2[2:] del self.l3[2:] cd = 0 cu = 0 if self.l0[0] >= self.l1[0]: cu = self.l0[0] - self.l1[0] else: cd = self.l1[0] - self.l0[0] if self.l1[0] >= self.l2[0]: cu = cu + self.l1[0] - self.l2[0] else: cd = cd + self.l2[0] - self.l1[0] if self.l2[0] >= self.l3[0]: cu = cu + self.l2[0] - self.l3[0] else: cd = cd + self.l3[0] - self.l2[0] self.lines.lrsi[0] = cu / (cu + cd)
-
Thanks @RandyT for code!
I was thinking about of implementation of this RSI too. :) -
Cool, let me now if you come up with anything interesting. :-)
-
@RandyT Now I'll use your code :)
-
I hope so. :-) Let me know if you come up with any interesting systems using it.
-
The final implementation, already pushed to the
development
branch, respects the original implementation by Ehlers and rather than expecting the price component to havehigh
andlow
it takes simplyself.data
(which unless changed is theclose
from regular data feeds)Using it for the midpoint of the bar is easy:
midpoint = (self.data.high + self.data.low) / 2.0 lrsi = bt.ind.LRSI(midpoint)
The advantage of not having specifics like
high
andlow
in the code is that theLRSI
can be calculated for anything which is a data feed or descendant of ... you could create theLRSI
of aSimpleMovingAverage