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 in next():.

    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:
    0_1487291725101_Selection_NNN(071).png

    Here is a look at the indicator in another system:
    0_1487291754514_Selection_NNN(072).png


  • administrators

    gamma is 0.2 in the C# code and 0.5 in python, unless you are changing things later in C# (the backtrader chart shows that gamma had a value of 0.5 during execution)

    To have equivalente code to C# you would also need to define prenext which is run before the minimum period (6) is met. There the C# calculations for if (index < 6) would be performed.

    Some thumb counting would also indicate that the minimum period in python should probably be 7. The reason being that 6 in the C# code is referring to a zero-based index, whereas the minimum period is referred to the actual len.


  • administrators

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

    0_1487304589844_Selection_NNN(076).png

    #!/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.


  • administrators

    The final implementation, already pushed to the development branch, respects the original implementation by Ehlers and rather than expecting the price component to have high and low it takes simply self.data (which unless changed is the close 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 and low in the code is that the LRSI can be calculated for anything which is a data feed or descendant of ... you could create the LRSI of a SimpleMovingAverage


Log in to reply
 

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