Source code for backtrader.indicators.contrib.zig_zag_recent_pivot_signal

#!/usr/bin/env python
"""Functional-test indicators migrated to contrib.

Generated from a single functional strategy module to preserve file-local
helper functions and constants without cross-test name collisions.
"""

from .. import Indicator

__all__ = [
    "ZigZagRecentPivotSignal",
]


[docs] class ZigZagRecentPivotSignal(Indicator): """Custom indicator tracking local pivot age and pivot price breakout channels. Lines: signal (Line): Pivot direction signal line (1 for high, -1 for low). pivot_age (Line): Age of the confirmed pivot in bars. pivot_price (Line): Confirmed pivot price level. """ lines = ("signal", "pivot_age", "pivot_price") params = ( ("depth", 17), ("deviation", 7), ("backstep", 5), ("point", 0.01), ) def __init__(self): """Initialize indicator buffers and establish minimum warmup period.""" self.addminperiod(self.p.depth + self.p.backstep + 2) self._last_pivot_type = 0 self._last_pivot_price = None self._latest_signal = 0 self._latest_age = 999999 self._latest_pivot_price = 0.0
[docs] def next(self): """Calculate local ZigZag pivot high and low levels on each new bar.""" self.lines.signal[0] = 0 self.lines.pivot_age[0] = self._latest_age if self._latest_age < 999999 else 999999 self.lines.pivot_price[0] = self._latest_pivot_price if len(self.data) <= self.p.depth + self.p.backstep: return shift = self.p.backstep candidate_high = float(self.data.high[-shift]) candidate_low = float(self.data.low[-shift]) high_window = [float(self.data.high[-shift - i]) for i in range(self.p.depth)] low_window = [float(self.data.low[-shift - i]) for i in range(self.p.depth)] deviation_abs = self.p.deviation * self.p.point pivot_type = 0 pivot_price = None if candidate_high >= max(high_window): pivot_type = 1 pivot_price = candidate_high elif candidate_low <= min(low_window): pivot_type = -1 pivot_price = candidate_low if pivot_type != 0 and pivot_price is not None: is_new_pivot = False if ( self._last_pivot_price is None or pivot_type != self._last_pivot_type and abs(pivot_price - self._last_pivot_price) >= deviation_abs ): is_new_pivot = True elif pivot_type == self._last_pivot_type: if pivot_type == 1 and pivot_price > self._last_pivot_price: is_new_pivot = True if pivot_type == -1 and pivot_price < self._last_pivot_price: is_new_pivot = True if is_new_pivot: self._last_pivot_type = pivot_type self._last_pivot_price = pivot_price self._latest_signal = 1 if pivot_type == 1 else -1 self._latest_age = 0 self._latest_pivot_price = pivot_price if self._latest_age < 999999: self.lines.signal[0] = self._latest_signal self.lines.pivot_age[0] = self._latest_age self.lines.pivot_price[0] = self._latest_pivot_price self._latest_age += 1