Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import math
from typing import List, Sequence
from .math_round_float import round_float ← from math.round-float ^1.0.0 · built alongside by fune
from .stats_moving_average_types import MovingAverageKind
def _window_mean(values: List[float], end: int, window: int) -> float:
# Summed afresh, left to right; math.fsum or sum() with a start value
# would round differently from the other languages.
total = 0.0
for i in range(end - window + 1, end + 1):
total = total + values[i]
return total / window
def moving_average(values: Sequence[float], window: int, kind: MovingAverageKind, decimals: int) -> List[float]:
"""Simple or exponential moving average, one value per full window.
The exponential average is seeded with the simple average of the first
window and carried unrounded; only the returned values are rounded, by
math.round-float on the exact value of each double (2.675 gives 2.67).
"""
if isinstance(values, (str, bytes)) or not isinstance(values, (list, tuple)):
raise TypeError("values must be a list of numbers")
for v in values:
if isinstance(v, bool) or not isinstance(v, (int, float)) or not math.isfinite(v):
raise TypeError("values must be finite numbers, received %r" % (v,))
if isinstance(window, bool) or not isinstance(window, int) or window < 1:
raise ValueError("window must be a whole number of 1 or greater, received %r" % (window,))
if kind not in ("simple", "exponential"):
raise ValueError('unknown moving average kind "%s"' % (kind,))
# Checked here as well as in round_float (same wording), so a series too
# short to produce any output still refuses bad decimals.
if isinstance(decimals, bool) or not isinstance(decimals, int) or decimals < 0 or decimals > 12:
raise ValueError("decimals must be a whole number from 0 to 12, received %r" % (decimals,))
series = [float(v) for v in values]
out: List[float] = []
if len(series) < window:
return out
if kind == "simple":
for end in range(window - 1, len(series)):
out.append(round_float(_window_mean(series, end, window), decimals))
return out
alpha = 2.0 / (window + 1)
ema = _window_mean(series, window - 1, window)
out.append(round_float(ema, decimals))
for i in range(window, len(series)):
ema = ema + alpha * (series[i] - ema)
out.append(round_float(ema, decimals))
return out