Aplicación 3.6: Autocorrelación temporal y regresiones dinámicas
Caso univariante (especificación ARDL): Tipos de interés en España
En la primera aplicación se estimará un modelo de regresión uniecuacional para explicar los tipos de interés de largo plazo vigentes en la economía española en la década de los años 1980, se analizará la correlación serial de los residuos de dicho modelo, y se propondrán soluciones al problema detectado de autocorrelación en los errores.
La regresión de referencia utilizada es la siguiente:
\[RL_{t} = \beta_1 + \beta_2 R3M_{t} + \beta_3 RD_{t} + e_{t}\]
donde \(RL\) y \(R3M\) son los tipos de interés de largo y corto (3 meses) plazo, respectivamente, y \(RD\) es el tipo de interés de remuneración de los depósitos bancarios. Dicha especificación se corresponde con el denominado modelo de Klein-Monti.
Code
# Lectura de librerías
library(tidyverse)
library(dynlm)
library(car)
library(lmtest)
library(sandwich)
library(orcutt)
# Lectura de datos
TIPOS_INT_ESP <- read_delim("data/TIPOS_INT_ESP.csv", delim = ";")
str(TIPOS_INT_ESP)spc_tbl_ [35 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
$ date: Date[1:35], format: "1982-01-01" "1982-04-01" ...
$ R3M : num [1:35] 16.1 17.5 17.1 18.5 17.9 ...
$ RD : num [1:35] 8.12 8.29 8.36 8.49 8.54 8.7 8.71 8.79 9.04 9.12 ...
$ RL : num [1:35] 17.5 17.6 17.7 17.7 17.7 ...
- attr(*, "spec")=
.. cols(
.. date = col_date(format = ""),
.. R3M = col_double(),
.. RD = col_double(),
.. RL = col_double()
.. )
- attr(*, "problems")=<externalptr>
Code
# Gráfica de las series temporales
ggplot(TIPOS_INT_ESP,aes(date,R3M)) +
geom_line(color="blue") +
geom_line(aes(date,RD),color="orange") +
geom_line(aes(date,RL),color="green") +
xlab("") + ylab("Tipos de interés")Code
# Asignación del formato trimestral
TIPOS_ESP_ts <- ts(TIPOS_INT_ESP[,2:4], start=c(1982,1), end = c(1990,3),
frequency = 4)
plot(TIPOS_ESP_ts)Code
# Modelo de regresión estático
# Función lm() estándar:
# summary(lm_KM <- lm(RL ~ R3M + RD, data=TIPOS_ESP_ts))
# Librería dynlm (especificación ARDL(0,0,0))
summary(dynlm_KM_0 <- dynlm(RL ~ R3M + RD, data=TIPOS_ESP_ts))
Time series regression with "ts" data:
Start = 1982(1), End = 1990(3)
Call:
dynlm(formula = RL ~ R3M + RD, data = TIPOS_ESP_ts)
Residuals:
Min 1Q Median 3Q Max
-0.75780 -0.16274 0.07166 0.25488 0.61088
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 7.02728 0.48653 14.444 1.43e-15 ***
R3M 0.15918 0.02301 6.918 7.84e-08 ***
RD 0.92600 0.07777 11.907 2.69e-13 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.3567 on 32 degrees of freedom
Multiple R-squared: 0.9322, Adjusted R-squared: 0.9279
F-statistic: 219.9 on 2 and 32 DF, p-value: < 2.2e-16
Code
# Contrastes de correlación en los errores (autocorrelación)
# Residuos del modelo
resid <-residuals(dynlm_KM_0)
plot(resid)
abline(h=0, lty=2)Code
# Correlograma de los residuos
corr <- acf(resid)Code
corr$acf[2:10][1] 0.661182301 0.373478408 0.196080083 0.059991302 0.055270320 0.096234644
[7] 0.258730586 0.172078454 0.005038669
Code
# Test de Durbin-Watson
dwtest(dynlm_KM_0, alternative = "two.sided")
Durbin-Watson test
data: dynlm_KM_0
DW = 0.49682, p-value = 9.951e-09
alternative hypothesis: true autocorrelation is not 0
Code
dwtest(dynlm_KM_0, alternative = "greater")
Durbin-Watson test
data: dynlm_KM_0
DW = 0.49682, p-value = 4.976e-09
alternative hypothesis: true autocorrelation is greater than 0
Code
# Test de Breusch-Godfrey
bgtest(dynlm_KM_0, order=1, type="Chisq", fill=0)
Breusch-Godfrey test for serial correlation of order up to 1
data: dynlm_KM_0
LM test = 18.689, df = 1, p-value = 1.539e-05
Code
# Corrección de la autocorrelación
# MCO corregidos: errores estándar robustos, HAC (Newey-West)
summary(dynlm_KM_0 <- dynlm(RL ~ R3M + RD, data=TIPOS_ESP_ts),
vcov.=vcovHAC(dynlm_KM_0))
Time series regression with "ts" data:
Start = 1982(1), End = 1990(3)
Call:
dynlm(formula = RL ~ R3M + RD, data = TIPOS_ESP_ts)
Residuals:
Min 1Q Median 3Q Max
-0.75780 -0.16274 0.07166 0.25488 0.61088
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 7.02728 0.66475 10.571 5.72e-12 ***
R3M 0.15918 0.03572 4.457 9.56e-05 ***
RD 0.92600 0.11776 7.863 5.69e-09 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.3567 on 32 degrees of freedom
Multiple R-squared: 0.9322, Adjusted R-squared: 0.9279
F-statistic: 146.4 on 2 and 32 DF, p-value: < 2.2e-16
Code
# Mínimos cuadrados generalizados (MCG): errores AR(1)
cochrane.orcutt(dynlm_KM_0)Cochrane-orcutt estimation for first order autocorrelation
Call:
dynlm(formula = RL ~ R3M + RD, data = TIPOS_ESP_ts)
number of interaction: 11
rho 0.773176
Durbin-Watson statistic
(original): 0.49682 , p-value: 4.976e-09
(transformed): 1.69696 , p-value: 1.308e-01
coefficients:
(Intercept) R3M RD
7.013488 0.157823 0.913996
Code
# Dinámica en las variables: modelo ARDL(1,1,1)
summary(dynlm_KM_1 <- dynlm(RL ~ L(RL, 1:1) + L(R3M, 0:1) + L(RD, 0:1),
data=TIPOS_ESP_ts))
Time series regression with "ts" data:
Start = 1982(2), End = 1990(3)
Call:
dynlm(formula = RL ~ L(RL, 1:1) + L(R3M, 0:1) + L(RD, 0:1), data = TIPOS_ESP_ts)
Residuals:
Min 1Q Median 3Q Max
-0.40953 -0.14242 -0.01385 0.16811 0.38224
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.95931 1.02773 1.906 0.06690 .
L(RL, 1:1) 0.71922 0.13830 5.200 1.60e-05 ***
L(R3M, 0:1)0 0.16624 0.02491 6.673 3.05e-07 ***
L(R3M, 0:1)1 -0.07777 0.03656 -2.127 0.04237 *
L(RD, 0:1)0 0.65959 0.19117 3.450 0.00179 **
L(RD, 0:1)1 -0.48983 0.24726 -1.981 0.05748 .
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.2333 on 28 degrees of freedom
Multiple R-squared: 0.9743, Adjusted R-squared: 0.9697
F-statistic: 212 on 5 and 28 DF, p-value: < 2.2e-16
Code
# Comparación de modelos
compareCoefs(dynlm_KM_0,dynlm_KM_1)Calls:
1: dynlm(formula = RL ~ R3M + RD, data = TIPOS_ESP_ts)
2: dynlm(formula = RL ~ L(RL, 1:1) + L(R3M, 0:1) + L(RD, 0:1), data =
TIPOS_ESP_ts)
Model 1 Model 2
(Intercept) 7.027 1.959
SE 0.487 1.028
R3M 0.159
SE 0.023
RD 0.9260
SE 0.0778
L(RL, 1:1) 0.719
SE 0.138
L(R3M, 0:1)0 0.1662
SE 0.0249
L(R3M, 0:1)1 -0.0778
SE 0.0366
L(RD, 0:1)0 0.660
SE 0.191
L(RD, 0:1)1 -0.490
SE 0.247
Code
# Estimación de los efectos parciales asociados
# Efectos a corto y largo plazo
library(nlWaldTest)
# Modelo estático (cp=lp)
nlConfint(dynlm_KM_0, c("b[2]","b[3]")) value 2.5 % 97.5 %
b[2] 0.1591826 0.1140841 0.2042812
b[3] 0.9260003 0.7735733 1.0784272
Code
nlWaldtest(dynlm_KM_0, "b[2]")
Wald Chi-square test of a restriction on model parameters
data: dynlm_KM_0
Chisq = 47.859, df = 1, p-value = 4.58e-12
Code
nlWaldtest(dynlm_KM_0, "b[3]")
Wald Chi-square test of a restriction on model parameters
data: dynlm_KM_0
Chisq = 141.77, df = 1, p-value < 2.2e-16
Code
# Modelo dinámico
# Corto plazo
nlConfint(dynlm_KM_1, c("b[3]","b[5]")) value 2.5 % 97.5 %
b[3] 0.1662367 0.1174099 0.2150636
b[5] 0.6595859 0.2848907 1.0342812
Code
nlWaldtest(dynlm_KM_1, "b[3]")
Wald Chi-square test of a restriction on model parameters
data: dynlm_KM_1
Chisq = 44.528, df = 1, p-value = 2.508e-11
Code
nlWaldtest(dynlm_KM_1, "b[5]")
Wald Chi-square test of a restriction on model parameters
data: dynlm_KM_1
Chisq = 11.904, df = 1, p-value = 0.0005602
Code
# Largo plazo
nlConfint(dynlm_KM_1, c("(b[3]+b[4])/(1-b[2])","(b[5]+b[6])/(1-b[2])")) value 2.5 % 97.5 %
(b[3]+b[4])/(1-b[2]) 0.3150717 0.1198835 0.5102598
(b[5]+b[6])/(1-b[2]) 0.6045916 0.1048464 1.1043367
Code
nlWaldtest(dynlm_KM_1, "(b[3]+b[4])/(1-b[2])")
Wald Chi-square test of a restriction on model parameters
data: dynlm_KM_1
Chisq = 10.009, df = 1, p-value = 0.001557
Code
nlWaldtest(dynlm_KM_1, "(b[5]+b[6])/(1-b[2])")
Wald Chi-square test of a restriction on model parameters
data: dynlm_KM_1
Chisq = 5.6224, df = 1, p-value = 0.01773
Code
# Lectura de librerías
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import statsmodels.api as sm
import statsmodels.formula.api as smf
import statsmodels.stats.api as sms
from statsmodels.graphics import tsaplots
import statsmodels.stats.diagnostic as smsdiag
from statsmodels.compat import lzip
# Lectura de datos y asignación del índice temporal
TIPOS_INT_ESP = pd.read_csv("data/TIPOS_INT_ESP.csv", sep=";",
parse_dates=['date'], index_col='date')
TIPOS_INT_ESP.info()<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 35 entries, 1982-01-01 to 1990-07-01
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 R3M 35 non-null float64
1 RD 35 non-null float64
2 RL 35 non-null float64
dtypes: float64(3)
memory usage: 1.1 KB
Code
# Gráfica de las series temporales
plt.figure(1)
fig, ax = plt.subplots()
TIPOS_INT_ESP.plot(ax=ax)
plt.legend(['R3M','RD','RL']); plt.xlabel(''); plt.ylabel('Tipos de interés')
plt.show()Code
# Asignación del formato trimestral
TIPOS_ESP_ts = pd.read_csv("data/TIPOS_INT_ESP.csv", sep=";").iloc[:,1:]
date = pd.date_range(start = '1982', periods = len(TIPOS_ESP_ts.index),
freq = 'QS')
TIPOS_ESP_ts.index = date
TIPOS_ESP_ts.info()<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 35 entries, 1982-01-01 to 1990-07-01
Freq: QS-JAN
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 R3M 35 non-null float64
1 RD 35 non-null float64
2 RL 35 non-null float64
dtypes: float64(3)
memory usage: 1.1 KB
Code
TIPOS_ESP_ts.indexDatetimeIndex(['1982-01-01', '1982-04-01', '1982-07-01', '1982-10-01',
'1983-01-01', '1983-04-01', '1983-07-01', '1983-10-01',
'1984-01-01', '1984-04-01', '1984-07-01', '1984-10-01',
'1985-01-01', '1985-04-01', '1985-07-01', '1985-10-01',
'1986-01-01', '1986-04-01', '1986-07-01', '1986-10-01',
'1987-01-01', '1987-04-01', '1987-07-01', '1987-10-01',
'1988-01-01', '1988-04-01', '1988-07-01', '1988-10-01',
'1989-01-01', '1989-04-01', '1989-07-01', '1989-10-01',
'1990-01-01', '1990-04-01', '1990-07-01'],
dtype='datetime64[ns]', freq='QS-JAN')
Code
plt.figure(2)
fig, ax = plt.subplots(3, 1, sharex = True)
ax[0].plot(TIPOS_ESP_ts.R3M)
ax[0].set_ylabel('R3M')
ax[1].plot(TIPOS_ESP_ts.RD)
ax[1].set_ylabel('RD')
ax[2].plot(TIPOS_ESP_ts.RL)
ax[2].set_ylabel('RL')
plt.show()Code
# Modelo de regresión estático
model = smf.ols(formula = "RL ~ R3M + RD", data = TIPOS_ESP_ts)
lm_KM = model.fit()
print(lm_KM.summary()) OLS Regression Results
==============================================================================
Dep. Variable: RL R-squared: 0.932
Model: OLS Adj. R-squared: 0.928
Method: Least Squares F-statistic: 219.9
Date: Sun, 09 Feb 2025 Prob (F-statistic): 2.00e-19
Time: 14:21:36 Log-Likelihood: -12.013
No. Observations: 35 AIC: 30.03
Df Residuals: 32 BIC: 34.69
Df Model: 2
Covariance Type: nonrobust
==============================================================================
coef std err t P>|t| [0.025 0.975]
------------------------------------------------------------------------------
Intercept 7.0273 0.487 14.444 0.000 6.036 8.018
R3M 0.1592 0.023 6.918 0.000 0.112 0.206
RD 0.9260 0.078 11.907 0.000 0.768 1.084
==============================================================================
Omnibus: 3.138 Durbin-Watson: 0.497
Prob(Omnibus): 0.208 Jarque-Bera (JB): 2.855
Skew: -0.671 Prob(JB): 0.240
Kurtosis: 2.602 Cond. No. 144.
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
Code
# Contrastes de correlación en los errores (autocorrelación)
# Errores estimados (residuos)
residuos = lm_KM.resid
plt.figure(3)
plt.plot(residuos, label='Residuos')
plt.xlabel('Date')
plt.legend()
plt.show()Code
# Correlograma de los residuos
r = sm.tsa.stattools.acf(residuos, nlags=9, fft=True)
plt.figure(4)
fig, ax = plt.subplots()
tsaplots.plot_acf(residuos, lags=15, alpha=0.05, zero=False,
vlines_kwargs={"colors":'black'}, color='black', title='', ax=ax)
plt.show()Code
print(r)[1. 0.6611823 0.37347841 0.19608008 0.0599913 0.05527032
0.09623464 0.25873059 0.17207845 0.00503867]
Code
# Test de Durbin-Watson
sms.durbin_watson(residuos).round(5)0.49682
Code
# Test de Breusch-Godfrey
name = ['LM statistic', 'Chi^2 p-val', 'F statistic', 'F p-val']
BG_test = smsdiag.acorr_breusch_godfrey(lm_KM,1)
lzip(name, BG_test)[('LM statistic', 18.689051294910442), ('Chi^2 p-val', 1.538634377813448e-05), ('F statistic', 35.51973589135522), ('F p-val', 1.3756949593855872e-06)]
Code
# Corrección de la autocorrelación
# MCO corregidos: errores estándar robustos
# Corrección de la matriz de covarianzas: estimador de Newey-West
# (si maxlags=0 -> HAC=HC1)
lm_KM_HAC = smf.ols(formula = "RL ~ R3M + RD", data = TIPOS_ESP_ts).fit(cov_type='HAC', cov_kwds={'maxlags':6,'use_correction':True})
print(lm_KM_HAC.summary()) OLS Regression Results
==============================================================================
Dep. Variable: RL R-squared: 0.932
Model: OLS Adj. R-squared: 0.928
Method: Least Squares F-statistic: 103.2
Date: Sun, 09 Feb 2025 Prob (F-statistic): 1.11e-14
Time: 14:21:37 Log-Likelihood: -12.013
No. Observations: 35 AIC: 30.03
Df Residuals: 32 BIC: 34.69
Df Model: 2
Covariance Type: HAC
==============================================================================
coef std err z P>|z| [0.025 0.975]
------------------------------------------------------------------------------
Intercept 7.0273 0.859 8.183 0.000 5.344 8.710
R3M 0.1592 0.033 4.778 0.000 0.094 0.224
RD 0.9260 0.142 6.509 0.000 0.647 1.205
==============================================================================
Omnibus: 3.138 Durbin-Watson: 0.497
Prob(Omnibus): 0.208 Jarque-Bera (JB): 2.855
Skew: -0.671 Prob(JB): 0.240
Kurtosis: 2.602 Cond. No. 144.
==============================================================================
Notes:
[1] Standard Errors are heteroscedasticity and autocorrelation robust (HAC) using 6 lags and with small sample correction
Code
# Dinámica en las variables: modelo ARDL(1,1,1)
model = smf.ols(formula = "RL ~ RL.shift(1) + R3M + R3M.shift(1) + RD + RD.shift(1)",
data = TIPOS_ESP_ts)
lm_KM_dyn = model.fit()
print(lm_KM_dyn.summary()) OLS Regression Results
==============================================================================
Dep. Variable: RL R-squared: 0.974
Model: OLS Adj. R-squared: 0.970
Method: Least Squares F-statistic: 212.0
Date: Sun, 09 Feb 2025 Prob (F-statistic): 2.41e-21
Time: 14:21:37 Log-Likelihood: 4.5383
No. Observations: 34 AIC: 2.923
Df Residuals: 28 BIC: 12.08
Df Model: 5
Covariance Type: nonrobust
================================================================================
coef std err t P>|t| [0.025 0.975]
--------------------------------------------------------------------------------
Intercept 1.9593 1.028 1.906 0.067 -0.146 4.065
RL.shift(1) 0.7192 0.138 5.200 0.000 0.436 1.003
R3M 0.1662 0.025 6.673 0.000 0.115 0.217
R3M.shift(1) -0.0778 0.037 -2.127 0.042 -0.153 -0.003
RD 0.6596 0.191 3.450 0.002 0.268 1.051
RD.shift(1) -0.4898 0.247 -1.981 0.057 -0.996 0.017
==============================================================================
Omnibus: 2.342 Durbin-Watson: 1.600
Prob(Omnibus): 0.310 Jarque-Bera (JB): 1.374
Skew: -0.177 Prob(JB): 0.503
Kurtosis: 2.081 Cond. No. 787.
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
Code
b = lm_KM_dyn.params[1:]
# Multiplicadores de corto plazo
b[1].round(3) ; b[3].round(3)0.166
0.66
Code
# Multiplicadores de largo plazo
b_lr_R3M = (b[1]+b[2])/(1-b[0])
b_lr_R3M.round(3)0.315
Code
b_lr_RD = (b[3]+b[4])/(1-b[0])
b_lr_RD.round(3)0.605
Code
# Contrastes
H_0_b1 = '(R3M = 0)'
W_test = lm_KM_dyn.wald_test(H_0_b1, use_f = False)
W_test<class 'statsmodels.stats.contrast.ContrastResults'>
<Wald test (chi2): statistic=[[44.52791871]], p-value=2.507557561604631e-11, df_denom=1>
Code
H_0_b3 = '(RD = 0)'
W_test = lm_KM_dyn.wald_test(H_0_b3, use_f = False)
W_test<class 'statsmodels.stats.contrast.ContrastResults'>
<Wald test (chi2): statistic=[[11.90371726]], p-value=0.0005602203401182512, df_denom=1>
Caso multivariante (especificación VAR): Evolución temporal de tipos de cambio
En esta aplicación se estimará un modelo vectorial autorregresivo para explicar la evolución en el tiempo de las variaciones diarias en los tipos de cambio respecto al dólar de tres de las principales monedas al nivel mundial, el euro, el yen y la libra.
El vector \(\mathbf{z}\) que se usará para el estimar el modelo \(\mathbf{z}_{t} = \mathbf{A}_{0} + \mathbf{A}_{1}\mathbf{z}_{t - 1} + \mathbf{A}_{2}\mathbf{z}_{t - 2} + \ldots\mathbf{A}_{p}\mathbf{z}_{t - p} + \mathbf{u}_{t}\) está compuesto en este caso por los cambios porcentuales diarios de los tipos de cambio, \(\mathbf{z} = (rEurUsd, rUsdJpy, rGbpUsd)'\), donde \(EurUsd\), \(UsdJpy\) y \(GbpUsd\) son, respectivamente, los tipos de cambio de cierre diarios respecto al dólar del euro, el yen y la libra.
Code
# Lectura de librerías
library(tidyverse)
library(imputeTS)
library(FinTS)
library(vars)
library(rmgarch)
# Lectura de datos
# Descarga de las series de tipos de cambio EURUSD, USDJPY y GBPUSD
# de la página https://finance.yahoo.com/currencies
library(tseries)
EURUSD <- get.hist.quote("EURUSD=X", start = "2003-12-01", end = "2023-12-31")time series ends 2023-12-29
Code
USDJPY <- get.hist.quote("JPY=X", start = "2003-12-01", end = "2023-12-31")time series ends 2023-12-29
Code
GBPUSD <- get.hist.quote("GBPUSD=X", start = "2003-12-01", end = "2023-12-31")time series ends 2023-12-29
Code
XRATES_0 <- merge(EURUSD$Close, USDJPY$Close, GBPUSD$Close)
class(XRATES_0)[1] "zoo"
Code
autoplot(XRATES_0) + facet_free()Code
# Análisis de datos perdidos (missing data: NA)
statsNA(EURUSD$Close)[1] "Length of time series:"
[1] 5240
[1] "-------------------------"
[1] "Number of Missing Values:"
[1] 29
[1] "-------------------------"
[1] "Percentage of Missing Values:"
[1] "0.553%"
[1] "-------------------------"
[1] "Number of Gaps:"
[1] 15
[1] "-------------------------"
[1] "Average Gap Size:"
[1] 1.933333
[1] "-------------------------"
[1] "Stats for Bins"
[1] " Bin 1 (1310 values from 1 to 1310) : 24 NAs (1.83%)"
[1] " Bin 2 (1310 values from 1311 to 2620) : 2 NAs (0.153%)"
[1] " Bin 3 (1310 values from 2621 to 3930) : 2 NAs (0.153%)"
[1] " Bin 4 (1310 values from 3931 to 5240) : 1 NAs (0.0763%)"
[1] "-------------------------"
[1] "Longest NA gap (series of consecutive NAs)"
[1] "11 in a row"
[1] "-------------------------"
[1] "Most frequent gap size (series of consecutive NA series)"
[1] "1 NA in a row (occurring 13 times)"
[1] "-------------------------"
[1] "Gap size accounting for most NAs"
[1] "1 NA in a row (occurring 13 times, making up for overall 13 NAs)"
[1] "-------------------------"
[1] "Overview NA series"
[1] " 1 NA in a row: 13 times"
[1] " 5 NA in a row: 1 times"
[1] " 11 NA in a row: 1 times"
Code
ggplot_na_distribution(EURUSD$Close)Code
statsNA(USDJPY$Close)[1] "Length of time series:"
[1] 5240
[1] "-------------------------"
[1] "Number of Missing Values:"
[1] 29
[1] "-------------------------"
[1] "Percentage of Missing Values:"
[1] "0.553%"
[1] "-------------------------"
[1] "Number of Gaps:"
[1] 15
[1] "-------------------------"
[1] "Average Gap Size:"
[1] 1.933333
[1] "-------------------------"
[1] "Stats for Bins"
[1] " Bin 1 (1310 values from 1 to 1310) : 24 NAs (1.83%)"
[1] " Bin 2 (1310 values from 1311 to 2620) : 2 NAs (0.153%)"
[1] " Bin 3 (1310 values from 2621 to 3930) : 2 NAs (0.153%)"
[1] " Bin 4 (1310 values from 3931 to 5240) : 1 NAs (0.0763%)"
[1] "-------------------------"
[1] "Longest NA gap (series of consecutive NAs)"
[1] "11 in a row"
[1] "-------------------------"
[1] "Most frequent gap size (series of consecutive NA series)"
[1] "1 NA in a row (occurring 13 times)"
[1] "-------------------------"
[1] "Gap size accounting for most NAs"
[1] "1 NA in a row (occurring 13 times, making up for overall 13 NAs)"
[1] "-------------------------"
[1] "Overview NA series"
[1] " 1 NA in a row: 13 times"
[1] " 5 NA in a row: 1 times"
[1] " 11 NA in a row: 1 times"
Code
ggplot_na_distribution(USDJPY$Close)Code
statsNA(GBPUSD$Close)[1] "Length of time series:"
[1] 5240
[1] "-------------------------"
[1] "Number of Missing Values:"
[1] 17
[1] "-------------------------"
[1] "Percentage of Missing Values:"
[1] "0.324%"
[1] "-------------------------"
[1] "Number of Gaps:"
[1] 14
[1] "-------------------------"
[1] "Average Gap Size:"
[1] 1.214286
[1] "-------------------------"
[1] "Stats for Bins"
[1] " Bin 1 (1310 values from 1 to 1310) : 12 NAs (0.916%)"
[1] " Bin 2 (1310 values from 1311 to 2620) : 2 NAs (0.153%)"
[1] " Bin 3 (1310 values from 2621 to 3930) : 2 NAs (0.153%)"
[1] " Bin 4 (1310 values from 3931 to 5240) : 1 NAs (0.0763%)"
[1] "-------------------------"
[1] "Longest NA gap (series of consecutive NAs)"
[1] "3 in a row"
[1] "-------------------------"
[1] "Most frequent gap size (series of consecutive NA series)"
[1] "1 NA in a row (occurring 12 times)"
[1] "-------------------------"
[1] "Gap size accounting for most NAs"
[1] "1 NA in a row (occurring 12 times, making up for overall 12 NAs)"
[1] "-------------------------"
[1] "Overview NA series"
[1] " 1 NA in a row: 12 times"
[1] " 2 NA in a row: 1 times"
[1] " 3 NA in a row: 1 times"
Code
ggplot_na_distribution(GBPUSD$Close)Code
# Solución a la presencia de valores ausentes
# Reemplazo de NAs por valores interpolados linealmente
# (la opción na.spline usa splines cúbicos para la interpolación)
EurUsd <- na.approx(EURUSD$Close)
UsdJpy <- na.approx(USDJPY$Close)
GbpUsd <- na.approx(GBPUSD$Close)
XRATES <- merge(EurUsd, UsdJpy, GbpUsd)
autoplot(XRATES) + facet_free()Code
# NOTA: Se guardan los datos para el análisis en Python
# write.zoo(XRATES, "TIPOS_CAMB.CSV", index.name = "Date")
# Cálculo de las variables del modelo VAR:
# cambios porcentuales diarios (aproximación dif-log)
rEurUsd <- 100 * diff(log(EurUsd))
autoplot(rEurUsd)Code
rUsdJpy <- 100 * diff(log(UsdJpy))
autoplot(rUsdJpy)Code
rGbpUsd <- 100 * diff(log(GbpUsd))
autoplot(rGbpUsd)Code
# Agrupación de los datos en una matriz para el análisis VAR
VAR_data <- as.matrix(cbind(rEurUsd,rUsdJpy,rGbpUsd))
class(VAR_data)[1] "matrix" "array"
Code
plot.ts(VAR_data)Code
# Modelo VAR para las variables rEurUsd, rUsdJpy y rGbpUsd
# Librería vars (https://cran.r-project.org/web/packages/vars/)
# Elección del retardo óptimo (type = c("const", "trend", "both", "none"))
VARselect(VAR_data,lag.max = 5, type = "const")$selection
AIC(n) HQ(n) SC(n) FPE(n)
3 2 2 3
$criteria
1 2 3 4 5
AIC(n) -2.80306349 -2.82666306 -2.82771607 -2.82637410 -2.82671302
HQ(n) -2.79780201 -2.81745546 -2.81456236 -2.80927427 -2.80566707
SC(n) -2.78801665 -2.80033109 -2.79009897 -2.77747186 -2.76652565
FPE(n) 0.06062406 0.05921011 0.05914779 0.05922722 0.05920715
Code
# Estimación del modelo VAR(2)
VAR2 <- VAR(VAR_data, p=2)
summary(VAR2)
VAR Estimation Results:
=========================
Endogenous variables: rEurUsd, rUsdJpy, rGbpUsd
Deterministic variables: const
Sample size: 5237
Log Likelihood: -14867.524
Roots of the characteristic polynomial:
0.3972 0.3972 0.1218 0.1218 0.09244 0.07614
Call:
VAR(y = VAR_data, p = 2)
Estimation results for equation rEurUsd:
========================================
rEurUsd = rEurUsd.l1 + rUsdJpy.l1 + rGbpUsd.l1 + rEurUsd.l2 + rUsdJpy.l2 + rGbpUsd.l2 + const
Estimate Std. Error t value Pr(>|t|)
rEurUsd.l1 -0.2645415 0.0163622 -16.168 < 2e-16 ***
rUsdJpy.l1 -0.1086175 0.0135507 -8.016 1.34e-15 ***
rGbpUsd.l1 0.1285755 0.0191449 6.716 2.07e-11 ***
rEurUsd.l2 -0.1032622 0.0164544 -6.276 3.76e-10 ***
rUsdJpy.l2 -0.0862361 0.0134700 -6.402 1.67e-10 ***
rGbpUsd.l2 0.0212043 0.0191176 1.109 0.267
const -0.0004788 0.0095229 -0.050 0.960
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.689 on 5230 degrees of freedom
Multiple R-Squared: 0.066, Adjusted R-squared: 0.06492
F-statistic: 61.59 on 6 and 5230 DF, p-value: < 2.2e-16
Estimation results for equation rUsdJpy:
========================================
rUsdJpy = rEurUsd.l1 + rUsdJpy.l1 + rGbpUsd.l1 + rEurUsd.l2 + rUsdJpy.l2 + rGbpUsd.l2 + const
Estimate Std. Error t value Pr(>|t|)
rEurUsd.l1 -0.208080 0.016832 -12.362 < 2e-16 ***
rUsdJpy.l1 -0.179985 0.013940 -12.912 < 2e-16 ***
rGbpUsd.l1 0.085397 0.019694 4.336 1.48e-05 ***
rEurUsd.l2 -0.053506 0.016926 -3.161 0.00158 **
rUsdJpy.l2 -0.065954 0.013856 -4.760 1.99e-06 ***
rGbpUsd.l2 0.016067 0.019666 0.817 0.41396
const 0.006500 0.009796 0.664 0.50701
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.7088 on 5230 degrees of freedom
Multiple R-Squared: 0.0635, Adjusted R-squared: 0.06242
F-statistic: 59.1 on 6 and 5230 DF, p-value: < 2.2e-16
Estimation results for equation rGbpUsd:
========================================
rGbpUsd = rEurUsd.l1 + rUsdJpy.l1 + rGbpUsd.l1 + rEurUsd.l2 + rUsdJpy.l2 + rGbpUsd.l2 + const
Estimate Std. Error t value Pr(>|t|)
rEurUsd.l1 -0.015425 0.014215 -1.085 0.2779
rUsdJpy.l1 0.006271 0.011773 0.533 0.5943
rGbpUsd.l1 0.034416 0.016633 2.069 0.0386 *
rEurUsd.l2 -0.019181 0.014295 -1.342 0.1797
rUsdJpy.l2 -0.004401 0.011703 -0.376 0.7069
rGbpUsd.l2 0.008667 0.016609 0.522 0.6018
const -0.005646 0.008273 -0.682 0.4950
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.5986 on 5230 degrees of freedom
Multiple R-Squared: 0.001177, Adjusted R-squared: 3.078e-05
F-statistic: 1.027 on 6 and 5230 DF, p-value: 0.4055
Covariance matrix of residuals:
rEurUsd rUsdJpy rGbpUsd
rEurUsd 0.474785 -0.003627 0.22434
rUsdJpy -0.003627 0.502420 -0.05376
rGbpUsd 0.224338 -0.053761 0.35837
Correlation matrix of residuals:
rEurUsd rUsdJpy rGbpUsd
rEurUsd 1.000000 -0.007427 0.5439
rUsdJpy -0.007427 1.000000 -0.1267
rGbpUsd 0.543865 -0.126698 1.0000
Code
# Análisis de causalidad de Granger
causality(VAR2, cause = c("rGbpUsd","rUsdJpy"))$Granger
Granger causality H0: rUsdJpy rGbpUsd do not Granger-cause rEurUsd
data: VAR object VAR2
F-Test = 38.143, df1 = 4, df2 = 15690, p-value < 2.2e-16
Code
causality(VAR2, cause = c("rEurUsd","rUsdJpy"))$Granger
Granger causality H0: rEurUsd rUsdJpy do not Granger-cause rGbpUsd
data: VAR object VAR2
F-Test = 0.78936, df1 = 4, df2 = 15690, p-value = 0.5319
Code
causality(VAR2, cause = c("rEurUsd","rGbpUsd"))$Granger
Granger causality H0: rEurUsd rGbpUsd do not Granger-cause rUsdJpy
data: VAR object VAR2
F-Test = 40.419, df1 = 4, df2 = 15690, p-value < 2.2e-16
Code
# Funciones de respuesta al impulso (IRFs)
irf_VAR2 <- irf(VAR2, n.ahead = 5)
plot(irf_VAR2)Code
# Funciones de descomposición de la varianza (FEVDs)
fevd_VAR2 <- fevd(VAR2, n.ahead = 5)
plot(fevd_VAR2)Code
# ANEXO: Modelo VAR con estructura GARCH multivariante (MGARCH))
# Librería rmgarch (https://cran.r-project.org/web/packages/rmgarch/)
# Contraste ARCH para los residuos del modelo VAR(2)
ArchTest(VAR2$varresult$rEurUsd$residuals , lags = 1)
ARCH LM-test; Null hypothesis: no ARCH effects
data: VAR2$varresult$rEurUsd$residuals
Chi-squared = 434.21, df = 1, p-value < 2.2e-16
Code
ArchTest(VAR2$varresult$rGbpUsd$residuals , lags = 1)
ARCH LM-test; Null hypothesis: no ARCH effects
data: VAR2$varresult$rGbpUsd$residuals
Chi-squared = 54.996, df = 1, p-value = 1.208e-13
Code
ArchTest(VAR2$varresult$rUsdJpy$residuals , lags = 1)
ARCH LM-test; Null hypothesis: no ARCH effects
data: VAR2$varresult$rUsdJpy$residuals
Chi-squared = 745.32, df = 1, p-value < 2.2e-16
Code
# Modelo VAR con correlación condicional (GARCH-Copula model)
VAR_GARCH_spec <- ugarchspec(
variance.model = list (garchOrder =c(1,1), model = "sGARCH") )
Multi_spec <- multispec ( replicate (3 , VAR_GARCH_spec) )
Copula_GARCH_spec <- cgarchspec (Multi_spec, VAR = TRUE, lag = 2)
VAR_GARCH_fit <- cgarchfit ( Copula_GARCH_spec , data = VAR_data )
show(VAR_GARCH_fit)
*-------------------------------------------------*
* Copula GARCH Fit *
*-------------------------------------------------*
Distribution : mvnorm
No. of Parameters : 30
[VAR GARCH CC] : [21+9+0]
No. of Series : 3
No. of Observations : 5239
Log-Likelihood : -12013.46
Av.Log-Likelihood : -2.293
Optimal Parameters
---------------------------------------------------
Estimate Std. Error t value Pr(>|t|)
[rEurUsd].omega 0.001137 0.000667 1.7043 0.088327
[rEurUsd].alpha1 0.041876 0.003549 11.7979 0.000000
[rEurUsd].beta1 0.957124 0.001271 753.2873 0.000000
[rUsdJpy].omega 0.003261 0.002539 1.2844 0.199017
[rUsdJpy].alpha1 0.065382 0.019181 3.4087 0.000653
[rUsdJpy].beta1 0.931255 0.023867 39.0192 0.000000
[rGbpUsd].omega 0.004389 0.003261 1.3460 0.178315
[rGbpUsd].alpha1 0.057312 0.027181 2.1085 0.034985
[rGbpUsd].beta1 0.931312 0.033560 27.7507 0.000000
Information Criteria
---------------------
Akaike 4.5896
Bayes 4.6009
Shibata 4.5896
Hannan-Quinn 4.5935
Elapsed time : 4.17373
Code
# Correlaciones condicionales entre los residuos estandarizados
VAR_GARCH_fit@mfit$Rt [,1] [,2] [,3]
[1,] 1.0000000 -0.2838862 0.6227347
[2,] -0.2838862 1.0000000 -0.2192316
[3,] 0.6227347 -0.2192316 1.0000000
Code
# Lectura de librerías
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import statsmodels.tsa.api as smt
# Lectura de datos
XRATES = pd.read_csv("data/TIPOS_CAMB.csv", delimiter = ' ', index_col = 0)
def LogDiff(x):
x_diff = 100*np.log(x/x.shift(1))
x_diff = x_diff.dropna()
return x_diff
VAR_data = pd.DataFrame({'rEurUsd':LogDiff(XRATES['EurUsd']),
'rUsdJpy':LogDiff(XRATES['UsdJpy']),
'rGbpUsd':LogDiff(XRATES['GbpUsd'])})
# Modelo VAR
model = smt.VAR(VAR_data)
VARselect = model.select_order(maxlags=5)
print(VARselect.summary()) VAR Order Selection (* highlights the minimums)
=================================================
AIC BIC FPE HQIC
-------------------------------------------------
0 -2.688 -2.684 0.06805 -2.686
1 -2.803 -2.788 0.06062 -2.798
2 -2.827 -2.800* 0.05921 -2.817*
3 -2.828* -2.790 0.05915* -2.815
4 -2.826 -2.777 0.05923 -2.809
5 -2.827 -2.767 0.05921 -2.806
-------------------------------------------------
Code
VAR2 = model.fit(maxlags=2)
print(VAR2.summary()) Summary of Regression Results
==================================
Model: VAR
Method: OLS
Date: Sun, 09, Feb, 2025
Time: 14:21:58
--------------------------------------------------------------------
No. of Equations: 3.00000 BIC: -2.80141
Nobs: 5237.00 HQIC: -2.81853
Log likelihood: -14867.5 FPE: 0.0591468
AIC: -2.82773 Det(Omega_mle): 0.0589102
--------------------------------------------------------------------
Results for equation rEurUsd
=============================================================================
coefficient std. error t-stat prob
-----------------------------------------------------------------------------
const -0.000479 0.009523 -0.050 0.960
L1.rEurUsd -0.264542 0.016362 -16.168 0.000
L1.rUsdJpy -0.108617 0.013551 -8.016 0.000
L1.rGbpUsd 0.128576 0.019145 6.716 0.000
L2.rEurUsd -0.103262 0.016454 -6.276 0.000
L2.rUsdJpy -0.086236 0.013470 -6.402 0.000
L2.rGbpUsd 0.021204 0.019118 1.109 0.267
=============================================================================
Results for equation rUsdJpy
=============================================================================
coefficient std. error t-stat prob
-----------------------------------------------------------------------------
const 0.006500 0.009796 0.664 0.507
L1.rEurUsd -0.208080 0.016832 -12.362 0.000
L1.rUsdJpy -0.179985 0.013940 -12.912 0.000
L1.rGbpUsd 0.085397 0.019694 4.336 0.000
L2.rEurUsd -0.053506 0.016926 -3.161 0.002
L2.rUsdJpy -0.065954 0.013856 -4.760 0.000
L2.rGbpUsd 0.016067 0.019666 0.817 0.414
=============================================================================
Results for equation rGbpUsd
=============================================================================
coefficient std. error t-stat prob
-----------------------------------------------------------------------------
const -0.005646 0.008273 -0.682 0.495
L1.rEurUsd -0.015425 0.014215 -1.085 0.278
L1.rUsdJpy 0.006271 0.011773 0.533 0.594
L1.rGbpUsd 0.034416 0.016633 2.069 0.039
L2.rEurUsd -0.019181 0.014295 -1.342 0.180
L2.rUsdJpy -0.004401 0.011703 -0.376 0.707
L2.rGbpUsd 0.008667 0.016609 0.522 0.602
=============================================================================
Correlation matrix of residuals
rEurUsd rUsdJpy rGbpUsd
rEurUsd 1.000000 -0.007427 0.543865
rUsdJpy -0.007427 1.000000 -0.126698
rGbpUsd 0.543865 -0.126698 1.000000
Code
# Funciones de respuesta al impulso y de descomposición de la varianza
irf_VAR2 = VAR2.irf(5)
plt.figure(5)
fig = irf_VAR2.plot()
plt.show()Code
fevd_VAR2 = VAR2.fevd(5)
plt.figure(6)
fig = fevd_VAR2.plot()
plt.show()