Source code for backtrader.indicators.contrib.ema_rsi_va

#!/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 (
    RSI,
    Indicator,
)

__all__ = [
    "EmaRsiVa",
]


[docs] class EmaRsiVa(Indicator): """Volatility-adaptive EMA whose smoothing reacts to RSI distance from 50.""" lines = ("value",) params = ( ("rsi_period", 14), ("ema_periods", 14.0), ("applied_price", "close"), ) def __init__(self): """Resolve the applied price line, build RSI, and set warmup period.""" self.price_line = self._resolve_price_line() self.rsi = RSI(self.price_line, period=self.p.rsi_period) self.addminperiod(max(2, int(self.p.rsi_period) * 2)) def _resolve_price_line(self): ap = str(self.p.applied_price).lower() if ap in ("close", "price_close"): return self.data.close if ap in ("open", "price_open"): return self.data.open if ap in ("high", "price_high"): return self.data.high if ap in ("low", "price_low"): return self.data.low if ap in ("median", "price_median"): return (self.data.high + self.data.low) / 2.0 if ap in ("typical", "price_typical"): return (self.data.high + self.data.low + self.data.close) / 3.0 if ap in ("weighted", "price_weighted"): return (self.data.high + self.data.low + self.data.close * 2.0) / 4.0 return self.data.close
[docs] def next(self): """Update the adaptive EMA line using the RSI-modulated smoothing factor.""" price = float(self.price_line[0]) if len(self) == int(self.p.rsi_period) * 2: self.lines.value[0] = price return rsi_value = float(self.rsi[0]) if len(self.rsi) else float("nan") if not math.isfinite(rsi_value): prev = ( float(self.lines.value[-1]) if math.isfinite(float(self.lines.value[-1])) else price ) self.lines.value[0] = prev return rsvoltl = abs(rsi_value - 50.0) + 1.0 multi = (5.0 + 100.0 / float(self.p.rsi_period)) / ( 0.06 + 0.92 * rsvoltl + 0.02 * rsvoltl**2 ) pdsx = max(1.0, multi * float(self.p.ema_periods)) alpha = 2.0 / (pdsx + 1.0) prev_value = float(self.lines.value[-1]) if not math.isfinite(prev_value): prev_value = float(self.price_line[-1]) self.lines.value[0] = price * alpha + prev_value * (1.0 - alpha)