EasyTime Series

Simple Moving Average

Time Series

Easy

Problem

The Simple Moving Average (SMA) is the most basic time series smoothing technique. It computes the unweighted mean of a sliding window of consecutive observations, producing a smoother signal that filters out short-term fluctuations and highlights longer-term trends.

Given a list of numeric values and a window size k, compute the SMA for each valid window position.

Algorithm

For each position i from 0 to n - k, compute the average of k consecutive values:

\text{SMA}[i] = \frac{1}{k} \sum_{j=0}^{k-1} x[i+j]

The output has length n - k + 1, where n is the input length.

Return n minus window_size plus one moving-average values.

Theory

A simple moving average (SMA) is the mean of the most recent w observations in a time series. It creates a smoothed version of the data by averaging out short-term fluctuations.

\text{SMA}_t(w) = \frac{1}{w} \sum_{i=0}^{w-1} y_{t-i}

where w is the window size.


The Formula

For a time series y_1, y_2, ..., y_T, the SMA at time t with window w is:

\text{SMA}_t = \frac{y_t + y_{t-1} + ... + y_{t-w+1}}{w}

Example with w = 3:

\text{SMA}_t = \frac{y_t + y_{t-1} + y_{t-2}}{3}


Worked Example

Time series: [10, 12, 15, 14, 16, 18, 17]

Window size: w = 3

Calculations:

Result: [NaN, NaN, 12.33, 13.67, 15.00, 16.00, 17.00]

First w-1 values are undefined.


Purpose and Use Cases

Smoothing:

Remove random noise and reveal underlying trends.

Trend identification:

Upward slope indicates rising trend, downward slope indicates declining trend.

Support and resistance:

In trading, SMAs act as dynamic support/resistance levels.

Forecast baseline:

Simple predictions: next value = current SMA.


Window Size Selection

Small window (e.g., w=3):

Large window (e.g., w=50):

Trade-off: Responsiveness vs smoothness.


Lag Effect

SMA is a lagging indicator: it reacts to changes after they occur.

Delay: Approximately \frac{w-1}{2} time steps.

Example: With w=10, SMA lags by about 4.5 time steps.

Implication: Not suitable for real-time change detection. Trend has already changed when SMA shows it.


Handling Edges

At the start (t < w):

Cannot compute full SMA.

Options:

  1. Leave as NaN/undefined
  2. Use expanding window: \text{SMA}_t = \frac{1}{t} \sum_{i=1}^{t} y_i
  3. Pad with initial value or global mean

Most common: Leave initial values as NaN.


SMA for Forecasting

Naive forecast:

\hat{y}_{t+1} = \text{SMA}_t

Predict next value as the average of recent values.

Multi-step:

\hat{y}_{t+h} = \text{SMA}_t \text{ for all } h > 0

Flat forecast (constant).


Centered Moving Average

Instead of using past values only, center the window:

\text{CMA}_t = \frac{1}{w} \sum_{i=-(w-1)/2}^{(w-1)/2} y_{t+i}

For odd w=5:

\text{CMA}_t = \frac{y_{t-2} + y_{t-1} + y_t + y_{t+1} + y_{t+2}}{5}

Advantage: No lag, better for smoothing.

Disadvantage: Requires future values (not causal).

Use for retrospective analysis, not forecasting.


SMA in Stock Trading

Golden Cross: Fast SMA (50-day) crosses above slow SMA (200-day). Bullish signal.

Death Cross: Fast SMA crosses below slow SMA. Bearish signal.

Price vs SMA:


Multiple SMAs

Use several windows simultaneously:

Interpretation:


SMA vs Exponential Moving Average

SMA: Equal weight to all w observations.

EMA: Exponentially decaying weights, more weight to recent values.

\text{EMA}_t = \alpha y_t + (1-\alpha) \text{EMA}_{t-1}

SMA advantages:

EMA advantages:


Computational Efficiency

Naive approach: Recompute sum each time.

Time complexity: O(w) per value, O(Tw) total.

Efficient approach: Running sum.

\text{SMA}_t = \text{SMA}_{t-1} + \frac{y_t - y_{t-w}}{w}

Time complexity: O(1) per value, O(T) total.

Subtract oldest value, add newest, divide by w.


SMA for Seasonal Adjustment

Use SMA with window = seasonal period to remove seasonality:

Monthly data with yearly seasonality:

w = 12 averages out the seasonal pattern.

Result: Trend-cycle component without seasonal fluctuations.

Caveat: Also removes any signal at that frequency.


Signal Extraction

SMA decomposes the series:

y_t = \text{Trend}_t + \text{Noise}_t

where \text{Trend}_t \approx \text{SMA}_t and \text{Noise}_t = y_t - \text{SMA}_t.

Residual analysis: Examine y_t - \text{SMA}_t for patterns.


Frequency Domain Interpretation

SMA acts as a low-pass filter:

Transfer function: Sinc function in frequency domain.

Cutoff frequency: Depends on w; larger w has lower cutoff.


Weighted vs Unweighted

SMA assigns equal weight \frac{1}{w} to each observation.

Truncation effect: Observation t-w has weight \frac{1}{w}, observation t-w-1 has weight 0.

Discontinuous weights cause edge effects.

Alternative: Weighted MA with smooth weights (triangular, Gaussian).


SMA for Anomaly Detection

Method:

  1. Compute SMA
  2. Compute standard deviation of residuals
  3. Flag points where |y_t - \text{SMA}_t| > k \sigma

Typical: k=3 for 3-sigma rule.

Use case: Detecting unusual spikes or drops in metrics.


Double Moving Average

Apply SMA twice:

\text{SMA2}_t = \text{SMA}(\text{SMA}_t)

Effect: Even smoother, but even more lag.

Used in double exponential smoothing context for capturing trends.

Examples

Example 1

Input
values = [1, 2, 3, 4, 5], window_size = 3
Output
[2.0, 3.0, 4.0]
Explanation
The three complete windows have means 2, 3, and 4.

Example 2

Input
values = [10, 20, 30, 40], window_size = 2
Output
[15.0, 25.0, 35.0]

Hints

  1. Iterate over every start index that leaves a complete window.
  2. Average the slice from the current start through window_size elements.

Requirements

Constraints

Starter Code

def simple_moving_average(values: list, window_size: int) -> list:
    """
    Returns the mean of every complete sliding window.
    """
    # Write code here
    pass

Test Cases

CaseMatches
Basic sliding windowpublic
Pairspublic