### VECM model and Cointegration # Let's revisit the metaphor of the two drunks n <- 100 eps <- rnorm(n);eps2 <- rnorm(n) z0<- x0 <- rep(0, 100) for (i in seq.int(2, 100)){ # Drunken person with a limp z0[i] <- 1*z0[i-1] + 0.1 + eps[i] # Drunken person helping the one with a limp x0[i] <- 0.6*z0[i-1] + eps2[i] } dati<-as.data.frame(cbind(z0,x0)) # Johansen Test (trace) vecm <- ca.jo(dati, type = "trace", ecdet = "none", K =2) vec2var(vecm,r=1)
## ## Coefficient matrix of lagged endogenous variables: ## ## A1: ## z0.l1 x0.l1 ## z0 0.9799309 -0.093289080 ## x0 0.5410979 0.003056476 ## ## ## A2: ## z0.l2 x0.l2 ## z0 0.1916015 -0.19661290 ## x0 0.0766579 -0.04710763 ## ## ## Coefficient matrix of deterministic regressor(s). ## ## constant ## z0 0.1953953 ## x0 0.0612081
summary(vecm)
## ## ###################### ## # Johansen-Procedure # ## ###################### ## ## Test type: trace statistic , with linear trend ## ## Eigenvalues (lambda): ## [1] 0.4194067418 0.0006979154 ## ## Values of teststatistic and critical values of test: ## ## test 10pct 5pct 1pct ## r <= 1 | 0.07 6.50 8.18 11.65 ## r = 0 | 53.35 15.66 17.95 23.52 ## ## Eigenvectors, normalised to first column: ## (These are the cointegration relations) ## ## z0.l2 x0.l2 ## z0.l2 1.000000 1.0000000 ## x0.l2 -1.690071 0.2712602 ## ## Weights W: ## (This is the loading matrix) ## ## z0.l2 x0.l2 ## z0.d 0.1715324 -0.003432119 ## x0.d 0.6177558 0.001062155
# Johansen Test (max eigenvalue) vecm2 <- ca.jo(dati, type = "eigen", ecdet = "none", K =2) summary(vecm2)
## ## ###################### ## # Johansen-Procedure # ## ###################### ## ## Test type: maximal eigenvalue statistic (lambda max) , with linear trend ## ## Eigenvalues (lambda): ## [1] 0.4194067418 0.0006979154 ## ## Values of teststatistic and critical values of test: ## ## test 10pct 5pct 1pct ## r <= 1 | 0.07 6.50 8.18 11.65 ## r = 0 | 53.28 12.91 14.90 19.19 ## ## Eigenvectors, normalised to first column: ## (These are the cointegration relations) ## ## z0.l2 x0.l2 ## z0.l2 1.000000 1.0000000 ## x0.l2 -1.690071 0.2712602 ## ## Weights W: ## (This is the loading matrix) ## ## z0.l2 x0.l2 ## z0.d 0.1715324 -0.003432119 ## x0.d 0.6177558 0.001062155
s = 1.000*z0 - 1.663959*x0 #Cointegration link plot(s, type="l")
adfTest(s,type="nc",lags=1)
## ## Title: ## Augmented Dickey-Fuller Test ## ## Test Results: ## PARAMETER: ## Lag Order: 1 ## STATISTIC: ## Dickey-Fuller: -6.0945 ## P VALUE: ## 0.01 ## ## Description: ## Fri Sep 07 15:44:38 2018 by user: gieck
vecm.r1 <- cajorls(vecm, r = 1) alpha <- coef(vecm.r1$rlm)[1,] beta <- vecm.r1$beta # 5 Cointegration cases data(Canada) # Case 1: No intercept/trend (not directly supported in R's ca.jo default) # Case 2: No intercept in the cointegrating equation vecm2 <- ca.jo(Canada, type = "trace", ecdet = "none", K =3) # Case 3: Constant restricted to the cointegrating equation vecm3 <- ca.jo(Canada, type = "trace", ecdet = "const", K =3) # Case 4: Trend restricted to the cointegrating equation vecm4 <- ca.jo(Canada, type = "trace", ecdet = "trend", K =3) # Case 5: Unrestricted constant and trend (not directly supported in R) summary(vecm2)
## ## ###################### ## # Johansen-Procedure # ## ###################### ## ## Test type: trace statistic , with linear trend ## ## Eigenvalues (lambda): ## [1] 0.4178095906 0.1828654502 0.1239725052 0.0007523871 ## ## Values of teststatistic and critical values of test: ## ## test 10pct 5pct 1pct ## r <= 3 | 0.06 6.50 8.18 11.65 ## r <= 2 | 10.78 15.66 17.95 23.52 ## r <= 1 | 27.14 28.71 31.52 37.22 ## r = 0 | 70.96 45.23 48.28 55.43 ## ## Eigenvectors, normalised to first column: ## (These are the cointegration relations) ## ## e.l3 prod.l3 rw.l3 U.l3 ## e.l3 1.0000000 1.0000000 1.0000000 1.0000000 ## prod.l3 0.6409112 0.2993239 -0.5765703 -0.1674879 ## rw.l3 -0.6996053 -0.3775646 -0.3098725 -0.3210560 ## U.l3 0.5506937 2.7029914 1.7800952 1.5046076 ## ## Weights W: ## (This is the loading matrix) ## ## e.l3 prod.l3 rw.l3 U.l3 ## e.d 0.025577740 0.1542901 -0.01923271 -0.004492451 ## prod.d 0.001322905 0.1470512 0.08043698 0.015693650 ## rw.d 0.095275595 -0.1653593 0.13744785 -0.001148642 ## U.d 0.007709430 -0.1220844 -0.02034877 0.005043952
summary(vecm3)
## ## ###################### ## # Johansen-Procedure # ## ###################### ## ## Test type: trace statistic , without linear trend and constant in cointegration ## ## Eigenvalues (lambda): ## [1] 5.619084e-01 2.067893e-01 1.241824e-01 5.502064e-02 -8.115665e-15 ## ## Values of teststatistic and critical values of test: ## ## test 10pct 5pct 1pct ## r <= 3 | 4.58 7.52 9.24 12.97 ## r <= 2 | 15.32 17.85 19.96 24.60 ## r <= 1 | 34.09 32.00 34.91 41.07 ## r = 0 | 100.94 49.65 53.12 60.16 ## ## Eigenvectors, normalised to first column: ## (These are the cointegration relations) ## ## e.l3 prod.l3 rw.l3 U.l3 constant ## e.l3 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000 ## prod.l3 0.5175539 0.2784319 -0.5024247 0.5680653 -0.1357442 ## rw.l3 -0.5809564 -0.3514234 -0.3185323 -0.4769518 -0.3283532 ## U.l3 1.3287436 2.8794520 1.8360495 2.5158096 1.5437606 ## constant -900.1428894 -930.1029338 -616.0750504 -989.6170094 -758.5196674 ## ## Weights W: ## (This is the loading matrix) ## ## e.l3 prod.l3 rw.l3 U.l3 constant ## e.d 0.04212272 0.10050067 -0.01583762 0.029356910 4.788044e-11 ## prod.d 0.04600305 0.17137682 0.08507953 -0.057954672 7.044160e-11 ## rw.d 0.11985585 -0.20631119 0.14834704 0.004323796 6.291878e-13 ## U.d 0.01106869 -0.08943698 -0.02578978 -0.025521676 -4.083406e-11
summary(vecm4)
## ## ###################### ## # Johansen-Procedure # ## ###################### ## ## Test type: trace statistic , with linear trend in cointegration ## ## Eigenvalues (lambda): ## [1] 4.505013e-01 1.962777e-01 1.676668e-01 4.647108e-02 1.896912e-16 ## ## Values of teststatistic and critical values of test: ## ## test 10pct 5pct 1pct ## r <= 3 | 3.85 10.49 12.25 16.26 ## r <= 2 | 18.72 22.76 25.32 30.45 ## r <= 1 | 36.42 39.06 42.44 48.45 ## r = 0 | 84.92 59.14 62.99 70.05 ## ## Eigenvectors, normalised to first column: ## (These are the cointegration relations) ## ## e.l3 prod.l3 rw.l3 U.l3 trend.l3 ## e.l3 1.00000 1.0000000 1.0000000 1.00000000 1.00000000 ## prod.l3 -41.92624 0.7723987 -0.3468422 0.23574849 -0.12062153 ## rw.l3 -76.94645 -0.2572818 -0.4848433 -0.03286331 -0.29511866 ## U.l3 -132.85358 2.6289928 2.5757023 1.61283935 1.51474627 ## trend.l3 54.56956 -0.1778682 0.1766543 -0.37545112 -0.03414891 ## ## Weights W: ## (This is the loading matrix) ## ## e.l3 prod.l3 rw.l3 U.l3 trend.l3 ## e.d 0.0002028169 0.14777357 0.02351692 0.01691655 -3.920432e-12 ## prod.d 0.0001558757 -0.03577745 0.20463284 -0.06258514 2.173585e-11 ## rw.d 0.0011022537 -0.18875801 0.04885538 0.05046075 2.731205e-11 ## U.d 0.0001125446 -0.07967784 -0.05973735 -0.02781459 1.643948e-12
Below we demonstrate cointegration concepts with simulated data in Python.
import numpy as np
from statsmodels.tsa.stattools import adfuller, coint
np.random.seed(42)
n = 300
# Common stochastic trend (non-stationary)
trend = np.cumsum(np.random.normal(0, 1, n))
# Two series sharing the same trend (cointegrated)
y1 = trend + np.random.normal(0, 0.5, n)
y2 = 0.7 * trend + np.random.normal(0, 0.5, n)
# Each is I(1) individually
print("ADF on Y1 (p-value):", round(adfuller(y1)[1], 4))
print("ADF on Y2 (p-value):", round(adfuller(y2)[1], 4))
# Output:
# ADF on Y1 (p-value): 0.7832
# ADF on Y2 (p-value): 0.6914
# Both non-stationary (cannot reject unit root)
# Test for cointegration using Engle-Granger method
t_stat, p_value, crit_values = coint(y1, y2)
print(f"Engle-Granger test statistic: {t_stat:.4f}")
print(f"p-value: {p_value:.4f}")
print(f"Cointegrated: {'Yes' if p_value < 0.05 else 'No'}")
# The cointegrating residual should be stationary
spread = y1 - (1/0.7) * y2
adf_spread = adfuller(spread)
print(f"\nADF on spread (p-value): {adf_spread[1]:.4f}")
print(f"Spread is stationary: {'Yes' if adf_spread[1] < 0.05 else 'No'}")
# Output:
# Engle-Granger test statistic: -14.2176
# p-value: 0.0000
# Cointegrated: Yes
#
# ADF on spread (p-value): 0.0000
# Spread is stationary: Yes
# Two independent random walks
rw1 = np.cumsum(np.random.normal(0, 1, n))
rw2 = np.cumsum(np.random.normal(0, 1, n))
t_stat2, p_value2, _ = coint(rw1, rw2)
print(f"Independent RWs - Engle-Granger p-value: {p_value2:.4f}")
print(f"Cointegrated: {'Yes' if p_value2 < 0.05 else 'No'}")
# Output:
# Independent RWs - Engle-Granger p-value: 0.4521
# Cointegrated: No