Source code for backtrader.indicators.contrib.safe_adx

#!/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__ = [
    "SafeADX",
    "SafeAMA",
]


[docs] class SafeADX(Indicator): """ADX indicator implementation without division by zero pitfalls.""" lines = ("adx",) params = (("period", 14),) def __init__(self): """Set minimum lookback period.""" self.addminperiod(self.p.period + 3)
[docs] def next(self): """Compute safe directional movement and ADX-like intensity.""" pdm_vals = [] mdm_vals = [] tr_vals = [] for idx in range(self.p.period): high0 = float(self.data.high[-idx]) high1 = float(self.data.high[-idx - 1]) low0 = float(self.data.low[-idx]) low1 = float(self.data.low[-idx - 1]) close1 = float(self.data.close[-idx - 1]) up_move = high0 - high1 down_move = low1 - low0 pdm = up_move if up_move > down_move and up_move > 0 else 0.0 mdm = down_move if down_move > up_move and down_move > 0 else 0.0 tr = max(high0 - low0, abs(high0 - close1), abs(low0 - close1)) pdm_vals.append(pdm) mdm_vals.append(mdm) tr_vals.append(tr) tr_sum = sum(tr_vals) if tr_sum <= 1e-12: self.lines.adx[0] = 0.0 return pdi = 100.0 * sum(pdm_vals) / tr_sum mdi = 100.0 * sum(mdm_vals) / tr_sum denom = pdi + mdi if denom <= 1e-12: self.lines.adx[0] = 0.0 return self.lines.adx[0] = 100.0 * abs(pdi - mdi) / denom
[docs] class SafeAMA(Indicator): """Adaptive moving average indicator with safe initialization.""" lines = ("ama",) params = ( ("period", 9), ("fast_period", 2), ("slow_period", 30), ) def __init__(self): """Initialize adaptive moving average state and warm-up window.""" self._prev = None self.addminperiod(self.p.period + 3)
[docs] def next(self): """Update AMA based on efficiency ratio and smoothing.""" if len(self) == 0 or self._prev is None: self._prev = float(self.data.close[0]) self.lines.ama[0] = self._prev return direction = abs(float(self.data.close[0]) - float(self.data.close[-self.p.period])) volatility = 0.0 for idx in range(self.p.period): volatility += abs(float(self.data.close[-idx]) - float(self.data.close[-idx - 1])) efficiency = 0.0 if volatility <= 1e-12 else direction / volatility fast_sc = 2.0 / (self.p.fast_period + 1.0) slow_sc = 2.0 / (self.p.slow_period + 1.0) smoothing = (efficiency * (fast_sc - slow_sc) + slow_sc) ** 2 current = self._prev + smoothing * (float(self.data.close[0]) - self._prev) self.lines.ama[0] = current self._prev = current