Source code for backtrader.indicators.smma
#!/usr/bin/env python
"""SMMA Indicator Module - Smoothed Moving Average.
This module provides the SMMA (Smoothed Moving Average) indicator used
by J. Welles Wilder in his 1978 book.
Classes:
SmoothedMovingAverage: SMMA indicator (aliases: SMMA, WilderMA,
MovingAverageSmoothed, MovingAverageWilder, ModifiedMovingAverage).
Example:
class MyStrategy(bt.Strategy):
def __init__(self):
self.smma = bt.indicators.SMMA(self.data.close, period=14)
def next(self):
if self.data.close[0] > self.smma[0]:
self.buy()
"""
from . import MovingAverageBase
[docs]
class SmoothedMovingAverage(MovingAverageBase):
"""
Smoothing Moving Average used by Wilder in his 1978 book `New Concepts in
Technical Trading`
Defined in his book originally as:
- new_value = (old_value * (period - 1) + new_data) / period
It Can be expressed as a SmoothingMovingAverage with the following factors:
- self.smfactor -> 1.0 / period
- self.smfactor1 -> `1.0 - self.smfactor`
Formula:
- movav = prev * (1.0 - smoothfactor) + newdata * smoothfactor
See also:
- http://en.wikipedia.org/wiki/Moving_average#Modified_moving_average
"""
alias = (
"SMMA",
"WilderMA",
"MovingAverageSmoothed",
"MovingAverageWilder",
"ModifiedMovingAverage",
)
lines = ("smma",)
def __init__(self):
"""Initialize the SMMA indicator.
Calculates alpha and alpha1 smoothing factors for the
smoothed moving average calculation.
"""
super().__init__()
self.alpha = 1.0 / self.p.period
self.alpha1 = 1.0 - self.alpha
[docs]
def nextstart(self):
"""Seed SMMA calculation with SMA on first valid bar.
Initializes with simple moving average of the first period values.
"""
# Seed value: SMA of first period values
period = self.p.period
data_sum = 0.0
for i in range(period):
data_sum += self.data[-i]
self.lines[0][0] = data_sum / period
[docs]
def next(self):
"""Calculate SMMA for the current bar.
Formula: SMMA = prev_SMMMA * alpha1 + current_price * alpha
where alpha = 1/period and alpha1 = 1 - alpha.
"""
# SMMA formula: prev * alpha1 + current * alpha
self.lines[0][0] = self.lines[0][-1] * self.alpha1 + self.data[0] * self.alpha
[docs]
def once(self, start, end):
"""Calculate SMMA in runonce mode"""
darray = self.data.array
larray = self.lines[0].array
alpha = self.alpha
alpha1 = self.alpha1
period = self.p.period
# Ensure output array is properly sized
while len(larray) < end:
larray.append(float("nan"))
limit = min(end, len(darray))
for i in range(limit):
larray[i] = float("nan")
# Seed at self._minperiod - 1 to match nextstart() behavior.
# nextstart() sums self.data[-i] for i in range(period) at the first
# valid bar, which corresponds to darray[seed_idx - period + 1 : seed_idx + 1].
seed_idx = self._minperiod - 1
if seed_idx >= limit or seed_idx < period - 1:
return
seed_start = seed_idx - period + 1
prev = sum(float(darray[j]) for j in range(seed_start, seed_idx + 1)) / period
larray[seed_idx] = prev
# SMMA is recursive - must calculate ALL values from period onwards
for i in range(seed_idx + 1, limit):
current_val = float(darray[i])
prev = prev * alpha1 + current_val * alpha
larray[i] = prev