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



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



  • And a screenshot with it used on an SMA:

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



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



  • 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
    
    

Log in to reply
 

});