Usage

Usage

We will be using the data from Bollerslev and Ghysels (1986), available as the constant BG96. The data consist of daily German mark/British pound exchange rates (1974 observations) and are often used in evaluating implementations of (G)ARCH models (see, e.g., Brooks et.al. (2001). We begin by convincing ourselves that the data exhibit ARCH effects; a quick and dirty way of doing this is to look at the sample autocorrelation function of the squared returns:

julia> using ARCHModels

julia> autocor(BG96.^2, 1:10, demean=true) # re-exported from StatsBase
10-element Array{Float64,1}:
 0.22294073831639766
 0.17663183540117078
 0.14086005904595456
 0.1263198344036979
 0.18922204038617135
 0.09068404029331875
 0.08465365332525085
 0.09671690899919724
 0.09217329577285414
 0.11984168975215709

Using a critical value of $1.96/\sqrt{1974}=0.044$, we see that there is indeed significant autocorrelation in the squared series.

A more formal test for the presence of volatility clustering is Engle's (1982) ARCH-LM test. The test statistic is given by $LM\equiv TR^2_{aux}$, where $R^2_{aux}$ is the coefficient of determination in a regression of the squared returns on an intercept and $p$ of their own lags. The test statistic follows a $\chi^2_p$ distribution under the null of no volatility clustering.

julia> ARCHLMTest(BG96, 1)
ARCH LM test for conditional heteroskedasticity
-----------------------------------------------
Population details:
    parameter of interest:   T⋅R² in auxiliary regression of rₜ² on an intercept and its own lags
    value under h_0:         0
    point estimate:          98.12107516935244

Test summary:
    outcome with 95% confidence: reject h_0
    p-value:                     <1e-22

Details:
    sample size:                    1974
    number of lags:                 1
    LM statistic:                   98.12107516935244

The null is strongly rejected, again providing evidence for the presence of volatility clustering.

Estimation

Having established the presence of volatility clustering, we can begin by fitting the workhorse model of volatility modeling, a GARCH(1, 1) with standard normal errors; for other model classes such as EGARCH, see the section on volatility specifications.

julia> fit(GARCH{1, 1}, BG96)

TGARCH{0,1,1} model with Gaussian errors, T=1974.


Mean equation parameters:

        Estimate  Std.Error   z value Pr(>|z|)
μ    -0.00616637 0.00920163 -0.670139   0.5028

Volatility parameters:

      Estimate  Std.Error z value Pr(>|z|)
ω    0.0107606 0.00649493 1.65677   0.0976
β₁    0.805875  0.0725003 11.1155   <1e-27
α₁    0.153411  0.0536586 2.85903   0.0042

This returns an instance of UnivariateARCHModel, as described in the section Working with UnivariateARCHModels. The parameters $\alpha_1$ and $\beta_1$ in the volatility equation are highly significant, again confirming the presence of volatility clustering. Note also that the fitted values are the same as those found by Bollerslev and Ghysels (1986) and Brooks et.al. (2001) for the same dataset.

The fit method supports a number of keyword arguments; the full signature is

fit(::Type{<:VolatilitySpec}, data::Vector; dist=StdNormal, meanspec=Intercept, algorithm=BFGS(), autodiff=:forward, kwargs...)

Their meaning is as follows:

The remaining keyword arguments are passed on to the optimizer.

As an example, an EGARCH(1, 1, 1) model without intercept and with Student's $t$ errors is fitted as follows:

julia> fit(EGARCH{1, 1, 1}, BG96; meanspec=NoIntercept, dist=StdT)

EGARCH{1,1,1} model with Student's t errors, T=1974.


Volatility parameters:

       Estimate Std.Error   z value Pr(>|z|)
ω    -0.0162014 0.0186806 -0.867286   0.3858
γ₁   -0.0378454  0.018024  -2.09972   0.0358
β₁     0.977687  0.012558   77.8538   <1e-99
α₁     0.255804 0.0625497   4.08961    <1e-4

Distribution parameters:

     Estimate Std.Error z value Pr(>|z|)
ν     4.12423   0.40059 10.2954   <1e-24

An alternative approach to fitting a VolatilitySpec to BG96 is to first construct a UnivariateARCHModel containing the data, and then using fit! to modify it in place:

julia> spec = GARCH{1, 1}([1., 0., 0.]);

julia> am = UnivariateARCHModel(spec, BG96)

TGARCH{0, 1,1} model with Gaussian errors, T=1974.


                             ω  β₁  α₁
Volatility parameters:     1.0 0.0 0.0



julia> fit!(am)

TGARCH{0, 1,1} model with Gaussian errors, T=1974.


Volatility parameters:

      Estimate  Std.Error z value Pr(>|z|)
ω    0.0108661 0.00657449 1.65277   0.0984
β₁    0.804431  0.0730395 11.0136   <1e-27
α₁    0.154597  0.0539319 2.86651   0.0042

Note that fit! will also modify the volatility (and mean and distribution) specifications:

julia> spec
TGARCH{0,1,1} specification.

                     ω       β₁       α₁
Parameters:  0.0108661 0.804431 0.154597

Calling fit(am) will return a new instance of UnivariateARCHModel instead:

julia> am2 = fit(am);

julia> am2 === am
false

julia> am2.spec.coefs == am.spec.coefs
true

Model selection

The function selectmodel can be used for automatic model selection, based on an information crititerion. Given a class of model (i.e., a subtype of VolatilitySpec), it will return a fitted UnivariateARCHModel, with the lag length parameters (i.e., $p$ and $q$ in the case of GARCH) chosen to minimize the desired criterion. The BIC is used by default.

Eg., the following selects the optimal (minimum AIC) EGARCH(o, p, q) model, where o, p, q < 2, assuming $t$ distributed errors.

julia> selectmodel(EGARCH, BG96; criterion=aic, maxlags=2, dist=StdT)

EGARCH{1,1,2} model with Student's t errors, T=1974.


Mean equation parameters:

       Estimate  Std.Error  z value Pr(>|z|)
μ    0.00196126 0.00695292 0.282077   0.7779

Volatility parameters:

       Estimate Std.Error   z value Pr(>|z|)
ω    -0.0031274 0.0112456 -0.278101   0.7809
γ₁   -0.0307681 0.0160754  -1.91398   0.0556
β₁     0.989056 0.0073654   134.284   <1e-99
α₁     0.421644 0.0678139   6.21767    <1e-9
α₂    -0.229068 0.0755326   -3.0327   0.0024

Distribution parameters:

     Estimate Std.Error z value Pr(>|z|)
ν     4.18795  0.418697 10.0023   <1e-22

Passing the keyword argument show_trace=true will show the criterion for each model after it is estimated.

Any unspecified lag length parameters in the mean specification (e.g., $p$ and $q$ for ARMA) will be optimized over as well:

julia> am = selectmodel(ARCH, BG96;  meanspec=AR, maxlags=2)

TGARCH{0,0,2} model with Gaussian errors, T=1974.


Mean equation parameters:

        Estimate  Std.Error  z value Pr(>|z|)
c    -0.00685701 0.00966961 -0.70913   0.4782
φ₁     0.0358363  0.0334292    1.072   0.2837

Volatility parameters:

     Estimate  Std.Error z value Pr(>|z|)
ω    0.119163 0.00995107 11.9749   <1e-32
α₁   0.315686  0.0576413 5.47674    <1e-7
α₂   0.183318  0.0444875 4.12067    <1e-4

Here, an ARCH(2)-AR(1) model was selected. Note that this can result in an explosion of the number of models that must be estimated; e.g., selecting the best model from the class of TGARCH{o, p, q}-ARMA{p, q} models results in $5^\mathbf{maxlags}$ models being estimated. It may be preferable to fix the lag length of the mean specification: am = selectmodel(ARCH, BG96; meanspec=AR{1}) considers only ARCH(q)-AR(1) models. Similarly, one may restrict the lag length of the volatility specification and select only among different mean specifications. E.g., the following will select the best ARMA{p, q} specification with constant variance:

julia> am = selectmodel(ARCH{0}, BG96;  meanspec=ARMA)

TGARCH{0,0,0} model with Gaussian errors, T=1974.


Mean equation parameters:

       Estimate Std.Error  z value Pr(>|z|)
c    -0.0266446 0.0174716 -1.52502   0.1273
φ₁    -0.621837  0.160738 -3.86864   0.0001
θ₁     0.643588    0.1543  4.17103    <1e-4

Volatility parameters:

     Estimate Std.Error z value Pr(>|z|)
ω    0.220848 0.0118061 18.7063   <1e-77

In this case, an ARMA(1, 1) specification was selected.

Risk measures

One of the primary uses of ARCH models is for estimating and forecasting risk measures, such as Value at Risk and Expected Shortfall. This section details the relevant functionality provided in this package.

Basic in-sample estimates for the Value at Risk implied by an estimated UnivariateARCHModel can be obtained using VaRs:

julia> am = fit(GARCH{1, 1}, BG96);

julia> vars = VaRs(am, 0.05);

julia> using Plots

julia> plot(-BG96, legend=:none, xlabel="\$t\$", ylabel="\$-r_t\$");

julia> plot!(vars, color=:purple);

VaR Plot

Forecasting

The predict(am::UnivariateARCHModel) method can be used to construct one-step ahead forecasts for a number of quantities. Its signature is

    predict(am::UnivariateARCHModel, what=:volatility; level=0.01)

The keyword argument what controls which object is predicted; the choices are :volatility (the default), :variance, :return, and :VaR. The VaR level can be controlled with the keyword argument level.

One way to use predict is in a backtesting exercise. The following code snippet constructs out-of-sample VaR forecasts for the BG96 data by re-estimating the model in a rolling window fashion, and then tests the correctness of the VaR specification with DQTest.

T = length(BG96);
windowsize = 1000;
vars = similar(BG96);
for t = windowsize+1:T-1
    m = fit(GARCH{1, 1}, BG96[t-windowsize:t]);
    vars[t+1] = predict(m, :VaR; level=0.05);
end
DQTest(BG96[windowsize+1:end], vars[windowsize+1:end], 0.05)

# output
Engle and Manganelli's (2004) DQ test (out of sample)
-----------------------------------------------------
Population details:
    parameter of interest:   Wald statistic in auxiliary regression
    value under h_0:         0
    point estimate:          2.5272613188161177

Test summary:
    outcome with 95% confidence: fail to reject h_0
    p-value:                     0.4704

Details:
    sample size:                    974
    number of lags:                 1
    VaR level:                      0.05
    DQ statistic:                   2.5272613188161177

Model diagnostics and specification tests

Testing volatility models in general relies on the estimated conditional volatilities $\hat{\sigma}_t$ and the standardized residuals $\hat{z}_t\equiv (r_t-\hat{\mu}_t)/\hat{\sigma}_t$, accessible via volatilities(::UnivariateARCHModel) and residuals(::UnivariateARCHModel), respectively. The non-standardized residuals $\hat{u}_t\equiv r_t-\hat{\mu}_t$ can be obtained by passing standardized=false as a keyword argument to residuals.

One possibility to test a volatility specification is to apply the ARCH-LM test to the standardized residuals. This is achieved by calling ARCHLMTest on the estimated UnivariateARCHModel:

julia> am = fit(GARCH{1, 1}, BG96);

julia> ARCHLMTest(am, 4) # 4 lags in test regression.
ARCH LM test for conditional heteroskedasticity
-----------------------------------------------
Population details:
    parameter of interest:   T⋅R² in auxiliary regression of rₜ² on an intercept and its own lags
    value under h_0:         0
    point estimate:          4.211230445141555

Test summary:
    outcome with 95% confidence: fail to reject h_0
    p-value:                     0.3782

Details:
    sample size:                    1974
    number of lags:                 4
    LM statistic:                   4.211230445141555

By default, the number of lags is chosen as the maximum order of the volatility specification (e.g., $\max(p, q)$ for a GARCH(p, q) model). Here, the test does not reject, indicating that a GARCH(1, 1) specification is sufficient for modelling the volatility clustering (a common finding).

Simulation

To simulate from a UnivariateARCHModel, use simulate. You can either specify the VolatilitySpec (and optionally the distribution and mean specification) and desired number of observations, or pass an existing UnivariateARCHModel. Use simulate! to modify the data in place.

julia> am3 = simulate(GARCH{1, 1}([1., .9, .05]), 1000; warmup=500, meanspec=Intercept(5.), dist=StdT(3.))

TGARCH{0,1,1} model with Student's t errors, T=1000.


                             μ
Mean equation parameters:  5.0

                             ω  β₁   α₁
Volatility parameters:     1.0 0.9 0.05

                             ν
Distribution parameters:   3.0

julia> am4 = simulate(am3, 1000); # passing the number of observations is optional, the default being nobs(am3)