Navigation

    Backtrader Community

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

    Custom Indicator: Schaff Cycle

    General Code/Help
    2
    7
    206
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Vincenzo Timmel
      Vincenzo Timmel last edited by

      Hello! After a lot of trail and error it finally worked. However, the output seems wierd. The formula is based on this implementation: https://www.tradingview.com/script/dbxXeuw2-Indicator-Schaff-Trend-Cycle-STC/

      It seems that setting some values to 0 if the have never been initiated causes wierd results. Is there a way to tell Backtrader that if there a no value to put it to 0? I think I'm not totally clear how to define the starting-value of recusive objects.

      class schaff_trend_cycle(bt.Indicator):
          lines = ('schaff_cycle', 'f1', 'f2', 'pf',)
      
          params = (('length', 20),
                    ('slowLength', 23),
                    ('fastLength', 50),
                    ('factor', 0.5),
                    )
      
          def __init__(self):
              self.m = bt.ind.MACDHisto(self.data.close, period_me1=self.p.fastLength, period_me2=self.p.slowLength)
              self.v1 = bt.ind.Lowest(self.m, period=self.p.length)
              self.v2 = bt.ind.Highest(self.m, period=self.p.length) - self.v1
              self.l.f1 = bt.If(self.v2 > 0, (self.m - self.v1) / self.v2 * 100, self.lines.f1(-1))
              self.l.pf = self.l.pf(-1) + (self.p.factor * (self.l.f1 - self.l.pf(-1)))
              self.v3 = bt.ind.Lowest(self.l.pf, period=self.p.length)
              self.v4 = bt.ind.Highest(self.l.pf, period=self.p.length) - self.v3
              self.l.f2 = bt.If(self.v4 > 0, ((self.l.pf - self.v3) / self.v4) * 100, self.l.f2(-1))
              self.l.schaff_cycle = self.l.schaff_cycle(-1) + (
                      self.p.factor * (self.l.f2 - self.l.schaff_cycle(-1)))
      
          def nextstart(self):
              self.l.f2[0] = 0.0
              self.l.pf[0] = 0.0
              self.l.schaff_cycle[0] = 0.0
      

      And here is the output:

      8164aa75-9a0c-4f62-b662-5d8ff7970d09-image.png

      On first glance it looks amazing. However, you quickly realize that the Schaff-Cycle is inverted?

      Thanks so much for your help!

      1 Reply Last reply Reply Quote 0
      • A
        ab_trader last edited by

        Read this article about development of Recursive indicators - Blog - Recursive indicators. It seems you next() call need to be used, not __init()__

        1 Reply Last reply Reply Quote 1
        • Vincenzo Timmel
          Vincenzo Timmel last edited by

          But shouldn't it be possible to define it declarative and put it all in init and just give him one starting value? Also, with next and nextstart I have the problem that some values are dependent on values[-1], which are only used to calculate the indicator.

              def next(self):
                      self.m = bt.ind.MACDHisto(self.data.close, period_me1=self.p.fastLength, period_me2=self.p.slowLength)
                      self.v1 = bt.ind.Lowest(self.m, period=self.p.length)
                      self.v2 = bt.ind.Highest(self.m, period=self.p.length) - self.v1
                      self.v3 = bt.ind.Lowest(self.l.pf, period=self.p.length)
                      self.v4 = bt.ind.Highest(self.l.pf, period=self.p.length) - self.v3
                      self.l.f1 = bt.If(self.v2 > 0, ((self.m - self.v1) / self.v2) *100, self.l.f1[-1])
                      self.l.f2 = bt.If(self.v4 > 0, ((self.l.pf - self.v3) / self.v4) * 100, self.l.f2[-1])
                      self.l.pf = self.l.pf + (self.p.factor * (self.l.f1 - self.l.pf))
                      self.l.schaff_cycle = self.l.schaff_cycle[-1] + (
                              self.p.factor * (self.l.f2 - self.l.schaff_cycle[-1]))
              
          
          
          Traceback (most recent call last):
            File "/home/vincenzot/Documents/fx_xd/fx_xd/fx_xd_bot.py", line 204, in <module>
              opt_runs = cerebro.run(runonce=False)
            File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/cerebro.py", line 1127, in run
              runstrat = self.runstrategies(iterstrat)
            File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/cerebro.py", line 1298, in runstrategies
              self._runnext(runstrats)
            File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/cerebro.py", line 1630, in _runnext
              strat._next()
            File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/strategy.py", line 347, in _next
              super(Strategy, self)._next()
            File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineiterator.py", line 263, in _next
              indicator._next()
            File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineiterator.py", line 282, in _next
              self.nextstart()  # only called for the 1st value
            File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineiterator.py", line 347, in nextstart
              self.next()
            File "/home/vincenzot/Documents/fx_xd/fx_xd/custom_indicators.py", line 61, in next
              self.l.f2 = bt.If(self.v4 > 0, ((self.l.pf - self.v3) / self.v4) * 100, self.l.f2[-1])
            File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineroot.py", line 227, in __sub__
              return self._operation(other, operator.__sub__)
            File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineroot.py", line 88, in _operation
              return self._operation_stage2(other, operation, r=r)
            File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineroot.py", line 209, in _operation_stage2
              other = other[0]
            File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineseries.py", line 467, in __getitem__
              return self.lines[0][key]
            File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/linebuffer.py", line 163, in __getitem__
              return self.array[self.idx + ago]
          IndexError: array index out of range```
          
          If I put it all in next, it gives me this error. Do I also have to put them in __init__?
          A 1 Reply Last reply Reply Quote 0
          • Vincenzo Timmel
            Vincenzo Timmel last edited by

            By adding the 0.0 in nextstart I get the following error:

                def next(self):
                    self.m = bt.ind.MACDHisto(self.data.close, period_me1=self.p.fastLength, period_me2=self.p.slowLength)
                    self.v1 = bt.ind.Lowest(self.m, period=self.p.length)
                    self.v2 = bt.ind.Highest(self.m, period=self.p.length) - self.v1
                    self.v3 = bt.ind.Lowest(self.l.pf, period=self.p.length)
                    self.v4 = bt.ind.Highest(self.l.pf, period=self.p.length) - self.v3
                    self.l.f1 = bt.If(self.v2 > 0, ((self.m - self.v1) / self.v2) *100, self.l.f1[-1])
                    self.l.f2 = bt.If(self.v4 > 0, ((self.l.pf - self.v3) / self.v4) * 100, self.l.f2[-1])
                    self.l.pf = self.l.pf + (self.p.factor * (self.l.f1 - self.l.pf))
                    self.l.schaff_cycle = self.l.schaff_cycle[-1] + (
                            self.p.factor * (self.l.f2 - self.l.schaff_cycle[-1]))
            
                def nextstart(self):
                    self.l.f1[0] = 0.0
                    self.l.f2[0] = 0.0
                    self.l.schaff_cycle[0] = 0.0
            
            Traceback (most recent call last):
              File "/home/vincenzot/Documents/fx_xd/fx_xd/fx_xd_bot.py", line 204, in <module>
                opt_runs = cerebro.run(runonce=False)
              File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/cerebro.py", line 1127, in run
                runstrat = self.runstrategies(iterstrat)
              File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/cerebro.py", line 1298, in runstrategies
                self._runnext(runstrats)
              File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/cerebro.py", line 1630, in _runnext
                strat._next()
              File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/strategy.py", line 347, in _next
                super(Strategy, self)._next()
              File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineiterator.py", line 263, in _next
                indicator._next()
              File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineiterator.py", line 280, in _next
                self.next()
              File "/home/vincenzot/Documents/fx_xd/fx_xd/custom_indicators.py", line 61, in next
                self.l.f2 = bt.If(self.v4 > 0, ((self.l.pf - self.v3) / self.v4) * 100, self.l.f2[-1])
              File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineroot.py", line 227, in __sub__
                return self._operation(other, operator.__sub__)
              File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineroot.py", line 88, in _operation
                return self._operation_stage2(other, operation, r=r)
              File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineroot.py", line 209, in _operation_stage2
                other = other[0]
              File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineseries.py", line 467, in __getitem__
                return self.lines[0][key]
              File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/linebuffer.py", line 163, in __getitem__
                return self.array[self.idx + ago]
            IndexError: array index out of range
            
            Process finished with exit code 1
            
            
            1 Reply Last reply Reply Quote 0
            • A
              ab_trader @Vincenzo Timmel last edited by

              @Vincenzo-Timmel said in Custom Indicator: Schaff Cycle:

              But shouldn't it be possible to define it declarative and put it all in init and just give him one starting value?

              My experience is that recursive indicators are always developed using cycles, but you can try it different way.

              I think Lowest, Highest, MACDHisto etc should go to __init__(). Otherwise they are defined on every next() call. With all my respect, you may want to red one more time thru the docs, since what is written right now contradicts all bt concepts.

              Did you subclass your indicator from bt.indicators.PeriodN as example shows or from bt.indicator? Former has some means to set up min period of the indicator.

              1 Reply Last reply Reply Quote -1
              • Vincenzo Timmel
                Vincenzo Timmel last edited by

                Hm, I pretty much read trough all the example but I couldn't find one where not a line is defined recursive, but a value from which an indicator gets calculated. I'm unsure how to initialize those. This is also a version I had but I sadly always got the same error and I couldn't fix it:

                    def __init__(self):
                        self.m = bt.ind.MACDHisto(self.data.close, period_me1=self.p.fastLength, period_me2=self.p.slowLength)
                        self.v1 = bt.ind.Lowest(self.m, period=self.p.length)
                        self.v2 = bt.ind.Highest(self.m, period=self.p.length) - self.v1
                
                
                    def next(self):
                        self.f1[0] = (self.m[0] - self.v1[0]) / self.v2[0] * 100 if self.v2[0] > 0 else self.f1[-1]
                        self.pf[0] = self.pf[-1] + (self.p.factor * (self.f1[0] - self.pf[-1]))
                        self.v3 = bt.ind.Lowest(self.pf, period=self.p.length)
                        self.v4 = bt.ind.Highest(self.pf, period=self.p.length) - self.v3
                        self.f2[0] = ((self.pf[0] - self.v3[0]) / self.v4[0]) * 100 if self.v4[0] > 0 else self.f2[-1]
                        self.l.schaff_cycle[0] = self.l.schaff_cycle[-1] + (
                                self.p.factor * (self.f2[0] - self.l.schaff_cycle[-1]))
                
                Traceback (most recent call last):
                  File "/home/vincenzot/Documents/fx_xd/fx_xd/fx_xd_bot.py", line 209, in <module>
                    opt_runs = cerebro.run(runonce=False)
                  File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/cerebro.py", line 1127, in run
                    runstrat = self.runstrategies(iterstrat)
                  File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/cerebro.py", line 1298, in runstrategies
                    self._runnext(runstrats)
                  File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/cerebro.py", line 1630, in _runnext
                    strat._next()
                  File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/strategy.py", line 347, in _next
                    super(Strategy, self)._next()
                  File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineiterator.py", line 263, in _next
                    indicator._next()
                  File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineiterator.py", line 282, in _next
                    self.nextstart()  # only called for the 1st value
                  File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineiterator.py", line 347, in nextstart
                    self.next()
                  File "/home/vincenzot/Documents/fx_xd/fx_xd/custom_indicators.py", line 37, in next
                    self.f1[0] = (self.m[0] - self.v1[0]) / self.v2[0] * 100 if self.v2[0] > 0 else self.f1[-1]
                  File "/home/vincenzot/anaconda3/envs/fx_xd/lib/python3.7/site-packages/backtrader/lineseries.py", line 461, in __getattr__
                    return getattr(self.lines, name)
                AttributeError: 'Lines_LineSeries_LineIterator_DataAccessor_Indicat' object has no attribute 'f1'
                
                Process finished with exit code 1
                
                

                The only thing missing is how to initialize this, where to give f1 the first value. I can't just define it in nextstart with a float because it has to be an array.

                self.f1[0] = (self.m[0] - self.v1[0]) / self.v2[0] * 100 if self.v2[0] > 0 else self.f1[-1]
                

                Thanks so much for your help btw. I think I still don't 100% how next, nextstart, and init work together.

                1 Reply Last reply Reply Quote 0
                • A
                  ab_trader last edited by

                  I don't think that this is the only thing you are missing. To initialize the value is very easy:

                  def nextstart(self):
                      self.l.f1[0] = 0.0
                  

                  But here is couple other items to correct:

                  • you call undefined variables such as self.f1 or self.pf - this is the main reason of the error returned
                  • this in the next()
                          self.v3 = bt.ind.Lowest(self.pf, period=self.p.length)
                          self.v4 = bt.ind.Highest(self.pf, period=self.p.length) - self.v3
                  
                  

                  and then

                  .... self.v3[0] ....
                  .... self.v4[0] ....
                  

                  doesn't work.

                  1 Reply Last reply Reply Quote 0
                  • 1 / 1
                  • First post
                    Last post
                  Copyright © 2016, 2017, 2018 NodeBB Forums | Contributors
                  $(document).ready(function () { app.coldLoad(); }); }