Exponential Moving Average
Time Series
Easy
Problem
The Exponential Moving Average (EMA) is a weighted moving average that gives more importance to recent observations. Unlike the Simple Moving Average which weights all values equally, EMA applies exponentially decreasing weights to older data points, making it more responsive to recent changes.
Given a list of numeric values and a smoothing factor alpha (0 < alpha <= 1), compute the EMA for each position.
Algorithm
Initialize with the first value and apply the recursive formula:
\text{EMA}[0] = x[0]
\text{EMA}[t] = \alpha \cdot x[t] + (1 - \alpha) \cdot \text{EMA}[t-1]
Higher alpha values make the EMA respond faster to recent changes, while lower alpha values produce a smoother signal.
Return one EMA value for each input value.
Theory
An exponential moving average (EMA) is a weighted average that gives more weight to recent observations. Weights decrease exponentially as observations get older.
\text{EMA}_t = \alpha y_t + (1-\alpha) \text{EMA}_{t-1}
where 0 < \alpha < 1 is the smoothing parameter.
The Formula
For a time series y_1, y_2, ..., y_T:
\text{EMA}_t = \alpha y_t + (1-\alpha) \text{EMA}_{t-1}
Recursive form: Current EMA combines current observation with previous EMA.
Expanded form:
\text{EMA}_t = \alpha \sum_{i=0}^{t-1} (1-\alpha)^i y_{t-i}
Weight on observation i periods ago: \alpha(1-\alpha)^i
Weights decay exponentially with age.
Smoothing Parameter
Alpha (\alpha):
Controls responsiveness to new data.
High \alpha (close to 1):
- More weight on recent observations
- Fast adaptation to changes
- Less smoothing, more noise
Low \alpha (close to 0):
- More weight on historical observations
- Slow adaptation, smoother curve
- More smoothing, less noise
Typical range: 0.1 \leq \alpha \leq 0.3
Alpha and Window Size Equivalence
EMA with parameter \alpha approximates SMA with window:
w \approx \frac{2}{\alpha} - 1
Examples:
- \alpha = 0.1: w \approx 19
- \alpha = 0.2: w \approx 9
- \alpha = 0.5: w \approx 3
Interpretation: \alpha = 0.2 gives similar smoothing to 9-period SMA.
Initialization
First value (\text{EMA}_1):
Common: \text{EMA}_1 = y_1
Alternative: \text{EMA}_1 = \text{average of first few observations}
Impact: Initial choice matters for first few periods, then influence fades exponentially.
After n periods: Weight on initial value is (1-\alpha)^n
Example: With \alpha=0.2, after 20 periods weight is (0.8)^{20} = 0.0115 (1.15%).
Worked Example
Data: [100, 105, 103, 108, 110]
Parameter: \alpha = 0.3
Initialization: \text{EMA}_1 = 100
Period 2:
\text{EMA}_2 = 0.3(105) + 0.7(100) = 31.5 + 70 = 101.5
Period 3:
\text{EMA}_3 = 0.3(103) + 0.7(101.5) = 30.9 + 71.05 = 101.95
Period 4:
\text{EMA}_4 = 0.3(108) + 0.7(101.95) = 32.4 + 71.365 = 103.765
Period 5:
\text{EMA}_5 = 0.3(110) + 0.7(103.765) = 33 + 72.636 = 105.636
Result: [100, 101.5, 101.95, 103.765, 105.636]
Weight Distribution
Weight on current observation: \alpha
Weight on observation 1 period ago: \alpha(1-\alpha)
Weight on observation 2 periods ago: \alpha(1-\alpha)^2
Weight on observation k periods ago: \alpha(1-\alpha)^k
Sum of all weights: \alpha \sum_{k=0}^{\infty} (1-\alpha)^k = \alpha \cdot \frac{1}{1-(1-\alpha)} = 1
Weights sum to 1 (proper average).
Comparison to Simple Moving Average
SMA weights: \frac{1}{w} for last w observations, 0 for older.
EMA weights: Exponentially decaying, never exactly zero.
SMA: Step function in weights.
EMA: Smooth decay in weights.
Advantage SMA: Clear interpretation (average of last w values).
Advantage EMA: All history considered, continuous weighting, faster computation.
Computational Efficiency
SMA: Requires storing last w values. O(w) per update.
EMA: Only requires previous EMA. O(1) per update.
Memory: EMA needs constant memory (just previous value), SMA needs O(w) memory.
Real-time systems: EMA preferable for low memory footprint and fast updates.
Multiple EMAs
Fast and slow EMAs:
- Fast: Small window (high \alpha), e.g., \alpha = 0.2 (10-period equivalent)
- Slow: Large window (low \alpha), e.g., \alpha = 0.05 (40-period equivalent)
Golden cross: Fast EMA crosses above slow EMA (bullish signal).
Death cross: Fast EMA crosses below slow EMA (bearish signal).
Application: Trading strategies, trend confirmation.
MACD Indicator
Moving Average Convergence Divergence:
\text{MACD} = \text{EMA}_{12} - \text{EMA}_{26}
Signal line:
\text{Signal} = \text{EMA}_9(\text{MACD})
Histogram:
\text{Histogram} = \text{MACD} - \text{Signal}
Interpretation:
- MACD crosses above signal: Buy signal
- MACD crosses below signal: Sell signal
- Histogram shows momentum strength
Lag Properties
Lag in EMA:
\text{Lag} \approx \frac{1-\alpha}{\alpha}
Example: \alpha = 0.2 gives lag of \frac{0.8}{0.2} = 4 periods.
Comparison to SMA lag: \frac{w-1}{2}
For SMA with w=9: lag = 4 periods (same as EMA with \alpha=0.2).
Implication: EMA and equivalent SMA have similar lag characteristics.
Forecasting with EMA
One-step ahead forecast:
\hat{y}_{t+1} = \text{EMA}_t
Multi-step ahead:
\hat{y}_{t+h} = \text{EMA}_t \text{ for all } h > 0
Flat forecast: EMA assumes no trend, predicts constant future.
Use case: Appropriate for stationary series with no trend.
Limitation: Poor for trending data. Use double exponential smoothing instead.
Connection to Simple Exponential Smoothing
EMA is equivalent to simple exponential smoothing (SES):
SES forecast:
\hat{y}_{t+1} = \alpha y_t + (1-\alpha)\hat{y}_t
Substituting \hat{y}_t = \text{EMA}_{t-1}:
\hat{y}_{t+1} = \alpha y_t + (1-\alpha)\text{EMA}_{t-1} = \text{EMA}_t
Interpretation: EMA is both a smoothing technique and a forecasting method.
Optimal Alpha Selection
Minimize forecast error:
\alpha^* = \arg\min_{\alpha} \sum_{t=1}^{T} (y_t - \text{EMA}_{t-1})^2
Grid search: Try \alpha \in \{0.01, 0.02, ..., 0.99\}, select best.
Closed-form: No analytical solution. Numerical optimization required.
Cross-validation: Split data, optimize on training set, validate on test set.
Typical result: \alpha \in [0.1, 0.3] for most applications.
Adaptive Exponential Smoothing
Problem: Fixed \alpha may not suit all periods.
Solution: Let \alpha vary over time.
Example adaptive rule:
\alpha_t = |\frac{e_t}{y_t}|
where e_t = y_t - \text{EMA}_{t-1}
Interpretation: Increase \alpha when forecast errors are large (adapt quickly).
Constraints: Bound \alpha_t to [\alpha_{\min}, \alpha_{\max}] to prevent instability.
Centered vs Trailing EMA
Trailing EMA (standard):
\text{EMA}_t = \alpha y_t + (1-\alpha)\text{EMA}_{t-1}
Uses only past values (causal).
Centered EMA:
Apply EMA forward and backward, then average.
\text{EMA}_{\text{centered},t} = \frac{\text{EMA}_{\text{forward},t} + \text{EMA}_{\text{backward},t}}{2}
Advantage: No lag, better smoothing.
Disadvantage: Requires future values (non-causal). Cannot forecast.
Use: Historical analysis, not real-time forecasting.
Variance of EMA
For white noise input with variance \sigma^2:
\text{Var}(\text{EMA}_t) = \frac{\alpha}{2-\alpha} \sigma^2
Higher \alpha: Higher variance (less smoothing).
Lower \alpha: Lower variance (more smoothing).
Variance reduction factor:
\frac{\text{Var}(\text{EMA}_t)}{\text{Var}(y_t)} = \frac{\alpha}{2-\alpha}
Example: \alpha = 0.2 gives variance reduction to \frac{0.2}{1.8} = 0.111 (11.1% of original).
Signal-to-Noise Ratio
Goal: Maximize signal retention while minimizing noise.
Trade-off: Low \alpha removes more noise but also removes signal.
Optimal choice depends on:
- Signal frequency
- Noise characteristics
- Application requirements
High SNR data: Use higher \alpha (less smoothing needed).
Low SNR data: Use lower \alpha (more smoothing needed).
EMA for Trend Detection
EMA slope:
\text{Slope}_t = \text{EMA}_t - \text{EMA}_{t-1} = \alpha(y_t - \text{EMA}_{t-1})
Positive slope: Upward trend.
Negative slope: Downward trend.
Acceleration:
\text{Acceleration}_t = \text{Slope}_t - \text{Slope}_{t-1}
Monitoring: Track slope changes for early trend reversal detection.
Bollinger Bands with EMA
Middle band: EMA of price.
Upper band: \text{EMA}_t + k \cdot \sigma_t
Lower band: \text{EMA}_t - k \cdot \sigma_t
where \sigma_t is rolling standard deviation and k=2 typically.
Interpretation:
- Price near upper band: Overbought
- Price near lower band: Oversold
- Squeeze (bands narrow): Low volatility, potential breakout
Exponential Weighting in Other Contexts
Exponentially weighted variance:
\sigma_t^2 = \lambda \sigma_{t-1}^2 + (1-\lambda) r_t^2
Used in GARCH models for volatility.
Exponentially weighted covariance:
\text{Cov}_t(x, y) = \lambda \text{Cov}_{t-1}(x, y) + (1-\lambda) x_t y_t
RiskMetrics: \lambda = 0.94 for daily data.
Principle: Recent data more relevant for current risk.
Double Exponential Smoothing Extension
Problem: EMA assumes no trend.
Solution: Add trend component (Holt's method).
\ell_t = \alpha y_t + (1-\alpha)(\ell_{t-1} + b_{t-1})
b_t = \beta(\ell_t - \ell_{t-1}) + (1-\beta)b_{t-1}
Forecast:
\hat{y}_{t+h} = \ell_t + h \cdot b_t
Linear trend instead of flat.
Triple Exponential Smoothing
Holt-Winters method:
Adds seasonal component to level and trend.
Three equations:
- Level: \ell_t = \alpha(y_t - s_{t-m}) + (1-\alpha)(\ell_{t-1} + b_{t-1})
- Trend: b_t = \beta(\ell_t - \ell_{t-1}) + (1-\beta)b_{t-1}
- Seasonal: s_t = \gamma(y_t - \ell_t) + (1-\gamma)s_{t-m}
Forecast:
\hat{y}_{t+h} = \ell_t + hb_t + s_{t+h-m}
Handles trend and seasonality.
Residual Analysis
One-step forecast errors:
e_t = y_t - \text{EMA}_{t-1}
Good fit: Residuals should be white noise.
Diagnostic plots:
- Time plot of residuals (no patterns)
- ACF of residuals (no significant autocorrelation)
- Histogram of residuals (approximately normal)
Ljung-Box test: Test for autocorrelation in residuals.
Heteroscedasticity: Check if residual variance is constant.
EMA in Volatility Modeling
EWMA volatility:
\sigma_t^2 = \lambda \sigma_{t-1}^2 + (1-\lambda) r_t^2
where r_t is return at time t.
RiskMetrics approach: \lambda = 0.94 for daily returns.
Interpretation: Variance forecast adapts to recent squared returns.
Application: Value at Risk (VaR) calculations, portfolio risk management.
Filter Interpretation
EMA is a discrete-time first-order IIR (infinite impulse response) filter:
Transfer function:
H(z) = \frac{\alpha}{1 - (1-\alpha)z^{-1}}
Frequency response:
Low-pass filter. Attenuates high-frequency components (noise).
Cutoff frequency: Depends on \alpha. Higher \alpha has higher cutoff (less filtering).
Outlier Robustness
Problem: Single outlier disproportionately affects EMA.
Example: Outlier y_t = 1000 when typical values are 100.
\text{EMA}_t = 0.2(1000) + 0.8(100) = 280
Large jump. Persists for several periods.
Robust alternatives:
- Median-based smoothing
- Trimmed mean exponential smoothing
- Winsorization before applying EMA
Trade-off: Robustness vs sensitivity to legitimate changes.
EMA for Time Series Decomposition
Trend extraction:
\text{Trend}_t = \text{EMA}_t
Detrended series:
\text{Detrended}_t = y_t - \text{EMA}_t
Seasonal component: Apply EMA to detrended series at seasonal lag.
Residual:
\text{Residual}_t = y_t - \text{Trend}_t - \text{Seasonal}_t
Application: Decompose into components for separate analysis.
Practical Considerations
Choice of \alpha:
- Financial trading: \alpha \in [0.1, 0.2] (smooth trends)
- Web analytics: \alpha \in [0.2, 0.4] (responsive to changes)
- Sensor data: \alpha \in [0.05, 0.15] (heavy smoothing)
Initialization: Use first observation or average of first 5-10 observations.
Outliers: Consider robust preprocessing before applying EMA.
Trend: If data has strong trend, use double exponential smoothing instead.
Seasonality: If seasonal patterns exist, use Holt-Winters method.
Examples
Example 1
- Input
values = [1, 2, 3, 4, 5], alpha = 0.5- Output
[1, 1.5, 2.25, 3.125, 4.0625]- Explanation
- Each new EMA equally weights the current value and the previous EMA.
Example 2
- Input
values = [100, 0, 0, 0], alpha = 0.5- Output
[100, 50.0, 25.0, 12.5]
Hints
- Initialize the output with the first input value.
- Append alpha times the current value plus one minus alpha times the previous EMA.
Requirements
- Initialize EMA[0] with the first value in the series
- Apply the recursive EMA formula for each subsequent value
- Return a list of the same length as the input
Constraints
- values has at least 1 element
- 0 < alpha <= 1
- Return a list with the same length as values
- Time limit: 300 ms
Starter Code
def exponential_moving_average(values: list, alpha: float) -> list:
"""
Returns the exponential moving average at every position.
"""
# Write code here
passTest Cases
| Case | Matches | |
|---|---|---|
| Basic sequence | — | public |
| Decay to zero | — | public |