Source code for backtrader.indicators.contrib.nrtr_extr_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 Indicator
__all__ = [
"NRTRExtrIndicator",
]
[docs]
class NRTRExtrIndicator(Indicator):
"""Reconstructs NRTR_extr indicator from its MQ5 source.
Same as NRTR but uses high/low extremes instead of close for price tracking.
In uptrend: price = max(price, high[bar]); check close < value.
In downtrend: price = min(price, low[bar]); check close > value.
On flip up: price = low[bar], value = price*(1-dK).
On flip down: price = high[bar], value = price*(1+dK).
"""
lines = ("trend_up", "trend_down", "sign_up", "sign_down")
params = (
("iperiod", 10),
("idig", 0),
)
def __init__(self):
"""Initialize internal NRTR state and warm-up period."""
self._period = int(self.p.iperiod)
self._idig = int(self.p.idig)
self._trend = 0
self._trend_prev = 0
self._price = 0.0
self._value = 0.0
self._first = True
self.addminperiod(self._period + 2)
[docs]
def next(self):
"""Update trend state, channel values, and signal lines."""
period = self._period
if self._first:
self._trend_prev = 0
self._price = float(self.data.close[0])
self._value = self._price
self._first = False
self._trend = self._trend_prev
price = self._price
value = self._value
avg_range = 0.0
for i in range(period):
avg_range += abs(float(self.data.high[-i]) - float(self.data.low[-i]))
avg_range /= period
digits_diff = 5 - self._idig
dK = avg_range / pow(10, digits_diff) if pow(10, digits_diff) != 0 else avg_range
cur_close = float(self.data.close[0])
cur_high = float(self.data.high[0])
cur_low = float(self.data.low[0])
if self._trend >= 0:
price = max(price, cur_high) # uses high instead of close
value = max(value, price * (1.0 - dK))
if cur_close < value:
price = cur_high # uses high
value = price * (1.0 + dK)
self._trend = -1
elif self._trend <= 0:
price = min(price, cur_low) # uses low instead of close
value = min(value, price * (1.0 + dK))
if cur_close > value:
price = cur_low # uses low
value = price * (1.0 - dK)
self._trend = 1
tu = value if self._trend > 0 else 0.0
td = value if self._trend < 0 else 0.0
su = tu if self._trend_prev < 0 and self._trend > 0 else 0.0
sd = td if self._trend_prev > 0 and self._trend < 0 else 0.0
self._trend_prev = self._trend
self._price = price
self._value = value
self.lines.trend_up[0] = tu
self.lines.trend_down[0] = td
self.lines.sign_up[0] = su
self.lines.sign_down[0] = sd