Modeling volatility
As we saw earlier, ARIMA models are used to model the conditional expectation of a process, given its past. For such a process, the conditional variance is constant. Real-world financial time series exhibit, among other characteristics, volatility clustering; that is, periods of relative calm are interrupted by bursts of volatility.
In this section we look at GARCH time series models that can take this stylized fact of real-world (financial) time series into account and apply these models to VaR forecasting.
Volatility forecasting for risk management
Financial institutions measure the risk of their activities using a Value-at-Risk (VaR), usually calculated at the 99% confidence level over a 10 business day horizon. This is the loss that is expected to be exceeded only 1% of the time.
We load the zoo
library and import monthly return data for Intel Corporation from January 1973 to December 2008.
> library("zoo") > intc <- read.zoo("intc.csv", header = TRUE,+ sep = ",", format = "%Y-%m", FUN = as.yearmon)
Testing for ARCH effects
A plot of the returns indicates that ARCH effects might exist in the monthly return data.
> plot(intc, main = "Monthly returns of Intel Corporation",+ xlab = "Date", ylab = "Return in percent")
The output of the preceding commands is as shown in the following figure:
We can use statistical hypothesis tests to verify our inkling. Two commonly used tests are as follows:
The Ljung-Box test for autocorrelation in squared returns (as a proxy for volatility)
The Lagrange Multiplier (LM) test by Engle (1982)
First, we perform the Ljung-Box test on the first 12 lags of the squared returns using the following command:
> Box.test(coredata(intc^2), type = "Ljung-Box", lag = 12) Box-Ljung test data: coredata(intc^2) X-squared = 79.3451, df = 12, p-value = 5.502e-12
We can reject the null hypothesis of no autocorrelations in the squared returns at the 1% significance level. Alternatively, we could employ the LM test from the FinTS
package, which gives the same result.
> install.packages("FinTS") > library("FinTS") > ArchTest(coredata(intc)) ARCH LM-test; Null hypothesis: no ARCH effects data: coredata(intc) Chi-squared = 59.3647, df = 12, p-value = 2.946e-08
Both tests confirm that ARCH effects exist in the monthly Intel returns; hence, an ARCH or GARCH model should be employed in modeling the return time series.
GARCH model specification
The most commonly used GARCH model, and one that is usually appropriate for financial time series as well, is a GARCH(1,1) model. We use the functions provided by the rugarch
library for model specification, parameter estimation, backtesting, and forecasting. If you haven't installed the package, use the following command:
> install.packages("rugarch")
Afterwards, we can load the library using the following command:
> library("rugarch")
First, we need to specify the model using the function ugarchspec
. For a GARCH(1,1) model, we need to set the garchOrder
to c(1,1)
and the model for the mean (mean.model
) should be a white noise process and hence equal to armaOrder =
c(0,0)
.
> intc_garch11_spec <- ugarchspec(variance.model = list(+ garchOrder = c(1, 1)),+ mean.model = list(armaOrder = c(0, 0)))
GARCH model estimation
The actual fitting of the coefficients by the method of maximum likelihood is done by the function ugarchfit
using the model specification and the return data as inputs.
> intc_garch11_fit <- ugarchfit(spec = intc_garch11_spec,+ data = intc)
For additional arguments, see the Help on ugarchfit
. The output of the fitted model (use the command intc_garch11_fit
) reveals useful information, such as the values of the optimal parameters, the value of the log-likelihood function, and the information criteria.
Backtesting the risk model
A useful test for checking the model performance is to do a historical backtest. In a risk model backtest, we compare the estimated VaR with the actual return over the period. If the return is more negative than the VaR, we have a VaR exceedance. In our case, a VaR exceedance should only occur in 1% of the cases (since we specified a 99% confidence level).
The function ugarchroll
performs a historical backtest on the specified GARCH model (here the model is intc_garch11_spec
). We specify the backtest as follows:
The return data to be used is stored in the
zoo
objectintc
The start period of the backtest (
n.start
) shall be 120 months after the beginning of the series (that is, January 1983)The model should be reestimated every month (
refit.every = 1
)We use a
moving
window for the estimationWe use a
hybrid
solverWe'd like to calculate the VaR (
calculate.VaR = TRUE
) at the 99% VaR tail level (VaR.alpha = 0.01
)We would like to keep the estimated coefficients (
keep.coef = TRUE)
The following command shows all the preceding points for a backtest:
> intc_garch11_roll <- ugarchroll(intc_garch11_spec, intc,+ n.start = 120, refit.every = 1, refit.window = "moving",+ solver = "hybrid", calculate.VaR = TRUE, VaR.alpha = 0.01,+ keep.coef = TRUE)
We can examine the backtesting report using the report
function. By specifying the type
argument as VaR
, the function executes the unconditional and conditional coverage tests for exceedances. VaR.alpha
is the tail probability and conf.level
is the confidence level on which the conditional coverage hypothesis test will be based.
> report(intc_garch11_roll, type = "VaR", VaR.alpha = 0.01,+ conf.level = 0.99) VaR Backtest Report =========================================== Model: sGARCH-norm Backtest Length: 312 Data: ========================================== alpha: 1% Expected Exceed: 3.1 Actual VaR Exceed: 5 Actual %: 1.6% Unconditional Coverage (Kupiec) Null-Hypothesis: Correct Exceedances LR.uc Statistic: 0.968 LR.uc Critical: 6.635 LR.uc p-value: 0.325 Reject Null: NO Conditional Coverage (Christoffersen) Null-Hypothesis: Correct Exceedances and Independence of Failures LR.cc Statistic: 1.131 LR.cc Critical: 9.21 LR.cc p-value: 0.568 Reject Null: O
Kupiec's unconditional coverage compares the number of expected versus actual exceedances given the tail probability of VaR, while the Christoffersen test is a joint test of the unconditional coverage and the independence of the exceedances. In our case, despite the actual five exceedances versus an expectation of three, we can't reject the null hypothesis that the exceedances are correct and independent.
A plot of the backtesting performance can also be generated easily. First, create a zoo
object using the extracted forecasted VaR from the ugarchroll
object.
> intc_VaR <- zoo(intc_garch11_roll@forecast$VaR[, 1])
We overwrite the index
property of the zoo
object with rownames
(year and month) from this object as well.
> index(intc_VaR) <- as.yearmon(rownames(intc_garch11_roll@forecast$VaR))
We do the same for the actual returns that are also stored in the ugarchroll
object.
> intc_actual <- zoo(intc_garch11_roll@forecast$VaR[, 2]) > index(intc_actual) <- as.yearmon(rownames(intc_garch11_roll@forecast$VaR))
Now, we are able to plot the VaR versus the actual returns of Intel using the following commands:
> plot(intc_actual, type = "b", main = "99% 1 Month VaR Backtesting",+ xlab = "Date", ylab = "Return/VaR in percent") > lines(intc_VaR, col = "red") > legend("topright", inset=.05, c("Intel return","VaR"), col = c("black","red"), lty = c(1,1))
The following figure shows the output of the preceding command lines:
Forecasting
Now that we can be reasonably sure that our risk model works properly, we can produce VaR forecasts as well. The function ugarchforecast
takes as arguments the fitted GARCH model (intc_garch11_fit
) and the number of periods for which a forecast should be produced (n.ahead = 12
; that is, twelve months).
> intc_garch11_fcst <- ugarchforecast(intc_garch11_fit, n.ahead = 12)
The resulting forecast can be expected by querying the forecast object as shown in the following command lines:
> intc_garch11_fcst *------------------------------------* * GARCH Model Forecast * *------------------------------------* Model: sGARCH Horizon: 12 Roll Steps: 0 Out of Sample: 0 0-roll forecast [T0=Dec 2008]: Series Sigma T+1 0.01911 0.1168 T+2 0.01911 0.1172 T+3 0.01911 0.1177 T+4 0.01911 0.1181 T+5 0.01911 0.1184 T+6 0.01911 0.1188 T+7 0.01911 0.1191 T+8 0.01911 0.1194 T+9 0.01911 0.1197 T+10 0.01911 0.1200 T+11 0.01911 0.1202 T+12 0.01911 0.1204
The one-period ahead forecast for the volatility (sigma) is 0.1168. Since we assume a normal distribution, the 99% VaR can be calculated using the 99% quantile (type in qnorm(0.99)
) of the standard normal distribution. The one-month 99% VaR estimate for the next period is hence qnorm(0.99)*0.1168 = 0.2717
. Hence, with 99% probability the monthly return is above -27%.