Skip to content
Ivan Svetunkov edited this page Jul 6, 2026 · 7 revisions

SMA - Simple Moving Average

SMA (Simple Moving Average) implements simple moving average in state-space form with automatic order selection. This provides a statistically rigorous foundation for the classic moving average method.

SMA is available in both R (sma()) and Python (SMA). Both share the same algorithm and options.

Function signatures

R

sma(y, order = NULL,
    ic = c("AICc", "AIC", "BIC", "BICc"),
    h = 10, holdout = FALSE,
    silent = TRUE, fast = TRUE, ...)

Python

class SMA(ADAM):
    def __init__(
        self,
        order: int | None = None,
        ic: Literal["AIC", "AICc", "BIC", "BICc"] = "AICc",
        h: int = 10,
        holdout: bool = False,
        fast: bool = True,
        verbose: int = 0,
        **kwargs,
    ) -> None: ...

Overview

The function constructs an AR model in state-space form based on the simple moving average concept:

yₜ = (1/n) Σⱼ₌₁ⁿ yₜ₋ⱼ

This is equivalent to an AR(n) process, allowing proper parameter estimation and prediction interval construction.

Note that the forecast from the SMA in the state space is not a straight line! It is a multistep conditional expectation from the respective AR(n) model with provided parameters. See Svetunkov & Petropoulos (2018) for more details.

How it works (both R and Python). SMA(m) is implemented as a thin wrapper over ADAM with model="NNN", an AR(m) component, and every AR coefficient hard-fixed at 1/m. Because of this it inherits the full ADAM machinery — recursive multi-step forecasts, proper forecast variance, prediction intervals, and residual diagnostics. When the order is left unspecified it is selected automatically by an information criterion (a fast modified ternary search by default, or an exhaustive scan). In Python this is the SMA class (from smooth import SMA), which subclasses ADAM and therefore exposes the same fit() / predict() / diagnostics surface.

R Usage

library(smooth)

# SMA with specific order
sma(y, order=12, h=18, holdout=TRUE)

# Automatic order selection
sma(y, h=18, holdout=TRUE)

# With specific information criterion
sma(y, order=NULL, ic="BIC", h=18, holdout=TRUE)

# Fast search (modified ternary search)
sma(y, h=18, holdout=TRUE, fast=TRUE)

# Full search
sma(y, h=18, holdout=TRUE, fast=FALSE)

Forecasting

model <- sma(y, order=12, h=18, holdout=TRUE)

# Point forecasts
forecast(model, h=18)

# With prediction intervals
plot(forecast(model, h=18, interval="empirical"))
plot(forecast(model, h=18, interval="parametric"))

Python Usage

The Python examples below mirror the R ones one-to-one. The only structural difference is the scikit-learn-style split: options go to the SMA(...) constructor and the data is passed to .fit(y).

from smooth import SMA

# SMA with specific order
SMA(order=12, h=18, holdout=True).fit(y)

# Automatic order selection
SMA(h=18, holdout=True).fit(y)

# With specific information criterion
SMA(order=None, ic="BIC", h=18, holdout=True).fit(y)

# Fast search (modified ternary search)
SMA(h=18, holdout=True, fast=True).fit(y)

# Full search
SMA(h=18, holdout=True, fast=False).fit(y)

Forecasting

model = SMA(order=12, h=18, holdout=True)
model.fit(y)

# Point forecasts
fc = model.predict(h=18)
fc.mean

# With prediction intervals (R "parametric" -> Python "prediction")
model.predict(h=18, interval="prediction")
model.predict(h=18, interval="simulated")

Parameters

Parameter Type (R) Type (Python) Default (R / Python) Description
y vector/ts NDArray / pd.Series - Time series data. In Python it is passed to .fit(y), not the constructor
order integer/NULL int / None NULL / None SMA order (NULL/None for auto-selection)
ic character str "AICc" Information criterion for order selection
h integer int 10 Forecast horizon
holdout logical bool FALSE / False Use holdout validation
silent (R) / verbose (Python) logical int TRUE / 0 Suppress output. Python uses verbose (0 = silent)
fast logical bool TRUE / True Use fast ternary search

Information Criteria

  • "AICc": Corrected AIC (default)
  • "AIC": Akaike Information Criterion
  • "BIC": Bayesian Information Criterion
  • "BICc": Corrected BIC

Order Selection

When order=NULL, the function automatically selects the optimal order:

  • Fast mode (fast=TRUE): Modified ternary search - finds local minimum quickly
  • Full mode (fast=FALSE): Exhaustive search - guarantees global minimum but slower

Output

The sma() function internally uses adam() with model="NNN" and AR coefficients all equal to 1/n. It returns an object of class "adam". In Python, SMA subclasses ADAM, so the same elements are exposed as attributes/properties on the fitted object (Type (Python) columns below; marks elements with no Python equivalent yet).

Model Information

Element Type (R) Type (Python) Description
model character str Model name, e.g. "SMA(12)" (model.model)
timeElapsed difftime float Time elapsed for model construction (model.time_elapsed_)
call call The function call (R only)
orders integer int The SMA order (R: orders(model); Python: embedded in model.model)

State Space Components

Element Type (R) Type (Python) Description
states matrix NDArray State matrix, observations × states (model.states)
transition matrix NDArray Transition matrix F, all elements = 1/n (model.transition)
persistence numeric vector NDArray Persistence vector g, all elements = 1/n (model.persistence)
measurement numeric vector NDArray Measurement vector w (model.measurement)
initial numeric vector dict Initial state vector values (model.initial_value)
initialType character str Type of initial values used (model.initial_type)

Fitted Values and Forecasts

Element Type (R) Type (Python) Description
fitted vector pd.Series / NDArray Fitted values (model.fitted)
forecast vector pd.Series Point forecasts for h steps ahead (model.predict(h).mean)
lower vector pd.DataFrame Lower bound of prediction interval (model.predict(h, interval=...).lower)
upper vector pd.DataFrame Upper bound of prediction interval (model.predict(h, interval=...).upper)
residuals vector NDArray Model residuals (model.residuals)
errors matrix Matrix of 1 to h steps ahead errors (for multistep losses)

Model Fit Statistics

Element Type (R) Type (Python) Description
s2 numeric float Residual variance, adjusted for degrees of freedom (model.sigma ** 2)
logLik numeric float Log-likelihood value (model.loglik)
lossValue numeric float Cost function value (model.loss_value)
loss character str Type of loss function used (model.loss_)
nParam matrix int Number of estimated parameters (model.nparam)

Data

Element Type (R) Type (Python) Description
y vector/ts NDArray Original data (model.actuals)
holdout vector/ts NDArray Holdout part of original data (model.holdout_data)
interval character str Type of interval requested (predict() argument)
level numeric float / list Confidence level for interval (predict() argument)
cumulative logical bool Whether forecast was cumulative (predict() argument)

Order Selection (when order=NULL / order=None)

Element Type (R) Type (Python) Description
ICs named numeric vector dict IC values for each tested order (R: model$ICs; Python: model.ICs_)

Accuracy (when holdout=TRUE)

Element Type (R) Type (Python) Description
accuracy numeric vector Accuracy measures (R only; not auto-computed in Python)

Accessing Elements

model <- sma(y, h=18, holdout=TRUE)

# Model name
model$model

# Get the order
orders(model)

# Fitted values and residuals
fitted(model)
residuals(model)

# Information criteria
AIC(model)
BIC(model)

# If order was auto-selected, see all tested IC values
model$ICs

The same in Python (note the trailing-underscore fitted attributes):

model = SMA(h=18, holdout=True)
model.fit(y)

# Model name
model.model

# Fitted values and residuals
model.fitted
model.residuals

# Information criteria
model.aic
model.bic

# If order was auto-selected, see all tested IC values
model.ICs_

Prediction Intervals

SMA supports various interval types:

# Empirical intervals (bootstrap-based)
forecast(model, h=12, interval="empirical")

# Parametric intervals (normal assumption)
forecast(model, h=12, interval="parametric")

# No intervals
forecast(model, h=12, interval="none")

In Python (R "parametric" → Python "prediction"):

# Parametric intervals (normal assumption)
model.predict(h=12, interval="prediction")

# Simulated intervals (Monte Carlo)
model.predict(h=12, interval="simulated")

# No intervals
model.predict(h=12, interval="none")

Note: for the Python SMA, interval="empirical" is not yet supported (use "prediction" or "simulated").

Comparison to Base R

Unlike stats::filter() which only provides smoothed values, smooth::sma():

  • Provides proper prediction intervals
  • Allows automatic order selection
  • Returns state-space components
  • Offers information criteria

Use Cases

SMA is appropriate when:

  • Simple averaging is conceptually appropriate
  • You want automatic order selection
  • You need proper uncertainty quantification
  • The series has no clear trend or seasonality

For trend or seasonal data, consider ES or ADAM.

Python

SMA is available in Python via the SMA class — see Python Usage above. It mirrors the R sma() function, including automatic order selection, multi-step forecasts, and prediction intervals. Unlike pandas' y.rolling(window=n).mean() (which only smooths in-sample), SMA produces proper recursive forecasts and uncertainty quantification.

from smooth import SMA

model = SMA(h=12)        # order auto-selected by AICc
model.fit(y)
print(model.model)       # e.g. "SMA(3)"
fc = model.predict(h=12)

References

  • Svetunkov, I., & Petropoulos, F. (2018). Old dog, new tricks: a modelling view of simple moving averages. International Journal of Production Research, 56(18), 6034-6047. DOI: 10.1080/00207543.2017.1380326

See Also

  • ES - Exponential Smoothing
  • ADAM - Unified framework
  • MSARIMA - ARIMA models

Clone this wiki locally