Re: Zigzag indicator
The previously posted zigzag study above was quite useful and my compliments to the authors. I needed something slightly different, however, more suitable for real-time identification and so here's a slightly tighter version that's about 80% smaller (from 100 lines of code down to about 18).
This zigzag indicator is more of an indicator than a study and therefore it identifies peaks one bar "late" which is the only way to behave for real time peak identification. Most importantly, it should (in theory) not set peaks/valleys retroactively. It does not offer the various lines of the other zigzag -- I had no need to know the bars since the last peak or valley but these can be added after the fact in a single line by counting a list comprehension or by using a ternary iteration.
''' Author: B. Bradford MIT License Copyright (c) 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. ''' class bbzigzag(bt.Indicator): plotinfo = dict(subplot=True, zigzag=dict(_name='zigzag', color='lightblue', ls='--', _skipnan=True), ) plotlines = dict( zigzag_peak=dict(marker='v', markersize=4.0, color='red', fillstyle='full', ls=''), zigzag_valley=dict(marker='^', markersize=4.0, color='red', fillstyle='full', ls=''), zigzag=dict(_name='zigzag', color='red', ls='--', _skipnan=True), ) params = ( ('up_retrace', 0.015), ('dn_retrace', 0.015), ('bardist', 0.015), # distance to last max/min (perc/100) ('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): self.setminperiod(2) self.pks =  def once(self, start, end): lstinputdata = self.datas.array[:] self.trnd = [0 if lstinputdata[i] == lstinputdata[i-1] else 1 if lstinputdata[i-1] > lstinputdata[i] else -1 for i in range(1, len(lstinputdata))] self.nogaps = [self.trnd[i-1] if self.trnd[i] == 0 else self.trnd[i] for i in range(len(self.trnd))] nogapslast=self.nogaps for i in range(len(self.nogaps)): if nogapslast != self.nogaps[i]: # peak found if abs(lstinputdata[i] - self.pks[-1]) > self.params.bardist * self.pks[-1]: self.pks.append(lstinputdata[i]) idx = -(len(self.nogaps)-i-1) absplotdist = lstinputdata[i] * self.params.plotdistance if self.nogaps[i] > nogapslast: self.lines.zigzag_peak[idx] = lstinputdata[i] + absplotdist if self.nogaps[i] < nogapslast: self.lines.zigzag_valley[idx] = lstinputdata[i] - absplotdist 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') nogapslast = self.nogaps[i]
As well you can specify a minimum threshold for the current bar minus the last bar to qualify as being recognized as a minimum, as well as a threshold for the minimum distance to the last peak/valley. Naturally, as in the case below, if you set these thresholds to non-zero values you can have peaks without valleys and valleys without peaks in some instances.
If you find a bug please let me know and any feedback is appreciated.
And here's a screenshot of this on a randomly (and poorly) selected SMA and the thresholds were just set arbitrarily just to show an example:
Thanks for sharing
@backtrader My pleasure Daniel - I'm probably being too cagey with the above so here's something that might (or might not) help you and what I am working towards. The below is my use of a zigzag indicator with very minimal filtering to identify wavelet inflection points at various increasing frequencies (decomposition level edge detection):
So far so good.