Source code for backtrader.indicators.contrib.ef_distance_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__ = [
"EFDistanceIndicator",
]
def resolve_price_line(data, mode):
"""Build an applied-price line from a data feed per MT5 price modes.
Args:
data: The backtrader data feed providing OHLC lines.
mode: MT5 applied-price mode (e.g. ``'price_close'``, ``'price_typical'``).
Returns:
A backtrader line expression for the selected applied price, defaulting
to the close line.
"""
price_mode = str(mode).lower()
if price_mode in {"price_open", "open"}:
return data.open
if price_mode in {"price_high", "high"}:
return data.high
if price_mode in {"price_low", "low"}:
return data.low
if price_mode in {"price_median", "median"}:
return (data.high + data.low) / 2.0
if price_mode in {"price_typical", "typical"}:
return (data.high + data.low + data.close) / 3.0
if price_mode in {"price_weighted", "weighted"}:
return (2.0 * data.close + data.high + data.low) / 4.0
if price_mode in {"price_simpl", "simpl"}:
return (data.open + data.close) / 2.0
if price_mode in {"price_quarter", "quarter"}:
return (data.open + data.close + data.high + data.low) / 4.0
return data.close
[docs]
class EFDistanceIndicator(Indicator):
"""Energy-field distance indicator producing a weighted applied-price value.
Weights each price in a window by the accumulated powered distance to the
other prices in the window, yielding a smoothed ``value`` line whose turns
signal momentum shifts.
"""
lines = ("value",)
params = (
("length", 10),
("power", 2.0),
("ipc", "price_close"),
("price_shift", 0.0),
)
def __init__(self):
"""Set the minimum period required for the energy-field window."""
self.addminperiod(int(self.p.length) * 2 + 2)
[docs]
def next(self):
"""Compute the energy-field weighted price value for the current bar."""
length = int(self.p.length)
float(resolve_price_line(self.data, self.p.ipc)[0])
weights = []
prices = []
for i in range(length):
base_price = float(resolve_price_line(self.data, self.p.ipc)[-i])
energy = 0.0
for j in range(length):
ref_price = float(resolve_price_line(self.data, self.p.ipc)[-(i + j)])
energy += abs((base_price - ref_price) ** float(self.p.power))
weights.append(energy)
prices.append(base_price)
norm = sum(weights)
value = sum(w * p for w, p in zip(weights, prices)) / norm if norm else 0.0
self.lines.value[0] = value + float(self.p.price_shift)