Salem Nkunda Nyisingize

La cointégration : deux marches aléatoires qui ne peuvent pas se perdre de vue

Pourquoi une régression entre deux séries non stationnaires ment presque toujours — et dans quel cas précis elle dit la vérité. Simulateurs interactifs, test d'Engle–Granger, modèle à correction d'erreur, et code Python exécutable directement dans cette page.

Pourquoi c'est important

Deux courbes montent ensemble depuis dix ans. Le prix d'une matière première et l'indice d'un secteur, la consommation d'électricité et la température moyenne, deux actions du même marché. La tentation est irrésistible : il y a forcément un lien, et on peut parier dessus. Une régression le confirme, avec un \(R^2\) flatteur et un coefficient très significatif.

Sauf que ce raisonnement est faux à peu près trois fois sur quatre. Entre deux séries qui dérivent librement — deux marches aléatoires sans le moindre rapport — la statistique de Student dépasse le seuil de significativité dans plus de 80 % des cas au lieu des 5 % annoncés. Et ça empire quand on allonge l'échantillon : plus vous avez de données, plus la fausse relation paraît solide. C'est le résultat qui a valu à Granger le prix Nobel, et c'est probablement l'erreur statistique la plus coûteuse jamais commise en finance.

La cointégration est la réponse : le critère exact qui sépare les vraies relations des mirages. Elle dit qu'il existe une combinaison des deux séries qui, elle, ne dérive pas — un écart qui revient toujours vers sa moyenne, comme un chien tenu en laisse par son promeneur. Si cette laisse existe, on peut parier sur le retour à l'équilibre. Sinon, on parie sur rien.

Dans cette page, vous allez tirer vous-même des paires de séries indépendantes et regarder des « relations » convaincantes apparaître sous vos yeux. Puis vous couperez la laisse d'une paire réellement liée, en poussant un curseur, et vous verrez le verdict du test basculer en direct. Le code qui fait tourner tout ça est là, modifiable, exécutable.

Racine unitaire Engle–Granger VECM Johansen Régression fallacieuse

Le problème : la régression fallacieuse

Prenons deux séries qui n'ont rien à voir l'une avec l'autre. Deux marches aléatoires pures, engendrées par deux générateurs indépendants :

Deux marches aléatoires indépendantes
\[X_{t1} = X_{t-1,1} + u_t, \qquad X_{t2} = X_{t-1,2} + v_t, \qquad \operatorname{Cov}(u_t, v_s) = 0 \;\;\forall t,s\]

Régressons la première sur la seconde. La théorie des moindres carrés dit que la pente devrait être nulle, le \(R^2\) nul, la statistique de Student petite. C'est faux. Granger et Newbold (1974) l'ont montré par simulation, Phillips (1986) l'a démontré : sous racine unitaire, la statistique \(t\) diverge en \(\sqrt{n}\) au lieu de converger vers une loi normale. Plus l'échantillon est long, plus la fausse relation paraît significative.

Le simulateur ci-dessous tire deux marches aléatoires indépendantes et régresse l'une sur l'autre. Relancez-le : vous verrez apparaître des « relations » convaincantes. Puis lancez l'expérience de Monte-Carlo pour mesurer le taux de rejet réel d'un test dont le niveau nominal est 5 %.

Simulateur 1 — deux marches aléatoires strictement indépendantes

Aucun lien n'existe entre les deux séries. La régression devrait ne rien trouver.

200
Ce qu'il faut retenir. Le test de Student n'est pas « un peu » trop permissif : son taux de rejet réel dépasse 70 % au lieu de 5 %, et il empire quand \(n\) augmente. Toute régression entre séries en niveau non stationnaires est suspecte tant qu'on n'a pas vérifié la cointégration. Le symptôme classique dans un rapport : \(R^2\) très élevé accompagné d'un Durbin–Watson proche de zéro.

Ordre d'intégration

Le vocabulaire d'abord. Une série \(X_t\) est dite intégrée d'ordre \(d\), notée \(X_t \sim I(d)\), s'il faut la différencier \(d\) fois pour la rendre stationnaire :

Ordre d'intégration
\[(1-B)^d X_t \;\text{est stationnaire}, \qquad (1-B)^{d-1} X_t \;\text{ne l'est pas}\]

Un bruit blanc est \(I(0)\) ; une marche aléatoire est \(I(1)\), car sa différence première est un bruit blanc. Dans le formalisme multivarié du chapitre VARMA, cela correspond à l'opérateur diagonal \(\lambda(B) = \operatorname{diag}\!\big((1-B)^{d_1}, \dots, (1-B)^{d_m}\big)\) du modèle VARIMA \(\Phi(B)\lambda(B)(\mathbf{X}_t - \boldsymbol\mu) = \Theta(B)\mathbf{a}_t\), où chaque composante peut avoir son propre ordre de différenciation \(d_j\).

Deux règles d'arithmétique donnent l'intuition de tout ce qui suit. Si \(X_t \sim I(0)\) et \(Y_t \sim I(0)\), alors toute combinaison linéaire est \(I(0)\). Si \(X_t \sim I(1)\) et \(Y_t \sim I(1)\), alors en général \(aX_t + bY_t \sim I(1)\) : la non-stationnarité ne s'annule pas. La cointégration est précisément l'exception à cette règle.

Définition de la cointégration

Définition (Engle et Granger, 1987)

Les composantes du vecteur \(\mathbf{X}_t = (X_{t1}, \dots, X_{tm})'\) sont dites cointégrées d'ordre \((d, b)\), noté \(\mathbf{X}_t \sim CI(d,b)\), si :

(i) chaque composante est \(I(d)\) ; (ii) il existe un vecteur non nul \(\boldsymbol\beta = (\beta_1,\dots,\beta_m)'\) tel que

\[z_t = \boldsymbol\beta' \mathbf{X}_t \sim I(d-b), \qquad b > 0.\]

Le cas qui compte en pratique est \(CI(1,1)\) : chaque série est \(I(1)\), mais une combinaison particulière est \(I(0)\), donc stationnaire. Le vecteur \(\boldsymbol\beta\) s'appelle vecteur de cointégration, et \(z_t\) l'erreur d'équilibre : l'écart momentané à une relation de long terme \(\boldsymbol\beta' \mathbf{X}_t = 0\).

Notez que \(\boldsymbol\beta\) n'est défini qu'à une constante multiplicative près : si \(\boldsymbol\beta' \mathbf{X}_t\) est stationnaire, \(2\boldsymbol\beta' \mathbf{X}_t\) l'est aussi. On normalise donc conventionnellement le premier coefficient à 1, ce qui donne dans le cas bivarié \(z_t = X_{t1} - \beta X_{t2}\).

L'image du promeneur et de son chien. Due à Murray (1994). Le promeneur suit une marche aléatoire : il n'a pas de destination. Le chien aussi. Prises séparément, les deux trajectoires sont \(I(1)\) et imprévisibles. Mais la laisse impose que leur écart reste borné : \(z_t\) est stationnaire. Sans laisse, les deux marches divergent et l'écart est lui-même \(I(1)\) — pas de cointégration. Toute la question statistique est : cette laisse existe-t-elle, ou est-ce que je vois deux promeneurs qui vont dans la même direction par hasard ?

Simulateur : la laisse et le chien

Le second simulateur construit une paire dont vous contrôlez la laisse. La série \(X_{t2}\) est une marche aléatoire pure — la tendance stochastique commune. L'erreur d'équilibre suit un AR(1) de paramètre \(\phi = 1 - \alpha\), et \(X_{t1}\) est reconstruite par \(X_{t1} = \beta X_{t2} + z_t\) :

Le modèle simulé
\[X_{t2} = X_{t-1,2} + u_t, \qquad z_t = (1-\alpha)\,z_{t-1} + e_t, \qquad X_{t1} = \beta X_{t2} + z_t\]

Le paramètre \(\alpha\) est la vitesse de rappel. Quand \(\alpha = 0\), la laisse est coupée : \(z_t\) devient une marche aléatoire et la paire n'est plus cointégrée, même si les deux courbes se ressemblent encore beaucoup à l'œil. Quand \(\alpha\) augmente, l'écart est ramené vers zéro de plus en plus vite. Faites glisser le curseur de \(\alpha\) vers 0 et regardez la statistique du test basculer.

Simulateur 2 — paire cointégrée à vitesse de rappel réglable

Haut : les deux séries en niveau. Bas : l'erreur d'équilibre \(z_t = X_{t1} - \beta X_{t2}\).

1.00 0.15 1.0 240

Trois choses méritent d'être observées dans les chiffres du simulateur. D'abord, l'estimation de \(\beta\) par moindres carrés est excellente même quand le bruit est énorme : c'est la superconsistance, \(\hat\beta\) converge en \(n\) au lieu de \(\sqrt{n}\). Ensuite, la demi-vie \(\ln(0{,}5)/\ln(\hat\phi)\) donne le nombre de périodes nécessaires pour résorber la moitié d'un choc : c'est la quantité qu'un praticien du pairs trading regarde en premier. Enfin, la statistique du test est comparée à \(-3{,}34\) et non à \(-1{,}96\) : la section suivante explique pourquoi.

Le test d'Engle–Granger

La procédure en deux étapes est directe. On estime d'abord la relation de long terme par moindres carrés ordinaires, puis on teste si le résidu est stationnaire :

Étape 1 — relation de long terme
\[X_{t1} = \mu + \beta X_{t2} + z_t \quad\Longrightarrow\quad \hat z_t = X_{t1} - \hat\mu - \hat\beta X_{t2}\]
Étape 2 — test de racine unitaire sur le résidu (ADF)
\[\Delta \hat z_t = \rho\, \hat z_{t-1} + \sum_{i=1}^{p} \gamma_i \Delta \hat z_{t-i} + \varepsilon_t, \qquad H_0 : \rho = 0 \;\;(\text{pas de cointégration})\]

Sous \(H_0\), \(\hat z_t\) contient une racine unitaire : les deux séries dérivent chacune de leur côté. Sous \(H_1\), \(\rho < 0\) et l'écart est ramené vers zéro. On rejette \(H_0\) quand la statistique \(t\) associée à \(\hat\rho\) est suffisamment négative.

Le point technique qui compte. La statistique n'est pas de Student, et elle n'est même pas celle d'un ADF ordinaire. Deux raisons se cumulent. Premièrement, sous racine unitaire, la loi limite s'exprime par des fonctionnelles de mouvement brownien, pas par une normale — c'est déjà le cas pour Dickey–Fuller. Deuxièmement, \(\hat z_t\) n'est pas observé : c'est un résidu dont les moindres carrés ont minimisé la variance, ce qui le fait paraître plus stationnaire qu'il ne l'est. Les valeurs critiques doivent donc être décalées vers la gauche (MacKinnon, 1991).
TestStatistiqueValeur critique à 5 %
Student ordinaire (invalide ici)\(t\)−1,96
Dickey–Fuller augmenté, série observée, avec constante\(t_{\rho}\)≈ −2,86
Engle–Granger, résidu, 2 variables\(t_{\rho}\)≈ −3,34
Engle–Granger, résidu, 3 variables\(t_{\rho}\)≈ −3,74
Engle–Granger, résidu, 4 variables\(t_{\rho}\)≈ −4,10

Valeurs asymptotiques approchées de MacKinnon (1991) pour le cas avec constante et sans tendance déterministe. Utiliser −1,96 revient à multiplier par cinq ou six le taux de fausses découvertes.

Vérifier tout cela par simulation

Le code ci-dessous s'exécute réellement dans votre navigateur (Python compilé en WebAssembly). Modifiez les valeurs et relancez : le premier lancement prend quelques secondes le temps de charger Python.

PYTHON · NUMPY
PYTHON · NUMPY

Correction d'erreur : le théorème de représentation

Le résultat le plus profond de la théorie n'est pas le test, c'est une équivalence. Le théorème de représentation de Granger énonce que deux séries \(I(1)\) sont cointégrées si et seulement si elles admettent une représentation à correction d'erreur :

Modèle à correction d'erreur (ECM), cas bivarié
\[\Delta X_{t1} = \alpha_1 \underbrace{(X_{t-1,1} - \beta X_{t-1,2})}_{z_{t-1}\;=\;\text{déséquilibre}} + \sum_{i} \gamma_{1i}\Delta X_{t-i,1} + \sum_{i} \delta_{1i}\Delta X_{t-i,2} + a_{t1}\] \[\Delta X_{t2} = \alpha_2 \, z_{t-1} + \sum_{i} \gamma_{2i}\Delta X_{t-i,1} + \sum_{i} \delta_{2i}\Delta X_{t-i,2} + a_{t2}\]

Chaque équation est parfaitement légitime au sens des moindres carrés : toutes les variables sont stationnaires, puisqu'on travaille en différences et que \(z_{t-1}\) est \(I(0)\) par hypothèse. Le coefficient \(\alpha_1\) mesure la fraction du déséquilibre corrigée à chaque période ; il doit être négatif pour que le système soit stable. Au moins un des \(\alpha_j\) doit être non nul, sinon rien ne ramène le système vers l'équilibre.

En notation VAR, la reformulation est éclairante. Un VAR(\(p\)) en niveau \(\mathbf{X}_t = \Phi_1\mathbf{X}_{t-1} + \dots + \Phi_p \mathbf{X}_{t-p} + \mathbf{a}_t\) se réécrit exactement :

Forme VECM d'un VAR(p)
\[\Delta \mathbf{X}_t = \Pi \mathbf{X}_{t-1} + \sum_{i=1}^{p-1} \Gamma_i \Delta\mathbf{X}_{t-i} + \mathbf{a}_t, \qquad \Pi = -\Big(I - \sum_{k=1}^{p}\Phi_k\Big)\]

Tout se joue alors sur le rang de la matrice \(\Pi\), de dimension \(m \times m\) :

rang(\(\Pi\)) = rInterprétationQue faire
\(r = 0\)\(\Pi = 0\) : aucune relation de long termeVAR en différences premières
\(0 < r < m\)\(\Pi = \boldsymbol\alpha \boldsymbol\beta'\), \(r\) relations de cointégrationVECM de rang \(r\)
\(r = m\)\(\Pi\) de rang plein : \(\mathbf{X}_t\) était déjà stationnaireVAR en niveau

La factorisation \(\Pi = \boldsymbol\alpha\boldsymbol\beta'\) sépare proprement les deux natures : \(\boldsymbol\beta\) (dimension \(m \times r\)) contient les relations d'équilibre de long terme, \(\boldsymbol\alpha\) (dimension \(m \times r\)) les vitesses d'ajustement de court terme.

PYTHON · NUMPY

Au-delà de deux séries : Johansen

Engle–Granger a deux faiblesses dès qu'on dépasse deux variables. D'abord, le résultat dépend de la variable qu'on choisit à gauche du signe égal : régresser \(X_1\) sur \(X_2\) ou \(X_2\) sur \(X_1\) ne donne pas exactement le même verdict en échantillon fini. Ensuite, avec \(m\) séries il peut exister jusqu'à \(m-1\) relations de cointégration indépendantes, et une régression unique n'en révèle au mieux qu'une combinaison.

La procédure de Johansen (1988, 1991) traite le problème directement comme une question de rang. On estime le VECM par maximum de vraisemblance et on teste le rang de \(\Pi\) à partir des valeurs propres \(\hat\lambda_1 \geq \dots \geq \hat\lambda_m\) d'un problème de corrélations canoniques entre \(\Delta\mathbf{X}_t\) et \(\mathbf{X}_{t-1}\), corrigées des retards :

Statistique de la trace
\[\mathrm{LR}_{\text{trace}}(r) = -n \sum_{j=r+1}^{m} \ln\!\big(1 - \hat\lambda_j\big), \qquad H_0 : \text{rang}(\Pi) \leq r\]

On teste séquentiellement \(r = 0\), puis \(r = 1\), et ainsi de suite, jusqu'au premier non-rejet — qui donne le rang retenu. La statistique de la valeur propre maximale \(-n\ln(1-\hat\lambda_{r+1})\) fournit un test complémentaire. Là encore, les lois limites sont non standard et tabulées séparément selon les termes déterministes inclus (constante dans la relation de cointégration, tendance, etc.) — six cas classiques, et se tromper de cas est une source d'erreur fréquente.

En pratique (R et Python)

Personne ne code un test de Johansen à la main en production. Voici les chemins standards, avec le détail qui compte à chaque ligne.

# ─── R : tseries + urca + vars ───
library(tseries); library(urca); library(vars)

# 0. verifier que CHAQUE serie est bien I(1) avant tout
adf.test(x1)              # non rejet attendu en niveau
adf.test(diff(x1))        # rejet attendu en difference

# 1. Engle-Granger en deux etapes
eg  <- lm(x1 ~ x2)
adf.test(residuals(eg))   # ATTENTION : p-value invalide ici
po.test(cbind(x1, x2))    # Phillips-Ouliaris : valeurs critiques correctes

# 2. Johansen : rang de cointegration
jo <- ca.jo(cbind(x1, x2), type = "trace", ecdet = "const", K = 2)
summary(jo)               # comparer la stat. de trace aux valeurs critiques

# 3. VECM estime, puis conversion en VAR en niveau pour prevoir
vecm <- cajorls(jo, r = 1)
var_niveau <- vec2var(jo, r = 1)
predict(var_niveau, n.ahead = 25)
# ─── Python : statsmodels ───
from statsmodels.tsa.stattools import adfuller, coint
from statsmodels.tsa.vector_ar.vecm import coint_johansen, VECM

adfuller(x1)                       # I(1) ? en niveau puis en difference
coint(x1, x2)                      # Engle-Granger, valeurs critiques correctes

jres = coint_johansen(X, det_order=0, k_ar_diff=1)
print(jres.lr1, jres.cvt)          # trace vs valeurs critiques 90/95/99 %

modele = VECM(X, k_ar_diff=1, coint_rank=1, deterministic="ci").fit()
print(modele.beta, modele.alpha)   # long terme, vitesses d'ajustement
modele.predict(steps=25)
Le piège de adf.test(residuals(eg)). La fonction rendra une p-value, et cette p-value sera fausse — trop petite — parce qu'elle est calculée pour une série observée, pas pour un résidu de moindres carrés. Utilisez po.test() en R ou coint() en Python, qui appliquent les valeurs critiques de MacKinnon adaptées au nombre de régresseurs.

Limites et pièges

Hypothèse ou piègeConséquence → parade
Les séries doivent être du même ordre \(I(1)\)Mélanger \(I(0)\) et \(I(1)\) rend le test ininterprétable → tester chaque série séparément d'abord
Rupture structurelle dans la relationLe test ne rejette pas \(H_0\) alors que la cointégration existe par morceaux → tests de Gregory–Hansen
Faible puissance en petit échantillonAvec \(n < 100\) et un rappel lent, on ne détecte presque rien → allonger l'échantillon plutôt qu'augmenter la fréquence
Choix du nombre de retards \(p\) dans l'ADFTrop peu : résidus autocorrélés ; trop : perte de puissance → AIC ou règle de Schwert
Termes déterministes (constante, tendance)Chaque cas a ses propres valeurs critiques → décider avant de regarder les données
Cointégration ≠ causalitéUne relation d'équilibre ne dit pas qui s'ajuste à qui → lire les \(\alpha_j\) et tester l'exogénéité faible
Relation stable dans le tempsEn finance, les paires se « décointègrent » → réestimer en fenêtre glissante et surveiller la demi-vie
L'erreur la plus coûteuse. Différencier systématiquement pour « rendre stationnaire » quand les séries sont en réalité cointégrées. On obtient un modèle valide mais amputé : toute l'information de long terme, celle qui contient le mécanisme de rappel, disparaît dans la différenciation. Les prévisions à long horizon deviennent alors nettement moins bonnes que celles du VECM correspondant.

Références

  • Engle, R. F. & Granger, C. W. J. (1987). Co-integration and error correction: representation, estimation, and testing, Econometrica 55(2) — l'article fondateur, Nobel 2003.
  • Granger, C. W. J. & Newbold, P. (1974). Spurious regressions in econometrics, Journal of Econometrics 2 — la mise en garde initiale, par simulation.
  • Phillips, P. C. B. (1986). Understanding spurious regressions in econometrics, Journal of Econometrics 33 — la théorie asymptotique du phénomène.
  • Johansen, S. (1991). Estimation and hypothesis testing of cointegration vectors in Gaussian vector autoregressive models, Econometrica 59(6).
  • MacKinnon, J. G. (1991). Critical values for cointegration tests, in Engle & Granger (éds.), Long-Run Economic Relationships, Oxford University Press.
  • Woodward, W. A., Gray, H. L. & Elliott, A. C. (2017). Applied Time Series Analysis with R, 2e éd., CRC Press — chapitre 10, cadre VAR/VARMA et tests d'association entre séries.
  • Murray, M. P. (1994). A drunk and her dog: an illustration of cointegration and error correction, The American Statistician 48(1) — la métaphore de la laisse.

Les simulateurs de cette page sont écrits en JavaScript/Canvas sans dépendance externe ; les cellules Python s'exécutent via Pyodide. Le code est disponible sur GitHub.