Modeling Volatility and GARCH Family › From Constant to Time-Varying Volatility
Quantitative FinanceAll chapters Modeling Volatility and GARCH Family › From Constant to Time-Varying VolatilityOne section · Sections in this chapter2 / 13 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
The Homoskedasticity Assumption
Recall from our time-series chapter that when we fit ARMA models to returns, we implicitly assumed that the error terms have constant variance. This property is called homoskedasticity, a term derived from the Greek words meaning "same dispersion." The assumption seems reasonable initially. If markets are efficient and information arrives randomly, why should the magnitude of price fluctuations vary systematically over time? Yet empirical observation tells a different story. Volatility itself becomes a dynamic, predictable quantity.
Mathematically, if we model returns as:
where:
- : return at time
- : constant mean return
- : residual or shock at time
homoskedasticity requires:
where:
- : variance of the error term
- : constant variance parameter
To appreciate what this assumption demands, consider what it implies. The variance of today's return shock must equal the variance of yesterday's shock, last month's shock, and the shock during the 2008 financial crisis. A single number, , must describe the dispersion of returns across all market conditions. This assumption is convenient for mathematical tractability, but it rarely holds for financial data. Let's see why by examining actual market data.
import numpy as np
import pandas as pd
def simulate_clustered_returns():
"""Create a deterministic fallback when live market data is unavailable."""
np.random.seed(42)
n_days = 6000 # Approximately 24 years of trading days
dates = pd.date_range(start="2000-01-01", periods=n_days, freq="B")
# Simulate returns with volatility clustering using a simple GARCH-like process
returns_vals = np.zeros(n_days)
sigma = 1.0
omega, alpha, beta = 0.02, 0.08, 0.90
for t in range(1, n_days):
sigma = np.sqrt(
omega + alpha * returns_vals[t - 1] ** 2 + beta * sigma**2
)
returns_vals[t] = sigma * np.random.standard_t(df=6)
return pd.Series(returns_vals, index=dates, name="returns").dropna()
try:
import yfinance as yf
# Download S&P 500 data
sp500 = yf.download(
"^GSPC", start="2000-01-01", end="2024-01-01", progress=False
)
if isinstance(sp500.columns, pd.MultiIndex):
sp500.columns = sp500.columns.get_level_values(0)
price_column = "Adj Close" if "Adj Close" in sp500.columns else "Close"
returns = sp500[price_column].pct_change().dropna() * 100
if returns.empty:
raise ValueError("No usable S&P 500 observations returned")
except (ImportError, KeyError, ValueError):
returns = simulate_clustered_returns()
The chart makes the clustering visible: returns during 2008-2009 swing widely between large positive and negative values, creating a dense band of extreme observations that dominates that section of the chart. The COVID-induced volatility in early 2020 shows a similar pattern, with daily moves that would be extraordinary during normal times occurring repeatedly over several weeks. In contrast, periods like 2017 or 2013 exhibit remarkably calm, small daily movements, with the return series appearing almost compressed near the zero line. This is volatility clustering in action: large returns (of either sign) tend to be followed by large returns, and small returns tend to be followed by small returns. Formal verification measures the persistence that the chart suggests.
Detecting Heteroskedasticity
We can formally test for time-varying variance using several approaches, each building on the fundamental insight that if variance were truly constant, certain statistical patterns should be absent from the data. The simplest and most intuitive approach is to examine the autocorrelation of squared returns. The logic is simple. If volatility were constant, knowing that yesterday's squared return was large would tell you nothing about today's squared return; they would be independent and show zero autocorrelation. However, if volatility clusters, a large squared return yesterday signals we are in a high-volatility regime, making a large squared return today more likely. This creates positive autocorrelation in the squared return series.
In practice, squared returns show strong positive autocorrelation that persists for many lags. We verify this using the Ljung-Box test on squared returns, specifically checking the test statistic and p-value at 10 lags. This test aggregates autocorrelation evidence across multiple lags into a single statistic, making it a powerful diagnostic for detecting the type of serial dependence that violates homoskedasticity.
from statsmodels.stats.diagnostic import acorr_ljungbox
# Test for autocorrelation in squared returns
squared_returns = returns**2
lb_test = acorr_ljungbox(squared_returns, lags=[10], return_df=True)Ljung-Box Test for Autocorrelation in Squared Returns ======================================================= Test statistic (10 lags): 5867.60 P-value: 0.000000
The highly significant Ljung-Box test confirms what we saw visually: squared returns are autocorrelated, meaning volatility exhibits persistence. The extremely small p-value allows us to reject the null hypothesis of no autocorrelation with overwhelming confidence. This finding is not unique to the S&P 500 or to our particular sample period; it emerges consistently across different markets, time periods, and asset classes. This is the phenomenon ARCH and GARCH models are designed to capture, and the universality of the finding motivates the development of a general modeling framework.

The ACF plot provides visual confirmation of what the Ljung-Box test detected statistically. The significant positive autocorrelation persists for many lags, with bars extending well beyond the confidence bands even at lags of 20 or 30 days. This slow decay indicates that volatility shocks have long-lasting effects, a key feature that GARCH models are designed to capture.
Heteroskedasticity refers to non-constant variance in a time series. In financial returns, this manifests as time-varying volatility where some periods exhibit high variance and others low variance. The opposite, homoskedasticity, assumes constant variance throughout the sample.