Source code for backtrader.indicators.contrib.price_channel_stop_indicator

#!/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.
"""

import math

from .. import Indicator

__all__ = [
    "PriceChannelStopIndicator",
]


[docs] class PriceChannelStopIndicator(Indicator): """Reconstructs PriceChannel_Stop from its MQ5 source. 6 output buffers mapped to lines: 0=DownTrendSignal, 1=DownTrendBuffer, 2=DownTrendLine 3=UpTrendSignal, 4=UpTrendBuffer, 5=UpTrendLine """ lines = ("down_signal", "down_buffer", "down_line", "up_signal", "up_buffer", "up_line") params = ( ("channel_period", 5), ("risk", 0.1), ) def __init__(self): """Initialize oscillator state and force initial warm-up period.""" self._cp = int(self.p.channel_period) self._risk = float(self.p.risk) self._trend = 0 self._prev_bsmax = 0.0 self._prev_bsmin = 0.0 self.addminperiod(self._cp + 2)
[docs] def next(self): """Update indicator lines from current and rolling high/low windows.""" cp = self._cp risk = self._risk # Highest high and lowest low over [bar, bar+ChannelPeriod) hi = max(float(self.data.high[-i]) for i in range(cp)) lo = min(float(self.data.low[-i]) for i in range(cp)) d_price = (hi - lo) * risk bsmax = hi - d_price bsmin = lo + d_price cur_close = float(self.data.close[0]) if cur_close > self._prev_bsmax: self._trend = 1 if cur_close < self._prev_bsmin: self._trend = -1 # Ratchet if self._trend > 0 and bsmin < self._prev_bsmin: bsmin = self._prev_bsmin if self._trend < 0 and bsmax > self._prev_bsmax: bsmax = self._prev_bsmax # Reset all self.lines.down_signal[0] = 0.0 self.lines.down_buffer[0] = 0.0 self.lines.down_line[0] = 0.0 self.lines.up_signal[0] = 0.0 self.lines.up_buffer[0] = 0.0 self.lines.up_line[0] = 0.0 prev_down_buffer = ( float(self.lines.down_buffer[-1]) if len(self) > 1 and not math.isnan(float(self.lines.down_buffer[-1])) else 0.0 ) prev_up_buffer = ( float(self.lines.up_buffer[-1]) if len(self) > 1 and not math.isnan(float(self.lines.up_buffer[-1])) else 0.0 ) if self._trend > 0: price = bsmin if prev_down_buffer > 0: self.lines.up_signal[0] = price self.lines.up_line[0] = price else: self.lines.up_buffer[0] = price self.lines.up_line[0] = price if self._trend < 0: price = bsmax if prev_up_buffer > 0: self.lines.down_signal[0] = price self.lines.down_line[0] = price else: self.lines.down_buffer[0] = price self.lines.down_line[0] = price self._prev_bsmax = bsmax self._prev_bsmin = bsmin