1
1
from typing import Dict , NamedTuple , Optional , Tuple
2
2
3
3
import numpy as np
4
- from numpy import zeros
5
4
from numpy .linalg import lstsq
6
5
import pandas as pd
6
+ from pandas .util ._decorators import Appender
7
7
from statsmodels .tools import add_constant
8
8
from statsmodels .tsa .tsatools import lagmat
9
9
14
14
normalize_kernel_name ,
15
15
)
16
16
from arch .typing import ArrayLike , NDArray
17
+ from arch .vendor import cached_property
18
+
19
+ __all__ = ["PreWhitenedRecolored" ]
17
20
18
21
19
22
class VARModel (NamedTuple ):
@@ -23,48 +26,98 @@ class VARModel(NamedTuple):
23
26
intercept : bool
24
27
25
28
26
- class PreWhitenRecoloredCovariance (CovarianceEstimator ):
29
+ class PreWhitenedRecolored (CovarianceEstimator ):
27
30
"""
31
+ VAR-HAC and Pre-Whitened-Recolored Long-run covariance estimation.
32
+
33
+ Andrews & Monahan [1]_ PWRC and DenHaan-Levin's VAR-HAC [2]_ covariance
34
+ estimators.
35
+
28
36
Parameters
29
37
----------
30
38
x : array_like
39
+ The data to use in covariance estimation.
31
40
lags : int, default None
41
+ The number of lags to include in the VAR. If None, a specification
42
+ search is used to select the order.
32
43
method : {"aic", "hqc", "bic"}, default "aic"
44
+ The information criteria to use in the model specification search.
33
45
diagonal : bool, default True
46
+ Flag indicating to consider both diagonal parameter coefficient
47
+ matrices on lags. A diagonal coefficient matrix restricts all
48
+ off-diagonal coefficient to be zero.
34
49
max_lag : int, default None
50
+ The maximum lag to use in the model specification search. If None,
51
+ then nobs**(1/3) is used.
35
52
sample_autocov : bool, default False
36
- kernel : str, default "bartlett"
53
+ Whether to the the same autocovariance or the theoretical
54
+ autocovariance implied by the estimated VAR when computing
55
+ the long-run covairance.
56
+ kernel : {str, None}, default "bartlett".
57
+ The name of the kernel to use. Can be any available kernel. Input
58
+ is normalised using lower casing and any underscores or hyphens
59
+ are removed, so that "QuadraticSpectral", "quadratic-spectral" and
60
+ "quadratic_spectral" are all the same. Use None to prevent recoloring.
37
61
bandwidth : float, default None
62
+ The kernel's bandwidth. If None, optimal bandwidth is estimated.
38
63
df_adjust : int, default 0
64
+ Degrees of freedom to remove when adjusting the covariance. Uses the
65
+ number of observations in x minus df_adjust when dividing
66
+ inner-products.
39
67
center : bool, default True
68
+ A flag indicating whether x should be demeaned before estimating the
69
+ covariance.
40
70
weights : array_like, default None
71
+ An array of weights used to combine when estimating optimal bandwidth.
72
+ If not provided, a vector of 1s is used. Must have nvar elements.
73
+ force_int : bool, default False
74
+ Force bandwidth to be an integer.
41
75
42
76
See Also
43
77
--------
78
+ arch.covariance.kernel
79
+ Kernel-based long-run covariance estimators
44
80
45
81
Notes
46
82
-----
83
+ TODO: Add detailed notes
47
84
48
85
Examples
49
86
--------
87
+
88
+ References
89
+ ----------
90
+ .. [1] Andrews, D. W., & Monahan, J. C. (1992). An improved
91
+ heteroskedasticity and autocorrelation consistent covariance matrix
92
+ estimator. Econometrica: Journal of the Econometric Society, 953-966.
93
+ .. [2] Haan, W. J. D., & Levin, A. T. (2000). Robust covariance matrix
94
+ estimation with data-dependent VAR prewhitening order (No. 255).
95
+ National Bureau of Economic Research.
50
96
"""
51
97
52
98
def __init__ (
53
99
self ,
54
100
x : ArrayLike ,
101
+ * ,
55
102
lags : Optional [int ] = None ,
56
103
method : str = "aic" ,
57
104
diagonal : bool = True ,
58
105
max_lag : Optional [int ] = None ,
59
106
sample_autocov : bool = False ,
60
- kernel : str = "bartlett" ,
107
+ kernel : Optional [ str ] = "bartlett" ,
61
108
bandwidth : Optional [float ] = None ,
62
109
df_adjust : int = 0 ,
63
110
center : bool = True ,
64
111
weights : Optional [ArrayLike ] = None ,
112
+ force_int : bool = False ,
65
113
) -> None :
66
114
super ().__init__ (
67
- x , bandwidth = bandwidth , df_adjust = df_adjust , center = center , weights = weights
115
+ x ,
116
+ bandwidth = bandwidth ,
117
+ df_adjust = df_adjust ,
118
+ center = center ,
119
+ weights = weights ,
120
+ force_int = force_int ,
68
121
)
69
122
self ._kernel_name = kernel
70
123
self ._lags = 0
@@ -75,7 +128,13 @@ def __init__(
75
128
self ._auto_lag_selection = True
76
129
self ._format_lags (lags )
77
130
self ._sample_autocov = sample_autocov
78
- kernel = normalize_kernel_name (kernel )
131
+ if kernel is not None :
132
+ kernel = normalize_kernel_name (kernel )
133
+ else :
134
+ if self ._bandwidth not in (0 , None ):
135
+ raise ValueError ("bandwidth must be None when kernel is None" )
136
+ self ._bandwidth = None
137
+ kernel = "zerolag"
79
138
if kernel not in KERNEL_ESTIMATORS :
80
139
raise ValueError (KERNEL_ERR )
81
140
@@ -155,7 +214,7 @@ def _ic_from_vars(
155
214
ics : Dict [Tuple [int , int ], float ] = {
156
215
(full_order , full_order ): self ._ic (sigma , nparam , nobs )
157
216
}
158
- if not self ._diagonal :
217
+ if not self ._diagonal or self . _x . shape [ 1 ] == 1 :
159
218
return ics
160
219
161
220
purged_indiv_lags = np .empty ((nvar , nobs , max_lag - full_order ))
@@ -206,15 +265,15 @@ def _select_lags(self) -> Tuple[int, int]:
206
265
return models [ic .argmin ()]
207
266
208
267
def _estimate_var (self , full_order : int , diag_order : int ) -> VARModel :
209
- nobs , nvar = self ._x .shape
268
+ nvar = self ._x .shape [ 1 ]
210
269
center = int (self ._center )
211
270
max_lag = max (full_order , diag_order )
212
271
lhs , rhs , extra_lags = self ._setup_model_data (max_lag )
213
272
c = int (self ._center )
214
273
rhs = rhs [:, : c + full_order * nvar ]
215
274
extra_lags = extra_lags [:, :, full_order :diag_order ]
216
275
217
- params = zeros ((nvar , nvar * max_lag + center ))
276
+ params = np . zeros ((nvar , nvar * max_lag + center ))
218
277
resids = np .empty_like (lhs )
219
278
ncommon = rhs .shape [1 ]
220
279
for i in range (nvar ):
@@ -246,8 +305,8 @@ def _estimate_sample_cov(self, nvar: int, nlag: int) -> NDArray:
246
305
if self ._center :
247
306
x = x - x .mean (0 )
248
307
nobs = x .shape [0 ]
249
- var_cov = zeros ((nvar * nlag , nvar * nlag ))
250
- gamma = zeros ((nlag , nvar , nvar ))
308
+ var_cov = np . zeros ((nvar * nlag , nvar * nlag ))
309
+ gamma = np . zeros ((nlag , nvar , nvar ))
251
310
for i in range (nlag ):
252
311
gamma [i ] = (x [i :].T @ x [: (nobs - i )]) / nobs
253
312
for r in range (nlag ):
@@ -258,10 +317,11 @@ def _estimate_sample_cov(self, nvar: int, nlag: int) -> NDArray:
258
317
var_cov [r * nvar : (r + 1 ) * nvar , c * nvar : (c + 1 ) * nvar ] = g
259
318
return var_cov
260
319
320
+ @staticmethod
261
321
def _estimate_model_cov (
262
- self , nvar : int , nlag : int , coeffs : NDArray , short_run : NDArray
322
+ nvar : int , nlag : int , coeffs : NDArray , short_run : NDArray
263
323
) -> NDArray :
264
- sigma = zeros ((nvar * nlag , nvar * nlag ))
324
+ sigma = np . zeros ((nvar * nlag , nvar * nlag ))
265
325
sigma [:nvar , :nvar ] = short_run
266
326
multiplier = np .linalg .inv (np .eye (coeffs .size ) - np .kron (coeffs , coeffs ))
267
327
vec_sigma = sigma .ravel ()[:, None ]
@@ -274,7 +334,7 @@ def _companion_form(
274
334
) -> Tuple [NDArray , NDArray ]:
275
335
nvar = var_model .resids .shape [1 ]
276
336
nlag = var_model .var_order
277
- coeffs = zeros ((nvar * nlag , nvar * nlag ))
337
+ coeffs = np . zeros ((nvar * nlag , nvar * nlag ))
278
338
coeffs [:nvar ] = var_model .params [:, var_model .intercept :]
279
339
for i in range (nlag - 1 ):
280
340
coeffs [(i + 1 ) * nvar : (i + 2 ) * nvar , i * nvar : (i + 1 ) * nvar ] = np .eye (
@@ -286,15 +346,21 @@ def _companion_form(
286
346
var_cov = self ._estimate_model_cov (nvar , nlag , coeffs , short_run )
287
347
return coeffs , var_cov
288
348
289
- @property
349
+ @cached_property
350
+ @Appender (CovarianceEstimator .cov .__doc__ )
290
351
def cov (self ) -> CovarianceEstimate :
291
352
common , individual = self ._select_lags ()
292
353
self ._order = (common , individual )
293
354
var_mod = self ._estimate_var (common , individual )
294
355
resids = var_mod .resids
295
356
nobs , nvar = resids .shape
296
357
self ._kernel_instance = self ._kernel (
297
- resids , self ._bandwidth , 0 , False , self ._x_weights , self ._force_int
358
+ resids ,
359
+ bandwidth = self ._bandwidth ,
360
+ df_adjust = 0 ,
361
+ center = False ,
362
+ weights = self ._x_weights ,
363
+ force_int = self ._force_int ,
298
364
)
299
365
kern_cov = self ._kernel_instance .cov
300
366
short_run = kern_cov .short_run
@@ -316,7 +382,7 @@ def cov(self) -> CovarianceEstimate:
316
382
have diagonal coefficient matrices. The maximum eigenvalue of the companion-form \
317
383
VAR(1) coefficient matrix is { max_eig } ."""
318
384
)
319
- coeff_sum = zeros ((nvar , nvar ))
385
+ coeff_sum = np . zeros ((nvar , nvar ))
320
386
params = var_mod .params [:, var_mod .intercept :]
321
387
for i in range (var_mod .var_order ):
322
388
coeff_sum += params [:, i * nvar : (i + 1 ) * nvar ]
@@ -343,7 +409,8 @@ def cov(self) -> CovarianceEstimate:
343
409
344
410
def _ensure_kernel_instantized (self ) -> None :
345
411
if self ._kernel_instance is None :
346
- self .cov
412
+ # Workaround to avoid linting noise
413
+ getattr (self , "cov" )
347
414
348
415
@property
349
416
def bandwidth_scale (self ) -> float :
0 commit comments