Source code for backtrader.indicators.contrib.frama_series

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


[docs] class FramaSeries(Indicator): """Fractal Adaptive Moving Average (FrAMA) of a single input series.""" lines = ("frama",) params = (("period", 14),) def __init__(self): """Set the minimum period required for the FrAMA computation.""" self.addminperiod(max(int(self.p.period), 2))
[docs] def next(self): """Compute the adaptive-alpha FrAMA value for the current bar.""" period = max(int(self.p.period), 2) half = max(period // 2, 1) window = [float(self.data[-i]) for i in range(period - 1, -1, -1)] if len(window) < period: self.lines.frama[0] = float(self.data[0]) return first = window[:half] second = window[-half:] n1 = (max(first) - min(first)) / float(half) n2 = (max(second) - min(second)) / float(half) n3 = (max(window) - min(window)) / float(period) if n1 > 0.0 and n2 > 0.0 and n3 > 0.0: dim = (math.log(n1 + n2) - math.log(n3)) / math.log(2.0) else: dim = 1.0 alpha = math.exp(-4.6 * (dim - 1.0)) alpha = min(max(alpha, 0.01), 1.0) prev = float(self.lines.frama[-1]) if len(self) > 0 else float(self.data[0]) self.lines.frama[0] = alpha * float(self.data[0]) + (1.0 - alpha) * prev
[docs] class FramaLinesIndicator(Indicator): """FrAMA candle indicator emitting smoothed OHLC and a color line.""" lines = ("o", "h", "l", "c", "color") params = (("period", 14),) def __init__(self): """Build FrAMA series for each of the open/high/low/close inputs.""" self.addminperiod(int(self.p.period) + 2) self.frama_open = FramaSeries(self.data.open, period=int(self.p.period)) self.frama_high = FramaSeries(self.data.high, period=int(self.p.period)) self.frama_low = FramaSeries(self.data.low, period=int(self.p.period)) self.frama_close = FramaSeries(self.data.close, period=int(self.p.period))
[docs] def next(self): """Assemble the smoothed FrAMA candle and classify its color.""" o = float(self.frama_open[0]) h = float(self.frama_high[0]) low_price = float(self.frama_low[0]) c = float(self.frama_close[0]) mx = max(o, c) mn = min(o, c) h = max(mx, h) low_price = min(mn, low_price) color = 1 if o < c: color = 2 elif o > c: color = 0 self.lines.o[0] = o self.lines.h[0] = h self.lines.l[0] = low_price self.lines.c[0] = c self.lines.color[0] = color