Fitting GARCH in R
Today let's fit a GARCH model to a real stock index in R, and then check how well the fit describes the data.
Our data are the daily closing prices of the German DAX index. From those prices we get 1,859 daily returns, one for each trading day from mid-1991 to 1998. A daily return is the change in the log of the closing price from one trading day to the next, written in percent. The standard deviation of the 1,859 returns is close to 1%, so on a typical day the index moves about 1% up or down.
But that 1% is an average over the whole period, and it hides something. The chart below shows the returns of trading days 1600 to 1800. From day 1600 to day 1660 the line swings widely, and the two largest moves in the window are on days 1651 and 1652, when the return was -6.01% and then +4.32%. From day 1720 to day 1780 the swings are much smaller.
In this chart the large returns come in a group, and so do the small ones.
The DAX daily returns and a check for ARCH effects
Before we fit anything, let's build the returns and check that their variance really does change over time.
The prices come from EuStockMarkets, a dataset that ships with R. It holds the daily closing prices of four European stock indices, and we take the DAX column. The prices are sampled in business time: weekends and holidays are left out and there are no calendar dates, so from here on we count trading days.
We work with log returns. The return on day t is the log of that day's price minus the log of the previous day's price, which is what diff(log(price)) computes. For small moves it is close to the percentage change, and unlike the percentage change it adds up across days. The first price has no day before it, so 1,860 prices give 1,859 returns, and we call the t-th return day t.
The first block builds the returns r, summarises them in percent and prints their variance.
The mean return is 0.065% a day and the standard deviation is 1.03%. The worst day was -9.63% and the best was +5.08%. The variance of r is 0.000106, which is a small number because the returns are small decimals.
What we want to know is whether that spread is the same on every day. The tool for it is the autocorrelation function (ACF): the correlation of a series with itself k days back, where k is the lag. At lag 1 it is the correlation of today's value with yesterday's, at lag 2 with the value two days back, and so on.
Here is the idea. If the variance changes in stretches, then the size of a return is related to the size of the returns just before it: a big move, up or down, tends to be followed by another big move. The size of a return shows up in its square. So we check for autocorrelation in r^2. The returns r themselves can stay uncorrelated, because the direction of the moves does not have to follow a pattern.
The Ljung-Box test looks at all the autocorrelations up to a chosen lag at once. Its null hypothesis is that there is no autocorrelation up to that lag, and a small p-value rejects it. The block runs it at lag 10 on r and on r^2.
On r, the statistic Q is 6.37 and p is 0.784. Under the null hypothesis Q follows a chi-squared distribution with 10 degrees of freedom, which has a mean of 10, so 6.37 is nothing unusual. There is no evidence of autocorrelation in the returns, and that is why the mean equation can be a constant. On r^2, Q is 110.75 and p is below 2.2e-16, so the null hypothesis is clearly rejected.
The table below puts the autocorrelations of r and r^2 at lags 1 to 5 side by side.
The autocorrelations of the returns are all within 0.032 of zero. Those of the squared returns are 0.079, 0.171, 0.074, 0.078 and 0.053, all positive.