In [1]:
import numpy as np
import numpy.random as npr
import matplotlib.pyplot as plt

import pandas as pd
from pandas import read_csv

from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
from statsmodels.tsa.stattools import acf, pacf
from statsmodels.tsa.arima_model import ARMA
from statsmodels.stats.diagnostic import acorr_ljungbox 

EXERCICE 1

Question 1

"CH" est l'acronyme de "Conditional Heteroskedasticity", qui est une façon sophistiquée de dire que la variance conditionnelle $\text{Var}[X_t|X_{t-1},X_{t-2},\ldots]$ dépend du temps $t$. Si $(X_t)$ est un GARCH alors il est stationnaire et en particuler sa variance (tout court) ne dépend pas du temps. En vertu de la Proposition 6.2 du cours, $(X_t)$ est un bruit blanc, et donc son ACF est celle d'un bruit blanc : $\rho(h)=\boldsymbol 1_{h=0}$. Ce n'est pas un bruit blanc fort car la série $(X_t^2)$ n'est pas un bruit blanc (même faible).

Question 2

On peut générer une réalisation $x_1,\ldots,x_n$ d'un processus ARCH(1), $X_t=\epsilon_t\sigma_t$, de paramètres $\omega,\alpha$ :

  • en simulant $\epsilon_1,\ldots,\epsilon_n$ i.i.d $N(0,1)$ gaussien
  • en calculant $\sigma_1^2,\ldots,\sigma_{n}^2$ et $x_1,\ldots,x_{n}$ récursivement : on part de $\sigma_0:=0$ (et donc $x_0=0$) et puis pour tout $t\geq 0$,
\begin{align*} \sigma_{t+1}^2 & = \omega + \alpha \,x_t^2 \\ x_{t+1} & = \epsilon_{t+1} \, \sqrt{\sigma_{t+1}^2} \end{align*}
  • après, comme on l'a fait pour l'AR(1) dans le TP1, il vaut mieux simuler une trajectoire plus longue et supprimer une partie pour faire disparaître la dépendance en la condition initiale (arbitraire) $\sigma_0=0$. Par exemple, on peut simuler une trajectoire $x_1,\ldots,x_{2n}$ de longueur $2n$ et ne garder que les $n$ dernières valeurs $x_{n+1},\ldots,x_{2n}$.
In [2]:
def ARCH1(omega,alpha,n):
    eps = npr.randn(2*n)
    sigma2 = 0
    ARCH = np.zeros(2*n)
    
    for i in range(2*n-1):
        sigma2 = omega + alpha * ARCH[i]**2
        ARCH[i+1] = eps[i] * np.sqrt(sigma2)
        
    return(ARCH[n:2*n])

Question 3

Remarque préliminaire : Comme on l'a vu dans le cours, la condition $\mathbb E[X_t^4]<\infty$ pour un ARCH(1) requiert que $0<\alpha<1/\sqrt{3}$ quand $(\epsilon_t)\sim \text{BB}(0,1)$ gaussien. En fait cette condition aide aussi à ce que la simulation de la question 2 soit représentative d'un ARCH(1) théorique. Ce qui suit dépendra donc de votre choix du paramètre $\alpha$, sachant que :

In [3]:
1/np.sqrt(3)
Out[3]:
0.5773502691896258
In [4]:
Xt = ARCH1(1,0.2,1000)
In [5]:
plt.plot(Xt)
plot_acf(Xt)
plt.show()
In [6]:
acorr_ljungbox(Xt,lags=range(60),boxpierce=True)[3] < 0.05
/Users/adrien/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in less
  """Entry point for launching an IPython kernel.
Out[6]:
array([False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False])

On peut raisonnablement supposer que la série $X_t$ est un bruit blanc. Etudions maintenant $X_t^2$.

In [7]:
plt.plot(Xt**2)
plot_acf(Xt**2)
plot_pacf(Xt**2)
plt.show()
In [8]:
acorr_ljungbox(Xt**2,lags=range(60),boxpierce=True)[3] < 0.05
/Users/adrien/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in less
  """Entry point for launching an IPython kernel.
Out[8]:
array([False,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True])

L'hypothèse que $X_t^2$ soit un bruit blanc est clairement rejetée. De plus, la PACF laisse penser que $X_t^2$ est modélisable par un AR(1) avec un terme constant, ce qu'on vérifie (notez qu'on n'a pas rajouté trend='nc' comme on l'a fait jusqu'à présent) :

In [9]:
model = ARMA(Xt**2,order=(1,0)).fit()
acorr_ljungbox(model.resid,lags=range(60),boxpierce=True)[3] < 0.05
/Users/adrien/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:2: RuntimeWarning: invalid value encountered in less
  
Out[9]:
array([False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False])

NB : On peut retrouver une approximation des coefficients $\omega,\alpha$ de l'ARCH(1) dans la modélisation AR(1) (pourquoi ?)

In [10]:
model.summary()
Out[10]:
ARMA Model Results
Dep. Variable: y No. Observations: 1000
Model: ARMA(1, 0) Log Likelihood -1943.964
Method: css-mle S.D. of innovations 1.690
Date: Mon, 30 Mar 2020 AIC 3893.928
Time: 17:55:04 BIC 3908.651
Sample: 0 HQIC 3899.524
coef std err z P>|z| [0.025 0.975]
const 1.2287 0.067 18.300 0.000 1.097 1.360
ar.L1.y 0.2040 0.031 6.590 0.000 0.143 0.265
Roots
Real Imaginary Modulus Frequency
AR.1 4.9015 +0.0000j 4.9015 0.0000

Notez qu'on peut isoler ces coefficients en utilisant : model.params

Comme $\sigma_{n+1}^2 = \omega + \alpha \, X_n^2$, on obtient l'estimateur de $\sigma_{1001}$ (imaginons un instant qu'on ne connaisse pas $\alpha,\omega$) :

In [11]:
np.sqrt(model.params[0] + model.params[1]*Xt[-1])
Out[11]:
1.1777001629209087

EXERCICE 2

Question 1

On étudie la série INTC, des log-rendements mensuels des actions d’INTEL de janvier 1973 à décembre 2003, disponible disponible sur ma page web.

In [12]:
INTC = read_csv('INTC.csv').x
In [13]:
INTC.plot()
plt.show()
In [14]:
plot_acf(INTC)
plot_pacf(INTC)
plt.show()
In [15]:
acorr_ljungbox(INTC,lags=range(60),boxpierce=True)[3] < 0.05
/Users/adrien/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in less
  """Entry point for launching an IPython kernel.
Out[15]:
array([False, False, False, False, False, False, False, False, False,
       False, False, False, False, False,  True,  True, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False])

Conclusion : On peut raisonnablement supposer que la série INTC est un bruit blanc.

Question 2

On étudie maintenant la série INTC$^2$

In [16]:
INTC2 = INTC**2
In [17]:
INTC2.plot()
plot_acf(INTC2)
plot_pacf(INTC2)
plt.show()
In [18]:
acorr_ljungbox(INTC2,lags=range(60),boxpierce=True)[3] < 0.05
/Users/adrien/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in less
  """Entry point for launching an IPython kernel.
Out[18]:
array([False,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True])

L'hypothèse d'un bruit blanc pour INTC$^2$ est clairement rejetée.

Question 3

Essayons de modéliser INTC à l'aide d'un GARCH, c'est-à-dire de modéliser INTC2 avec un ARMA. D'après la PACF de INTC2 un AR(1) ne semble pas approprié pour modéliser cette série (pas de pic isolé en $h=1$), donc ARCH(1) ne semble pas approprié pour INTC (car on fait l'hypothèse que cette série à un moment d'ordre 4). On teste quelques modèles ARMA(p,q) avec petits $p,q$ :

In [19]:
model_20 = ARMA(INTC2,order=(2,0)).fit()
model_30 = ARMA(INTC2,order=(2,0)).fit()
model_40 = ARMA(INTC2,order=(2,0)).fit()
model_11 = ARMA(INTC2,order=(1,1)).fit()
model_21 = ARMA(INTC2,order=(2,1)).fit()
In [20]:
model_20.aic, model_30.aic, model_40.aic, model_11.aic, model_21.aic
Out[20]:
(-1376.9491853911015,
 -1376.9491853911015,
 -1376.9491853911015,
 -1387.4961320742343,
 -1386.9322676863078)
In [21]:
model_20.bic, model_30.bic, model_40.bic, model_11.bic, model_21.bic
Out[21]:
(-1361.2736099740089,
 -1361.2736099740089,
 -1361.2736099740089,
 -1371.8205566571417,
 -1367.337798414942)

Le modèle de plus petit critère AIC (et BIC) est l'ARMA(1,1), dont on étudie les résidus maintenant.

In [22]:
residus = model_11.resid
In [23]:
acorr_ljungbox(residus,lags=range(60),boxpierce=True)[3] < 0.05
/Users/adrien/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in less
  """Entry point for launching an IPython kernel.
Out[23]:
array([False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False, False, False, False,
       False, False, False, False, False, False])

Conclusion : Les résidus ne semblent pas corrélés et le modèle GARCH(1,1) semble donc approprié pour INTC.

Question 4

  • Si on note $(X_t)$ la série théorique de INTC, alors sous l'hypothèse du modèle GARCH(1,1) sa volatilité $\sigma_t$ satisfait : $$ \sigma_t^2 = \omega + \alpha X_{t-1}^2 +\beta \sigma_{t-1}^2 $$ pour des paramètres $\omega,\alpha,\beta >0$ et $\alpha+\beta<1$.
  • En faisant de plus l'hypothèse que $\mathbb E[X_t^4]<\infty$, on sait que $(X_t^2)$ est un ARMA(1,1) avec une constante additive dont les paramètres sont en lien avec $\omega,\alpha,\beta$ :
    $$ X_t^2 = \omega + \varphi X_{t-1}^2+\theta\eta_{t-1} $$ où $(\eta_t)\sim BB(0,\eta^2)$ et $$\varphi=\alpha+\beta,\qquad \theta=-\beta.$$
  • On trouve les constantes $\omega,\varphi,\theta$ à l'aide de la modélisation faite en Question 3 : $\omega=0.018229$, $\varphi=0.912309$ et $\theta=-0.797182$
In [24]:
model_11.params 
Out[24]:
const      0.018229
ar.L1.x    0.912309
ma.L1.x   -0.797182
dtype: float64

On obtien alors $\alpha = \varphi+\theta$, $\beta = - \theta$ et et on vérifie que ces paramètres satisfont les contraintes.

In [25]:
omega = model_11.params[0]
alpha = model_11.params[1] + model_11.params[2]
beta = - model_11.params[2]
In [26]:
omega>0, alpha>0, beta>0, alpha+beta<1
Out[26]:
(True, True, True, True)

En prenant la condition iniale $\hat\sigma_0:=0$ (c'est arbitraire), on obtient un estimateur $\hat\sigma_n$ de la volatilité $\sigma_n$ en calculant récursivement pour $n\geq 0$, $$ \hat\sigma_{n+1}^2 := \omega+ \alpha \,x[n]^2 + \beta \,\hat\sigma_{n}^2 $$ et en prenant la racine carrée.

In [27]:
def Est_volatilité(x,n):
    est_sigma_2 = np.zeros(n+1)
    for i in range(n):
        est_sigma_2[i+1] = omega + alpha * x[i]**2 + beta * est_sigma_2[i]
    return(np.sqrt(est_sigma_2))

Dessinons un graphe de la volatilité ainsi estimée superposé à INTC.

In [28]:
N = len(INTC)
Vol = Est_volatilité(INTC,N)
plt.plot(Vol,linewidth=2)
plt.plot(INTC,linewidth=0.5)
plt.show()
  • On observe que les pics de volatilités se superposent aux endroits où INTC fluctue le plus.
  • On observe également des "clusters" de volatilité (les pics raprochés).
  • On pourrait changer la condition initiale $\hat\sigma_0=0$ en une autre sans trop changer le modèle à moyen terme (essayez !).

REMARQUE : Concernant l'hypothèse de modélisation $\mathbb E[X_t^4]<\infty$ que nous avons faite ici et là, il n'y a pas de façon propre de tester cette hypothèse. On peut néanmoins se convaincre qu'elle est raisonnable ici car, si $X_t$ est un GARCH(1,1), en particulier elle est stationnaire et on peut prendre $\frac1N\sum_{t=1}^N X_t^4$ comme estimateur de $\mathbb E[X_t^4]$. On vérifie ensuite que ce cette quantité n'est pas "trop grande" ; pour l'ordre de grandeur, on peut se demander si $\frac1N\sum_{t=1}^N X_t^4$ n'est pas beaucoup plus grand que $\frac1N\sum_{t=1}^N X_t^2\simeq \text{Var}[X_t].$ Attention : ce n'est en rien une preuve, c'est juste un critère qualitatif.

In [29]:
np.mean(INTC**4), np.mean(INTC**2)
Out[29]:
(0.0018157738074094337, 0.01816142032593223)

Complément : Testez ce critère dans l'Exercice 1, que l'on essaiera sur plusieurs réalisations d'un ARCH(1) suivant si $\alpha<1/\sqrt{3}$ ou non.