Source code for backtrader.indicators.contrib.digital_ft01_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 backtrader.utils.dateintern import num2date

from .. import Indicator

__all__ = [
    "DigitalFT01Indicator",
]


DIGITAL_WEIGHTS = [
    0.24470985659780,
    0.23139774006970,
    0.20613796947320,
    0.17166230340640,
    0.13146907903600,
    0.08950387549560,
    0.04960091651250,
    0.01502270569607,
    -0.01188033734430,
    -0.02989873856137,
    -0.03898967104900,
    -0.04014113626390,
    -0.03511968085800,
    -0.02611613850342,
    -0.01539056955666,
    -0.00495353651394,
    0.00368588764825,
    0.00963614049782,
    0.01265138888314,
    0.01307496106868,
    0.01169702291063,
    0.00974841844086,
    0.00898900012545,
    -0.00649745721156,
]


[docs] class DigitalFT01Indicator(Indicator): """Fixed-weight digital filter with a channel trigger for crossover signals.""" lines = ("digital", "trigger") params = ( ("halfchannel", 25), ("applied_price_code", 1), ("point", 0.01), ("signal_period_minutes", 180), ) def __init__(self): """Set the minimum period to cover the digital-filter kernel length.""" self.addminperiod(len(DIGITAL_WEIGHTS) + 10) def _price_value(self, shift): o = float(self.data.open[-shift] if shift else self.data.open[0]) h = float(self.data.high[-shift] if shift else self.data.high[0]) low_price = float(self.data.low[-shift] if shift else self.data.low[0]) c = float(self.data.close[-shift] if shift else self.data.close[0]) code = int(self.p.applied_price_code) if code == 1: return c if code == 2: return o if code == 3: return h if code == 4: return low_price if code == 5: return (h + low_price) / 2.0 if code == 6: return (h + low_price + c) / 3.0 if code == 7: return (h + low_price + c + c) / 4.0 if code == 8: return (o + h + low_price + c) / 4.0 if code == 12: base = h + low_price + c if c < o: return (base + low_price) / 4.0 if c > o: return (base + h) / 4.0 return (base + c) / 4.0 return c
[docs] def next(self): """Compute the digital-filter value and channel trigger for this bar. Convolves the fixed DIGITAL_WEIGHTS kernel with the applied price, then sets the trigger to a reference close plus/minus the half-channel depending on whether the filtered value is above or below that close. """ if len(self.data) < len(DIGITAL_WEIGHTS): return digital = 0.0 for shift, weight in enumerate(DIGITAL_WEIGHTS): digital += weight * self._price_value(shift) dt = num2date(self.data.datetime[0]) period_minutes = max(int(self.p.signal_period_minutes), 1) bars_from_day_start = int(round((dt.hour * 60 + dt.minute) / float(period_minutes)) + 1) if len(self.data) <= bars_from_day_start: return ref_close = float(self.data.close[-bars_from_day_start]) halfchannel = float(self.p.halfchannel) * float(self.p.point) trigger = ref_close + halfchannel if digital >= ref_close else ref_close - halfchannel self.lines.digital[0] = digital self.lines.trigger[0] = trigger