Exponential Moving Average Helper
Track a smoothed moving average with a period-derived smoothing factor, plus a windowed simple average for comparison.
Code
from collections.abc import Iterable
from dataclasses import dataclass, field
@dataclass
class EmaTracker:
period: int
value: float | None = None
alpha: float = field(init=False, default=0.0)
def __post_init__(self) -> None:
if self.period < 1:
raise ValueError("period must be at least 1")
self.alpha = 2.0 / (self.period + 1)
def update(self, sample: float) -> float:
if self.value is None:
self.value = sample
else:
self.value = self.alpha * sample + (1 - self.alpha) * self.value
return self.value
def reset(self) -> None:
self.value = None
def exponential_moving_average(values: Iterable[float], period: int) -> list[float]:
tracker = EmaTracker(period)
return [tracker.update(value) for value in values]
def simple_moving_average(values: list[float], window: int) -> list[float | None]:
if window < 1:
raise ValueError("window must be at least 1")
smoothed: list[float | None] = []
for index in range(len(values)):
if index + 1 < window:
smoothed.append(None)
else:
chunk = values[index + 1 - window : index + 1]
smoothed.append(sum(chunk) / window)
return smoothed
def main() -> None:
samples = [10.0, 12.0, 11.0, 15.0, 14.0, 16.0]
print("ema:", [round(value, 3) for value in exponential_moving_average(samples, 3)])
print("sma:", simple_moving_average(samples, 3))
if __name__ == "__main__":
main()Report a problem with this exampleOpens a pre-filled issue on GitHub.