The GARCH family of volatility models

Today let's see how the GARCH family of models describes a return series whose ups and downs grow and shrink over time.

The data is the FTSE 100, the London stock index, over 1,859 trading days from 1991 to 1998. Each day's return is the percentage change in the index from the day before. Across all 1,859 days the average return is 0.0432 and the standard deviation is 0.7958, and the largest single move is 5.44.

But that one standard deviation hides something. Take the standard deviation of any 20 consecutive days and it runs from a low of 0.369 to a high of 1.898. The chart below shows 120 consecutive days where the change is easy to see: the standard deviation of the first 60 days is 0.68, and of the last 60 days it is 1.42.

So the variance of these returns is not one fixed number. Small moves come in runs and large moves come in runs, a pattern called volatility clustering, and the size of the swings changes from one stretch to the next. The question for this lesson is which equation for the variance follows those changes with the fewest parameters. ARCH is the starting point, and GARCH, GJR and EGARCH are the family that grows from it.

Why does an ARCH model need so many lags?

Let's start by building the data. The return is 100 times the change in the log of the index, and the shock is a day's return minus the average return. The tseries function we use has no mean term, so we fit it to the shocks.

RInteractive R
# Build the FTSE 100 daily returns and the shocks suppressMessages(library(tseries)) ftse <- 100 * diff(log(as.numeric(EuStockMarkets[, "FTSE"]))) # daily return in percent e <- ftse - mean(ftse) # shock: the return minus its mean length(ftse) round(c(mean = mean(ftse), sd = sd(ftse), largest_move = max(abs(ftse))), 4) roll_sd <- sapply(20:length(ftse), function(i) sd(ftse[(i - 19):i])) # sd of every run of 20 days round(range(roll_sd), 3) #> [1] 1859 #> mean sd largest_move #> 0.0432 0.7958 5.4396 #> [1] 0.369 1.898

  

The last line prints the range of that 20-day standard deviation, 0.369 to 1.898, so the spread of the shocks depends on when you look. The number a volatility model has to follow is therefore not the plain variance of all 1,859 days. It is the variance of today's shock given the shocks up to yesterday, called the conditional variance.

How do we predict it? The shocks average 0, so the average of a squared shock is the variance, which makes a squared shock a noisy measurement of that day's variance. If busy days follow busy days, the squared shocks should be correlated from one day to the next.

RInteractive R
# Check whether a large squared shock is followed by a large squared shock round(acf(e^2, plot = FALSE)$acf[2], 3) # lag 1 autocorrelation of the squared shocks round(2 / sqrt(length(e)), 3) # the band around 0 that chance alone stays inside #> [1] 0.105 #> [1] 0.046

  

The lag 1 autocorrelation is 0.105. With 1,859 days, chance alone would keep it within about 0.046 of zero (two divided by the square root of 1,859), so yesterday's squared shock does carry information about today's variance.

An ARCH(q) model uses exactly that. It writes today's conditional variance as a constant plus a weighted sum of the last q squared shocks.

\[ \sigma_t^2 = \omega + \alpha_1 \varepsilon_{t-1}^2 + \alpha_2 \varepsilon_{t-2}^2 + \cdots + \alpha_q \varepsilon_{t-q}^2 \]

Here \(\sigma_t^2\) is the conditional variance of day t, \(\varepsilon_{t-j}^2\) is the squared shock j days back, \(\omega\) is a constant and the \(\alpha_j\) are the weights. So a large shock j days ago raises today's variance by \(\alpha_j\) times its square.

How many lags do we need? Let's fit ARCH(1), ARCH(5) and ARCH(10) and compare them by AIC. AIC is minus twice the log-likelihood plus twice the number of parameters, so lower is better and every extra parameter adds a penalty of 2. In garch(e, order = c(0, q)), the first number is the count of GARCH terms and the second is the count of ARCH terms, so c(0, q) is ARCH(q).

RInteractive R
# Fit ARCH(1), ARCH(5) and ARCH(10) and compare their AIC fit_arch1 <- garch(e, order = c(0, 1), trace = FALSE) fit_arch5 <- garch(e, order = c(0, 5), trace = FALSE) fit_arch10 <- garch(e, order = c(0, 10), trace = FALSE) arch_fits <- list(fit_arch1, fit_arch5, fit_arch10) data.frame( q = c(1, 5, 10), parameters = sapply(arch_fits, function(f) length(coef(f))), AIC = round(sapply(arch_fits, AIC), 2) ) #> q parameters AIC #> 1 1 2 4398.58 #> 2 5 6 4340.46 #> 3 10 11 4292.34

  

The AIC falls from 4398.58 to 4340.46 to 4292.34, even though ARCH(10) has 11 parameters (the constant and ten weights) against 2 for ARCH(1). So the FTSE 100 shocks call for a long lag order. Here are the ten weights of ARCH(10).

RInteractive R
# Print the ten ARCH(10) weights and plot them arch10_w <- coef(fit_arch10)[-1] # drop the constant, keep the ten weights round(arch10_w, 4) #> a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 #> 0.0587 0.0542 0.0782 0.0064 0.0552 0.0761 0.1214 0.0428 0.0114 0.0529 round(sum(arch10_w), 4) #> [1] 0.5573 barplot(arch10_w, names.arg = 1:10, col = "grey70", xlab = "days back", ylab = "weight on the squared shock")

  

All ten weights are positive and they add up to 0.5573. But they do not fall off smoothly: lag 4 gets 0.0064 and lag 7 gets 0.1214. A variance that reacts to shocks from 10 days back takes ten weights to write down one lag at a time, so is there a shorter way to carry the old shocks along?