Modélisation GARCH de la volatilité
Mise en œuvre sur R
- Présentation du cours
- Mandelbrot et les faits stylisés des rentabilités
- Regroupement de la volatilité et effet de levier
- Le cadre de la variance conditionnelle
- Le modèle ARCH(q) d'Engle
- Propriétés statistiques de l'ARCH
- Détecter et identifier un effet ARCH
- Le modèle GARCH(p,q) de Bollerslev
- Stationnarité, persistance et variance de long terme
- La dualité GARCH et ARMA sur les carrés
- La loi des innovations
- L'effet de levier et l'EGARCH de Nelson
- GJR-GARCH, TGARCH et la courbe d'impact
- IGARCH, GARCH-M et la mémoire longue
- L'estimation par quasi-maximum de vraisemblance
- La validation d'un modèle de volatilité
- La prévision de volatilité
- De la volatilité à la Value at Risk
- Mise en œuvre sur R
- Mise en œuvre sur Python
- Étude de cas sur données réelles — R
- Étude de cas sur données réelles — Python
Modélisation GARCH de la volatilité
Chapitre 17 : Mise en œuvre sur R
Avant de commencer
Le jeu de données
2 500 rentabilités quotidiennes simulées, livrées avec le cours. Le mécanisme générateur est un GJR-GARCH à innovations de Student : aucune mémoire en niveau, forte persistance de la volatilité, effet de levier.
Parce qu’on connaît la réponse. On peut vérifier que la démarche retrouve la persistance, l’asymétrie et l’épaisseur des queues qu’on y a mises — et rien d’autre. Sur données réelles, cette vérification est impossible.
Les bibliothèques
install.packages(c("rugarch", "FinTS", "moments", "tseries"))
library(rugarch) # ugarchspec, ugarchfit, ugarchforecast
library(FinTS) # ArchTest
library(moments) # skewness, kurtosisÉtape 1 — Décrire et détecter
Charger et décrire
don <- read.csv("rentabilites.csv")
r <- don$rentabilite
T <- length(r) # 2500
c(moyenne = mean(r), ecart_type = sd(r),
asymetrie = skewness(r), kurtosis = kurtosis(r)) moyenne ecart_type asymetrie kurtosis
0.0200 1.1060 -0.2900 6.0500
Kurtosis de 6{,}05 contre 3 : queues épaisses. Asymétrie négative : les baisses sont plus marquées. Écart-type de 1{,}1\,\% par jour, soit 17{,}5\,\% annualisé.
L’équation de moyenne, d’abord
Box.test(r, lag = 20, type = "Ljung-Box")X-squared = 17.033, df = 20, p-value = 0.6516
Non rejeté : aucune autocorrélation. L’équation de moyenne se réduit à une constante, exactement comme le cours ARMA l’annonçait.
On peut passer à la variance — et seulement parce que cette étape a été faite.
Détecter l’effet ARCH
Box.test(r^2, lag = 20, type = "Ljung-Box")
ArchTest(r, lags = 12)X-squared = 968.13, df = 20, p-value < 2.2e-16
ARCH LM-test
Chi-squared = 274.53, df = 12, p-value < 2.2e-16
Q_{\mathrm{LB}}(20) vaut 17{,}0 sur r_t et 968{,}1 sur r_t^2. Non corrélées, mais très loin d’être indépendantes.
Étape 2 — Estimer
Le GARCH(1,1) gaussien
spec <- ugarchspec(
variance.model = list(model = "sGARCH", garchOrder = c(1, 1)),
mean.model = list(armaOrder = c(0, 0), include.mean = TRUE),
distribution.model = "norm")
g_norm <- ugarchfit(spec, data = r) Estimate Std. Error t value Pr(>|t|)
mu 0.057500 0.019800 2.904 0.003683
omega 0.020500 0.005100 4.020 0.000058
alpha1 0.100000 0.014200 7.042 0.000000
beta1 0.887300 0.015900 55.805 0.000000
LogLikelihood : -3543.2 Bayes (BIC) : 7117.8
Le GARCH(1,1) gaussien (suite)
Persistance \hat\pi = 0{,}100+0{,}887 = 0{,}987. Demi-vie \ln 0{,}5/\ln 0{,}987 = 53 jours.
Variance de long terme 0{,}0205/(1-0{,}987) = 1{,}58, soit une volatilité de 1{,}26\,\% par jour.
Tous les coefficients sont massivement significatifs — c’est la règle sur ce type de données.
Passer à la loi de Student
spec_t <- ugarchspec(
variance.model = list(model = "sGARCH", garchOrder = c(1, 1)),
mean.model = list(armaOrder = c(0, 0), include.mean = TRUE),
distribution.model = "std") # std = Student
g_t <- ugarchfit(spec_t, data = r) Estimate Std. Error t value
mu 0.044400 0.018900 2.349
omega 0.019500 0.006300 3.095
alpha1 0.099500 0.017100 5.819
beta1 0.889900 0.018200 48.896
shape 6.460000 0.812000 7.956
LogLikelihood : -3491.0 Bayes (BIC) : 7021.1
Le BIC passe de 7\,117{,}8 à 7\,021{,}1 : 97 points pour un paramètre. Le seuil usuel d’un écart significatif est de 10.
shape = \hat\nu = 6{,}46 : queues nettement plus épaisses que la normale.
Tester l’asymétrie
signbias(g_t) t-value prob sig
Sign Bias 2.8140 0.0049 ***
Negative Sign Bias 3.1620 0.0016 ***
Joint Effect 18.4300 0.0004 ***
Rejet net : il reste de l’asymétrie que le GARCH symétrique ne capte pas. Il faut un GJR.
Le GJR-GARCH
spec_gjr <- ugarchspec(
variance.model = list(model = "gjrGARCH", garchOrder = c(1, 1)),
mean.model = list(armaOrder = c(0, 0), include.mean = TRUE),
distribution.model = "std")
g_gjr <- ugarchfit(spec_gjr, data = r) Estimate Std. Error t value Pr(>|t|)
mu 0.030700 0.018600 1.651 0.098800
omega 0.023200 0.006600 3.515 0.000440
alpha1 0.043400 0.015300 2.837 0.004556
beta1 0.886200 0.018400 48.163 0.000000
gamma1 0.115700 0.028100 4.117 0.000038
shape 6.620000 0.851000 7.779 0.000000
LogLikelihood : -3476.8 Bayes (BIC) : 7000.5
Le GJR-GARCH (suite)
\hat\gamma = 0{,}116, significatif à 4{,}1 écarts-types. Un choc négatif pèse 0{,}043+0{,}116 = 0{,}159 contre 0{,}043 pour un choc positif : près de quatre fois plus.
Persistance : 0{,}043 + 0{,}116/2 + 0{,}886 = 0{,}987.
Étape 3 — Valider
Les résidus standardisés
z <- residuals(g_gjr, standardize = TRUE)
Box.test(z, lag = 20, type = "Ljung-Box")
Box.test(z^2, lag = 20, type = "Ljung-Box")z : X-squared = 9.72, df = 20, p-value = 0.9738
z^2 : X-squared = 27.10, df = 20, p-value = 0.1320
Les résidus standardisés (suite)
Les résidus standardisés (suite 2)
Q_{\mathrm{LB}}(20) sur les carrés passe de 968 à 27. Le modèle a absorbé la totalité de la dépendance de la variance.
Le diagramme quantile-quantile
plot(g_gjr, which = 9) # QQ-plot des résidus standardisésContre la normale, les points décrochent aux deux extrémités. Contre la Student estimée, ils suivent la droite jusque dans les queues : la loi choisie convient.
Comparer les trois modèles
sapply(list(g_norm, g_t, g_gjr), infocriteria)[2, ] # ligne 2 = BICGARCH-norm GARCH-t GJR-t
7117.8 7021.1 7000.5
Étape 4 — Prévoir le risque
La prévision de volatilité
p <- ugarchforecast(g_gjr, n.ahead = 60)
head(sigma(p), 3)
tail(sigma(p), 1)[1] 0.7412 0.7534 0.7651
[60] 1.1893
La volatilité prévue part de 0{,}74\,\% — le marché est calme en fin d’échantillon — et remonte lentement vers 1{,}19\,\% à soixante jours.
C’est exactement la courbe du chapitre 15 : convergence par le bas, à la vitesse \pi = 0{,}987.
La VaR conditionnelle par simulation filtrée
q1 <- quantile(z, 0.01) # quantile empirique des résidus
var_c <- coef(g_gjr)["mu"] + sigma(g_gjr) * q1
var_u <- quantile(r, 0.01) # seuil fixe, pour comparaison
c(depassements_conditionnelle = sum(r < var_c),
depassements_inconditionnelle = sum(r < var_u))depassements_conditionnelle depassements_inconditionnelle
25 25
La VaR conditionnelle par simulation filtrée (suite)
Le backtesting
VaRTest(alpha = 0.01, actual = r, VaR = var_c)$uc.LRstat # Kupiec
VaRTest(alpha = 0.01, actual = r, VaR = var_c)$cc.LRstat # Christoffersen| Mesure | Dépassements | \mathrm{LR}_{uc} | \mathrm{LR}_{ind} | Verdict |
|---|---|---|---|---|
| conditionnelle | 25 | 0{,}00 | 1{,}38 | acceptée |
| inconditionnelle | 25 | 0{,}00 | 16{,}22 | rejetée |
Le seuil fixe a exactement le bon nombre de dépassements — et les groupe en grappes, dans les périodes de crise. C’est ce que le test d’indépendance détecte, et c’est ce que le GARCH corrige.
Synthèse
Ce qu’il faut retenir
Box.testsur r puis sur r^2 : 17{,}0 contre 968{,}1. Non corrélées, non indépendantes.ugarchspec+ugarchfit: trois modèles emboîtés, à comparer par le BIC.- Loi de Student : -97 points de BIC. Asymétrie GJR : -21 de plus.
signbiasteste l’effet de levier ; ici rejet net, \hat\gamma = 0{,}116.- Validation : Q_{\mathrm{LB}}(20) sur z^2 passe de 968 à 27.
ugarchforecastdonne le retour à la moyenne ;VaRTestfait le backtesting.
La même série, la même démarche, les mêmes chiffres — en Python. Non par redondance, mais pour vérifier que la méthode ne doit rien à l’outil.
Ressources du chapitre
- Diapositives
- Polycopié du chapitre
- Exercices corrigés
- Application interactive
- — Vidéo
- QCM (bientôt)