Source code for backtrader.indicators.contrib.dots_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.
"""
import math
from .. import Indicator
__all__ = [
"DotsIndicator",
]
[docs]
class DotsIndicator(Indicator):
"""Compute a multi-cycle Dots value and color-change state.
Attributes:
lines: Custom lines ``dots`` and ``color``.
params: Indicator parameters used in strategy logic.
"""
lines = ("dots", "color")
params = (
("length", 10),
("filter_points", 0.0),
("price_code", 1),
("point", 0.01),
)
def __init__(self):
"""Initialize internal caches and indicator warmup state."""
self.addminperiod(int(self.p.length) * 4 + int(self.p.length) + 5)
self.res1 = 1.0 / max(float(self.p.length), 1.0)
self.phase = max(int(self.p.length) - 1, 0)
self.cycle = 4
self.filter_distance = float(self.p.filter_points) * float(self.p.point)
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.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
return c
[docs]
def next(self):
"""Update smoothed value and color state for the current bar."""
total_len = self.phase + int(self.p.length) * self.cycle
if len(self.data) <= total_len:
return
t = 0.0
total = 0.0
weight = 0.0
for iii in range(total_len):
if t <= 0.5:
g = 1.0
else:
g = 1.0 / (self.phase + 1.0)
beta = math.cos(math.pi * t)
alpha = g * beta
price = self._price_value(iii)
total += alpha * price
weight += alpha
if t < 1.0:
t += self.res1
ma = total / weight if weight else self.data.close[0]
prev_ma = float(self.lines.dots[-1]) if len(self.data) > 1 else ma
color = float(self.lines.color[-1]) if len(self.data) > 1 else 0.0
if ma - prev_ma > self.filter_distance:
color = 0.0
elif prev_ma - ma > self.filter_distance:
color = 1.0
self.lines.dots[0] = ma
self.lines.color[0] = color