Skip to content

Commit 98a2bfa

Browse files
authored
Merge pull request #673 from bashtage/bug-figarch-forecast
BUG: Correct multistep forecasting for FIGARCH
2 parents 6bf7e77 + 647dea2 commit 98a2bfa

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

arch/tests/univariate/test_forecast.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,27 @@
4949
EGARCH(),
5050
]
5151

52+
ANALYTICAL_VOLATILITIES = [
53+
ConstantVariance(),
54+
GARCH(),
55+
FIGARCH(),
56+
EWMAVariance(lam=0.94),
57+
MIDASHyperbolic(),
58+
HARCH(lags=[1, 5, 22]),
59+
RiskMetrics2006(),
60+
]
61+
62+
5263
MODEL_SPECS = list(product(MEAN_MODELS, VOLATILITIES))
64+
ANALYTICAL_MODEL_SPECS = list(product(MEAN_MODELS, ANALYTICAL_VOLATILITIES))
5365

5466
IDS = [
5567
f"{str(mean).split('(')[0]}-{str(vol).split('(')[0]}" for mean, vol in MODEL_SPECS
5668
]
69+
ANALYTICAL_IDS = [
70+
f"{str(mean).split('(')[0]}-{str(vol).split('(')[0]}"
71+
for mean, vol in ANALYTICAL_MODEL_SPECS
72+
]
5773

5874

5975
@pytest.fixture(params=MODEL_SPECS, ids=IDS)
@@ -63,6 +79,13 @@ def model_spec(request):
6379
return mean
6480

6581

82+
@pytest.fixture(params=ANALYTICAL_MODEL_SPECS, ids=ANALYTICAL_IDS)
83+
def analytical_model_spec(request):
84+
mean, vol = request.param
85+
mean.volatility = vol
86+
return mean
87+
88+
6689
class TestForecasting:
6790
@classmethod
6891
def setup_class(cls):
@@ -1053,3 +1076,31 @@ def test_rescale_ar():
10531076
fcasts = res.forecast(horizon=100).variance
10541077
fcasts_no_rs = res_no_rs.forecast(horizon=100).variance
10551078
assert_allclose(fcasts.iloc[0, -10:], fcasts_no_rs.iloc[0, -10:], rtol=1e-5)
1079+
1080+
1081+
def test_figarch_multistep():
1082+
# GH 670
1083+
mod = ConstantMean(SP500, volatility=FIGARCH())
1084+
res = mod.fit(disp="off")
1085+
fcasts = res.forecast(horizon=10)
1086+
rv = fcasts.residual_variance
1087+
assert np.all(np.isfinite(rv))
1088+
assert rv.shape == (1, 10)
1089+
fcasts_ri = res.forecast(horizon=10, reindex=True)
1090+
rv_ri = fcasts_ri.residual_variance
1091+
assert_frame_equal(rv, rv_ri.iloc[-1:])
1092+
assert rv_ri.shape == (SP500.shape[0], 10)
1093+
1094+
1095+
def test_multistep(analytical_model_spec):
1096+
# GH 670
1097+
# Ensure all work as expected
1098+
res = analytical_model_spec.fit(disp="off")
1099+
fcasts = res.forecast(horizon=10)
1100+
rv = fcasts.residual_variance
1101+
assert np.all(np.isfinite(rv))
1102+
assert rv.shape == (1, 10)
1103+
fcasts_ri = res.forecast(horizon=10, reindex=True)
1104+
rv_ri = fcasts_ri.residual_variance
1105+
assert_frame_equal(rv, rv_ri.iloc[-1:])
1106+
assert rv_ri.shape == (SP500.shape[0], 10)

arch/univariate/volatility.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3235,6 +3235,7 @@ def _analytic_forecast(
32353235
temp_forecasts = np.empty(truncation + horizon)
32363236
resids2 = resids**2
32373237
for i in range(start, t):
3238+
fcast_loc = i - start
32383239
available = i + 1 - max(0, i - truncation + 1)
32393240
temp_forecasts[truncation - available : truncation] = resids2[
32403241
max(0, i - truncation + 1) : i + 1
@@ -3246,9 +3247,8 @@ def _analytic_forecast(
32463247
temp_forecasts[truncation + h] = omega_tilde + lam_rev.dot(
32473248
lagged_forecasts
32483249
)
3249-
forecasts[i, :] = temp_forecasts[truncation:]
3250+
forecasts[fcast_loc, :] = temp_forecasts[truncation:]
32503251

3251-
forecasts[:start] = np.nan
32523252
return VarianceForecast(forecasts)
32533253

32543254
def _simulation_forecast(

0 commit comments

Comments
 (0)