Source code for backtrader.indicators.contrib.i_anch_mom_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.
"""

from .. import (
    ExponentialMovingAverage,
    Indicator,
    SimpleMovingAverage,
)

__all__ = [
    "IAnchMomIndicator",
]


def get_price_line(data, price_mode):
    """Select an applied-price line from a data feed by mode name.

    Args:
        data: The backtrader data feed providing OHLC lines.
        price_mode: Applied-price mode (e.g. ``'close'``, ``'median'``,
            ``'typical'``, ``'weighted'``); case-insensitive.

    Returns:
        The selected price line or a derived combination of OHLC lines.

    Raises:
        ValueError: If ``price_mode`` is not recognised.
    """
    mode = str(price_mode).lower()
    if mode in ("close", "price_close", "price_close_"):
        return data.close
    if mode in ("open", "price_open", "price_open_"):
        return data.open
    if mode in ("high", "price_high", "price_high_"):
        return data.high
    if mode in ("low", "price_low", "price_low_"):
        return data.low
    if mode in ("median", "price_median", "price_median_"):
        return (data.high + data.low) / 2.0
    if mode in ("typical", "price_typical", "price_typical_"):
        return (data.high + data.low + data.close) / 3.0
    if mode in ("weighted", "price_weighted", "price_weighted_"):
        return (data.high + data.low + data.close + data.close) / 4.0
    raise ValueError(f"Unsupported price mode: {price_mode}")


[docs] class IAnchMomIndicator(Indicator): """Anchored-momentum oscillator: percentage gap of a fast EMA over a slow SMA.""" lines = ("value",) params = ( ("sma_period", 34), ("ema_period", 20), ("price_type", "close"), ) def __init__(self): """Build the SMA and EMA of the applied price and set the warm-up period.""" price = get_price_line(self.data, self.p.price_type) self.sma = SimpleMovingAverage(price, period=int(self.p.sma_period)) self.ema = ExponentialMovingAverage(price, period=int(self.p.ema_period)) self.addminperiod(int(self.p.sma_period) + 2)
[docs] def next(self): """Emit the percentage gap ``100 * (ema / sma - 1)`` for the current bar.""" sma = float(self.sma[0]) ema = float(self.ema[0]) self.l.value[0] = 0.0 if sma == 0.0 else 100.0 * ((ema / sma) - 1.0)