Source code for backtrader.indicators.contrib.force_index_ema
#!/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 (
ATR,
EMA,
Indicator,
)
__all__ = [
"ForceIndexEMA",
"ForceDiverSign",
]
[docs]
class ForceIndexEMA(Indicator):
"""Indicator producing a smoothed force index."""
lines = ("value",)
params = (("period", 3),)
def __init__(self):
"""Compute raw force index then smooth with an EMA."""
raw = (self.data.close - self.data.close(-1)) * self.data.volume
self.l.value = EMA(raw, period=max(int(self.p.period), 1))
[docs]
class ForceDiverSign(Indicator):
"""Divergence signal detector built from two force-index EMAs."""
lines = ("sell", "buy")
params = (
("i_period1", 3),
("i_period2", 7),
)
def __init__(self):
"""Initialize ATR filter and dual force-index streams."""
self.atr = ATR(self.data, period=10)
self.ind1 = ForceIndexEMA(self.data, period=int(self.p.i_period1)).value
self.ind2 = ForceIndexEMA(self.data, period=int(self.p.i_period2)).value
self.addminperiod(max(int(self.p.i_period1), int(self.p.i_period2)) * 2 + 10)
[docs]
def next(self):
"""Evaluate candle patterns and divergence confirmation each bar."""
self.l.sell[0] = float("nan")
self.l.buy[0] = float("nan")
if len(self.data) < 6:
return
sell_candle = (
float(self.data.open[-3]) < float(self.data.close[-3])
and float(self.data.open[-2]) > float(self.data.close[-2])
and float(self.data.open[-1]) < float(self.data.close[-1])
)
buy_candle = (
float(self.data.open[-3]) > float(self.data.close[-3])
and float(self.data.open[-2]) < float(self.data.close[-2])
and float(self.data.open[-1]) > float(self.data.close[-1])
)
ind1 = [
float(self.ind1[-4]),
float(self.ind1[-3]),
float(self.ind1[-2]),
float(self.ind1[-1]),
]
ind2 = [
float(self.ind2[-4]),
float(self.ind2[-3]),
float(self.ind2[-2]),
float(self.ind2[-1]),
]
atr = float(self.atr[0]) if not math.isnan(float(self.atr[0])) else 0.0
if sell_candle:
if ind1[0] < ind1[1] and ind1[1] > ind1[2] and ind1[2] < ind1[3]:
if ind2[0] < ind2[1] and ind2[1] > ind2[2] and ind2[2] < ind2[3]:
if (ind1[1] > ind1[3] and ind2[1] < ind2[3]) or (
ind1[1] < ind1[3] and ind2[1] > ind2[3]
):
self.l.sell[0] = float(self.data.high[0]) + atr * 3.0 / 8.0
if buy_candle:
if ind1[0] > ind1[1] and ind1[1] < ind1[2] and ind1[2] > ind1[3]:
if ind2[0] > ind2[1] and ind2[1] < ind2[2] and ind2[2] > ind2[3]:
if (ind1[1] > ind1[3] and ind2[1] < ind2[3]) or (
ind1[1] < ind1[3] and ind2[1] > ind2[3]
):
self.l.buy[0] = float(self.data.low[0]) - atr * 3.0 / 8.0