Skip to content

Fitting a GARCH Model and Reading the Output in Python

Original question

Can you walk me through fitting a GARCH model to historical stock returns using Python and interpreting its parameters?
Convert adjusted closing prices to percentage returns, choose a mean and innovation distribution, and fit GARCH(1,1) by maximum likelihood. Interpret α\alpha as the reaction to the most recent squared surprise, β\beta as the weight on the previous variance forecast, and α+β\alpha+\beta as expected variance persistence. Then check the standardised residuals and evaluate forecasts on observations held out in time order.
GARCH updates a variance forecast in much the same way that a moving average updates a running estimate. It also accounts for what the model already expected. A 2% daily return is a larger surprise during a calm month than during a crisis. Dividing the fitted residual by the fitted conditional volatility shows how large the move was in forecast volatility units.

Read the likelihood and parameters together

For a constant mean μ\mu, the model is

rt=μ+εt,εt=htzt,ht=ω+αεt−12+βht−1.r_t=\mu+\varepsilon_t, \quad \varepsilon_t=\sqrt{h_t}z_t, \quad h_t=\omega+\alpha\varepsilon_{t-1}^2+\beta h_{t-1}.

The optimiser evaluates a conditional likelihood using the innovation distribution for ztz_t. For Gaussian innovations the contribution, up to a constant, is

ℓt=−12(log⁡ht+(rt−μ)2ht).\ell_t=-\frac12\left(\log h_t+\frac{(r_t-\mu)^2}{h_t}\right).
A large miss costs more in the likelihood on a date when predicted variance is low, while the log term discourages making every variance forecast large. A standardised Student-t likelihood allows heavier tails. Its fitted degrees of freedom, often printed as nu, govern tail weight, with smaller values above two giving heavier tails. When α+β<1\alpha+\beta<1, the model has a finite long-run variance of ω/(1−α−β)\omega/(1-\alpha-\beta) under the usual stationary specification.
Suppose a fit in percent units reports ω=0.04\omega=0.04, α=0.12\alpha=0.12 and β=0.84\beta=0.84. The implied long-run variance is 0.04/(1−0.96)=1.000.04/(1-0.96)=1.00 in percentage-point-squared units. If today's conditional variance is 1.00 and today's surprise is −3%-3\%, the next forecast is 0.04+0.12(9)+0.84(1)=1.960.04+0.12(9)+0.84(1)=1.96, or 1.4%1.4\% daily volatility. Those numbers are illustrative. The code estimates its own values from a bundled historical S&P 500 price series.
When returns are entered in percentage points, hth_t is in squared percentage points. To report daily volatility, take the square root first. If the output is 1.41.4 in percentage-point units, convert it to 0.0140.014 for a decimal-volatility calculation. To see whether the variance recursion is doing its job, divide the residuals by the fitted conditional standard deviation and check the squared standardised residuals for dependence. A close-to-one persistence estimate can produce a plausible in-sample fit while leaving the long-run variance very sensitive to small parameter changes.

Fit and diagnose the model in Python

import numpy as np import arch.data.sp500 from arch import arch_model from statsmodels.stats.diagnostic import acorr_ljungbox prices = arch.data.sp500.load()["Adj Close"].sort_index() returns_pct = 100 * prices.pct_change().dropna() train = returns_pct.iloc[:-252] fit = arch_model(train, mean="Constant", vol="GARCH", p=1, q=1, dist="t").fit(disp="off") print(fit.params) print("Convergence flag:", fit.convergence_flag) std_resid = fit.std_resid.dropna() print(acorr_ljungbox(std_resid**2, lags=[10, 20])) next_variance = fit.forecast(horizon=1, reindex=False).variance.iloc[-1, 0] print("First held-out day volatility in percent:", np.sqrt(next_variance))

For a full evaluation, roll the forecast origin forward and compare against a constant variance and an EWMA benchmark using the same information cutoff at every date.

Interactive example

Explore how quickly a GARCH shock decays

Start at four times long-run variance. A stationary GARCH(1,1) with persistence α + β has a multi-step variance forecast that decays geometrically toward 1.

Half-life 9.6 periods · after 40 periods 1.16× long-run variance
Expected variance decays toward its long-run value1234010203040Periods after shockVariance multiple
  • Variance forecast
  • Long-run variance

References and next steps