1
1
from arch .compat .pandas import MONTH_END
2
2
3
3
from io import StringIO
4
+ import itertools
4
5
from itertools import product
5
6
from string import ascii_lowercase
6
7
import struct
26
27
27
28
from arch .data import sp500
28
29
from arch .typing import Literal
29
- from arch .univariate .base import ARCHModelForecast , ARCHModelResult , _align_forecast
30
+ from arch .univariate .base import (
31
+ ARCHModel ,
32
+ ARCHModelForecast ,
33
+ ARCHModelResult ,
34
+ _align_forecast ,
35
+ )
30
36
from arch .univariate .distribution import (
31
37
GeneralizedError ,
32
38
Normal ,
46
52
FixedVariance ,
47
53
MIDASHyperbolic ,
48
54
RiskMetrics2006 ,
55
+ VolatilityProcess ,
49
56
)
50
57
from arch .utility .exceptions import ConvergenceWarning , DataScaleWarning
51
58
73
80
DISPLAY : Literal ["off" , "final" ] = "off"
74
81
UPDATE_FREQ = 0 if DISPLAY == "off" else 3
75
82
SP500 = 100 * sp500 .load ()["Adj Close" ].pct_change ().dropna ()
83
+ rs = np .random .RandomState (20241029 )
84
+ X = SP500 * 0.01 + SP500 .std () * rs .standard_normal (SP500 .shape )
76
85
77
86
78
87
@pytest .fixture (scope = "module" , params = [True , False ])
@@ -83,6 +92,73 @@ def simulated_data(request):
83
92
return np .asarray (sim_data .data ) if request .param else sim_data .data
84
93
85
94
95
+ simple_mean_models = [
96
+ ARX (SP500 , lags = 1 ),
97
+ HARX (SP500 , lags = [1 , 5 ]),
98
+ ConstantMean (SP500 ),
99
+ ZeroMean (SP500 ),
100
+ ]
101
+
102
+ mean_models = [
103
+ ARX (SP500 , x = X , lags = 1 ),
104
+ HARX (SP500 , x = X , lags = [1 , 5 ]),
105
+ LS (SP500 , X ),
106
+ ] + simple_mean_models
107
+
108
+ analytic_volatility_processes = [
109
+ ARCH (3 ),
110
+ FIGARCH (1 , 1 ),
111
+ GARCH (1 , 1 , 1 ),
112
+ HARCH ([1 , 5 , 22 ]),
113
+ ConstantVariance (),
114
+ EWMAVariance (0.94 ),
115
+ FixedVariance (np .full_like (SP500 , SP500 .var ())),
116
+ MIDASHyperbolic (),
117
+ RiskMetrics2006 (),
118
+ ]
119
+
120
+ other_volatility_processes = [
121
+ APARCH (1 , 1 , 1 , 1.5 ),
122
+ EGARCH (1 , 1 , 1 ),
123
+ ]
124
+
125
+ volatility_processes = analytic_volatility_processes + other_volatility_processes
126
+
127
+
128
+ @pytest .fixture (
129
+ scope = "module" ,
130
+ params = list (itertools .product (simple_mean_models , analytic_volatility_processes )),
131
+ ids = [
132
+ f"{ a .__class__ .__name__ } -{ b } "
133
+ for a , b in itertools .product (simple_mean_models , analytic_volatility_processes )
134
+ ],
135
+ )
136
+ def forecastable_model (request ):
137
+ mod : ARCHModel
138
+ vol : VolatilityProcess
139
+ mod , vol = request .param
140
+ mod .volatility = vol
141
+ res = mod .fit ()
142
+ return res , mod .fix (res .params )
143
+
144
+
145
+ @pytest .fixture (
146
+ scope = "module" ,
147
+ params = list (itertools .product (mean_models , volatility_processes )),
148
+ ids = [
149
+ f"{ a .__class__ .__name__ } -{ b } "
150
+ for a , b in itertools .product (mean_models , volatility_processes )
151
+ ],
152
+ )
153
+ def fit_fixed_models (request ):
154
+ mod : ARCHModel
155
+ vol : VolatilityProcess
156
+ mod , vol = request .param
157
+ mod .volatility = vol
158
+ res = mod .fit ()
159
+ return res , mod .fix (res .params )
160
+
161
+
86
162
class TestMeanModel :
87
163
@classmethod
88
164
def setup_class (cls ):
@@ -1370,3 +1446,58 @@ def test_non_contiguous_input(use_numpy):
1370
1446
mod = arch_model (y , mean = "Zero" )
1371
1447
res = mod .fit ()
1372
1448
assert res .params .shape [0 ] == 3
1449
+
1450
+
1451
+ def test_fixed_equivalence (fit_fixed_models ):
1452
+ res , res_fixed = fit_fixed_models
1453
+
1454
+ assert_allclose (res .aic , res_fixed .aic )
1455
+ assert_allclose (res .bic , res_fixed .bic )
1456
+ assert_allclose (res .loglikelihood , res_fixed .loglikelihood )
1457
+ assert res .nobs == res_fixed .nobs
1458
+ assert res .num_params == res_fixed .num_params
1459
+ assert_allclose (res .params , res_fixed .params )
1460
+ assert_allclose (res .conditional_volatility , res_fixed .conditional_volatility )
1461
+ assert_allclose (res .std_resid , res_fixed .std_resid )
1462
+ assert_allclose (res .resid , res_fixed .resid )
1463
+ assert_allclose (res .arch_lm_test (5 ).stat , res_fixed .arch_lm_test (5 ).stat )
1464
+ assert res .model .__class__ is res_fixed .model .__class__
1465
+ assert res .model .volatility .__class__ is res_fixed .model .volatility .__class__
1466
+ assert isinstance (res .summary (), type (res_fixed .summary ()))
1467
+ if res .num_params > 0 :
1468
+ assert "std err" in str (res .summary ())
1469
+ assert "std err" not in str (res_fixed .summary ())
1470
+
1471
+
1472
+ @pytest .mark .skipif (not HAS_MATPLOTLIB , reason = "matplotlib not installed" )
1473
+ def test_fixed_equivalence_plots (fit_fixed_models ):
1474
+ res , res_fixed = fit_fixed_models
1475
+
1476
+ fig = res .plot ()
1477
+ fixed_fig = res_fixed .plot ()
1478
+ assert isinstance (fig , type (fixed_fig ))
1479
+
1480
+ import matplotlib .pyplot as plt
1481
+
1482
+ plt .close ("all" )
1483
+
1484
+
1485
+ @pytest .mark .parametrize ("simulations" , [1 , 100 ])
1486
+ def test_fixed_equivalence_forecastable (forecastable_model , simulations ):
1487
+ res , res_fixed = forecastable_model
1488
+ f1 = res .forecast (horizon = 5 )
1489
+ f2 = res_fixed .forecast (horizon = 5 )
1490
+ assert isinstance (f1 , type (f2 ))
1491
+ assert_allclose (f1 .mean , f2 .mean )
1492
+ assert_allclose (f1 .variance , f2 .variance )
1493
+ fig1 = res .hedgehog_plot (start = SP500 .shape [0 ] - 25 )
1494
+ fig2 = res_fixed .hedgehog_plot (start = SP500 .shape [0 ] - 25 )
1495
+ assert isinstance (fig1 , type (fig2 ))
1496
+ plt .close ("all" )
1497
+
1498
+ f1 = res .forecast (horizon = 5 , method = "simulation" , simulations = simulations )
1499
+ f2 = res_fixed .forecast (horizon = 5 , method = "simulation" , simulations = simulations )
1500
+ assert isinstance (f1 , type (f2 ))
1501
+ f1 = res .forecast (horizon = 5 , method = "bootstrap" , simulations = simulations )
1502
+ f2 = res_fixed .forecast (horizon = 5 , method = "bootstrap" , simulations = simulations )
1503
+ assert isinstance (f1 , type (f2 ))
0 commit comments