SMA

Simple moving average in single-source-of-error state space form, with optional automatic order selection.

class smooth.SMA(order=None, ic='AICc', h=10, holdout=False, fast=True, verbose=0, **kwargs)

Simple Moving Average in Single Source of Error state space form.

SMA(m) is an AR(m) state-space model where every AR coefficient is fixed at 1/m. It is implemented as a thin wrapper over ADAM with model="NNN" and the AR vector hard-coded, so it inherits the full ADAM fit / predict / diagnostics surface (multi-step forecasts, prediction intervals, residual diagnostics). If order is left unspecified, the order is selected automatically by information criterion.

Parameters:
  • order (Optional[int]) – Order of the moving average. If None, selected automatically using the information criterion (ternary search when fast=True, sequential scan when fast=False).

  • ic (Literal['AIC', 'AICc', 'BIC', 'BICc']) – Information criterion used for automatic order selection.

  • h (int) – Forecast horizon (used with holdout=True to reserve a test set).

  • holdout (bool) – Whether to hold out the last h observations for validation.

  • fast (bool) – If True, use ternary search for order selection (fast, finds a local minimum). If False, evaluate all orders 1 … min(200, T) sequentially. When a pandas Series with a DatetimeIndex is passed, the inferred seasonal period is always evaluated as a candidate regardless of this flag.

  • verbose (int) – Verbosity level. 0 = silent.

  • **kwargs – Additional arguments passed to ADAM (e.g. n_iterations).

model

Model name, e.g. "SMA(3)".

Type:

str

ICs_

IC values for each evaluated order (only present after auto-selection). Keys are order integers, values are IC floats.

Type:

dict

See also

ADAM

Parent class; all ADAM attributes are available after fit().

MSARIMA

General multiple-seasonal ARIMA wrapper.

Examples

Fixed order:

>>> import numpy as np
>>> from smooth import SMA
>>> y = np.cumsum(np.random.randn(60)) + 100
>>> model = SMA(order=4, h=5)
>>> model.fit(y)
>>> fc = model.predict(h=5)
>>> fc.mean

Auto-selected order:

>>> model = SMA(h=5)
>>> model.fit(y)
>>> print(model.model)   # e.g. "SMA(3)"
>>> print(model.ICs_)

References

Overview

SMA(m) is the classical simple moving average expressed as an AR(m) state-space model with every autoregressive coefficient fixed at 1/m. Wrapping the moving average inside the ADAM framework — rather than treating it as a rolling window — gives several practical advantages:

  • Multi-step forecasts are produced recursively from the state vector, not by repeating the last observation.

  • Forecast variance and prediction intervals follow directly from the state-space recursion, so intervals widen with the horizon as they should.

  • Automatic order selection is available: when order is left unspecified, SMA evaluates a range of orders and picks the one that minimises an information criterion (default AICc).

  • Full ADAM API is inherited — .fit(), .predict(), residual diagnostics, and the ForecastResult return type all behave exactly as for ADAM.

Internally SMA calls ADAM with model="NNN", ar_order=m, arma={"ar": [1/m] * m}, initial="backcasting", loss="MSE", and distribution="dnorm". These are fixed and cannot be overridden — use ADAM or MSARIMA directly if you need arbitrary AR coefficients.

Example Usage

Fixed order:

import numpy as np
from smooth import SMA

y = np.cumsum(np.random.randn(60)) + 100

model = SMA(order=4, h=5)
model.fit(y)
print(model)              # e.g. "SMA(4)"

fc = model.predict(h=5, interval="prediction", level=0.95)
fc.mean
fc.lower, fc.upper

Automatically selected order:

model = SMA(h=5)          # order=None → auto-select
model.fit(y)
print(model.model)        # e.g. "SMA(3)"
print(model.ICs_)         # {1: ic_1, 2: ic_2, ...} for evaluated orders

With holdout for validation:

model = SMA(h=12, holdout=True)
model.fit(y)              # last 12 observations reserved as test set
fc = model.predict(h=12)

Order selection

When order is None, SMA searches over orders 1 min(200, T) using the criterion given by ic (one of "AIC", "AICc", "BIC", "BICc"). Two search modes are available:

  • fast=True (default) — ternary search, which converges to a local IC minimum without evaluating every order. Fast on long series. When the input is a pandas Series with a DatetimeIndex, the inferred seasonal period is also evaluated explicitly so seasonal moving averages are never missed.

  • fast=Falsesequential scan of every candidate order. Slower but guaranteed to find the global minimum within the search range.

The IC values from the search are stored on the fitted object as ICs_ (a dict keyed by order).

References

Svetunkov, I., & Petropoulos, F. (2017). Old dog, new tricks: a modelling view of simple moving averages. International Journal of Production Research. https://doi.org/10.1080/00207543.2017.1380326

See Also

  • ADAM — Underlying unified state-space framework

  • MSARIMA — General multiple-seasonal ARIMA wrapper

  • ES — Exponential smoothing