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/

    Hyper Fast Lines Based Zig Zag Indicator 3

    Indicators/Strategies/Analyzers
    2
    7
    586
    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.
    • B
      bigdavediode last edited by

      Hi,

      Peak valley detection is an indicator that appears simple and isn't. This is my third zig zag indicator optimized to be lines-based in an init rather than step-by-step in a next() or once. It is blindingly fast.

      I'd be interested in hearing if anyone can detect any bugs. Please note that the zigzag line will be the unaltered turning points, but zigzag_peak and zigzag_valley are altered by the param plotdistance to move them away from the underlying data for visualization purposes.

      I've gotten it down to seven lines, not quite half my previous attempt. The extra two lines are for display purposes.

      If anyone can beat this for speed or efficiency I'd be very open to seeing what you can do.

      '''
      Author: B. Bradford
      
      MIT License
      
      Copyright (c) 2020 B. Bradford
      
      Permission is hereby granted, free of charge, to any person obtaining a copy
      of this software and associated documentation files (the "Software"), to deal
      in the Software without restriction, including without limitation the rights
      to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      copies of the Software, and to permit persons to whom the Software is
      furnished to do so, subject to the following conditions:
      
      The above copyright notice and this permission notice shall be included in all
      copies or substantial portions of the Software.
      
      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      SOFTWARE.
      '''
      
      
      import backtrader as bt
      import copy
      
      
      class bbzigzag(bt.Indicator):
          plotinfo = dict(subplot=True, zigzag=dict(_name='zigzag', color='darkblue', ls='--', _skipnan=True), )
      
          plotlines = dict(
              zigzag_peak=dict(marker='v', markersize=7.0, color='red', fillstyle='full', ls=''),
              zigzag_valley=dict(marker='^', markersize=7.0, color='red', fillstyle='full', ls=''),
              #zigzag=dict(_name='zigzag', color='red', ls='--', _skipnan=True),
          )
      
          params = (
              ('plotdistance', 0.03),  # distance to plot arrows (alters high/low indicator lines but not zigzag line)
          )
      
          lines = ('zigzag',
                   'zigzag_peak',
                   'zigzag_valley',)
      
          def __init__(self):
      
              tmp = copy.copy(self.data)
              tmp = bt.If(self.data(0) == self.data(-1), tmp(-1) + 0.000001, self.data(0))
              self.zigzag_peak = bt.If(bt.And(tmp(0)>tmp(-1), tmp(0)>tmp(1)), self.data(0), float('nan'))
              tmp = copy.copy(self.data)
              tmp = bt.If(self.data(0) == self.data(-1), tmp(-1) - 0.000001, self.data(0))
              self.zigzag_valley = bt.If(bt.And(tmp(0) < tmp(-1), tmp(0) < tmp(1)), self.data(0), float('nan'))
              self.lines.zigzag = bt.If(self.zigzag_peak, self.zigzag_peak, bt.If(self.zigzag_valley, self.zigzag_valley, float('nan')))
              self.lines.zigzag_peak = self.zigzag_peak * (1 + self.p.plotdistance)
              self.lines.zigzag_valley = self.zigzag_valley * (1 - self.p.plotdistance)
      
      
      
      

      This is also profoundly easy to change -- for example to allow it to ignore small retracements simply change the params section to:

      params = (
          ('up_retrace', 0.0),  # 0.02
          ('dn_retrace', 0.0),  # 0.02
          ('plotdistance', 0.03),  # distance to plot arrows (alters high/low indicator lines but not zigzag line)
      

      And change the tmpline from detecting equal bar entries to a range of bar entries to accommodate each retracement amount.

      1 Reply Last reply Reply Quote 1
      • B
        bigdavediode last edited by

        And a screenshot with it used on an SMA:

        bbzigzag Screenshot from 2020-06-19 17-40-17.png

        1 Reply Last reply Reply Quote 1
        • B
          bigdavediode last edited by

          I realize this chart isn't the best illustration as the SMA wiggles and results in some doubling up of peak/valley indications which makes it difficult to see if it's showing a high or low at points.

          1 Reply Last reply Reply Quote 0
          • B
            bigdavediode last edited by

            Just to mention if you're attempting to use this live it obviously looks ahead one bar into the future which isn't allowed. If you're using it for preloaded historical data in a batch it works for identifying the exact turning point rather than one bar late. But for live data you'll need to move it one bar back by subtracting one from every bar reference as below. I added a couple of del lines just to prompt a bit of memory cleanup but they are entirely optional.

            And yeah, I should use camelcase for the class name. Added a couple of comments to help with anyone who wants to understand the code.

               def __init__(self):
            
                    #Make a copy
                    tmp = copy.copy(self.data)
                    #Peak shift
                    tmp = bt.If(self.data(-1) == self.data(-2), tmp(-2) + 0.000001, self.data(-1))
                    #Find peaks
                    self.zigzag_peak = bt.If(bt.And(tmp(-1)>tmp(-2), tmp(-1)>tmp(0)), self.data(-1), float('nan'))
                    del tmp
                    tmp = copy.copy(self.data)
                    #valley shift
                    tmp = bt.If(self.data(-1) == self.data(-2), tmp(-2) - 0.000001, self.data(-1))
                    #Find valleys:
                    self.zigzag_valley = bt.If(bt.And(tmp(-1) < tmp(-2), tmp(-1) < tmp(0)), self.data(-1), float('nan'))
                    self.lines.zigzag = bt.If(self.zigzag_peak, self.zigzag_peak,
                                              bt.If(self.zigzag_valley, self.zigzag_valley, float('nan')))
                    self.lines.zigzag_peak = self.zigzag_peak * (1 + self.p.plotdistance)
                    self.lines.zigzag_valley = self.zigzag_valley * (1 - self.p.plotdistance)
                    del tmp
            
            
            1 Reply Last reply Reply Quote 0
            • B
              bigdavediode last edited by

              Fixed a real time bar delay bug and tightened the code:

                  def __init__(self):
              
                      cpy = copy.copy(self.data)
                      tmp = bt.If(self.data(0) == self.data(-1), cpy(-1) + 0.000001, self.data(0))
                      self.lines.zigzag_peak = bt.If(bt.And(tmp(-1)>tmp(-2), tmp(-1)>tmp(0)), self.data(-1), float('nan'))
                      cpy = copy.copy(self.data)
                      tmp = bt.If(self.data(0) == self.data(-1), cpy(-1) - 0.000001, self.data(0))
                      self.lines.zigzag_valley = bt.If(bt.And(tmp(-1) < tmp(-2), tmp(-1) < tmp(0)), self.data(-1), float('nan'))
                      self.lines.zigzag = bt.If(self.zigzag_peak, self.zigzag_peak, bt.If(self.zigzag_valley, self.zigzag_valley, float('nan')))
              
              
              1 Reply Last reply Reply Quote 2
              • Yacc Don
                Yacc Don last edited by

                            self.lines.zigzag[idx] = (self.zigzag_peak[idx] - absplotdist) if self.zigzag_peak[idx] == self.zigzag_peak[idx] else (self.zigzag_valley[idx] + absplotdist) if self.zigzag_valley[idx] == self.zigzag_valley[idx] else float('NaN')
                            self.lines.zigzag[idx] = (self.zigzag_peak[idx] - absplotdist) if True else (self.zigzag_valley[idx] + absplotdist) if True else float('NaN')
                

                Thank for sharing.

                About your former version of zigzag code, what's the difference with these two line of the code?

                Yacc Don 1 Reply Last reply Reply Quote 0
                • Yacc Don
                  Yacc Don @Yacc Don last edited by

                  @Yacc-Don @Yacc-Don Or you are testing if it is Nan. Is it equivalent to math.isnan()?

                  1 Reply Last reply Reply Quote 0
                  • 1 / 1
                  • First post
                    Last post
                  Copyright © 2016, 2017, 2018, 2019, 2020, 2021 NodeBB Forums | Contributors