Skip to content

fix(garch): use a zero point forecast for GARCH and ARCH - #1291

Open
AnthonyOlakangil wants to merge 1 commit into
Nixtla:mainfrom
AnthonyOlakangil:fix-1280-garch-zero-mean
Open

AnthonyOlakangil wants to merge 1 commit into
Nixtla:mainfrom
AnthonyOlakangil:fix-1280-garch-zero-mean

Conversation

@AnthonyOlakangil

@AnthonyOlakangil AnthonyOlakangil commented Oct 15, 2026 •

Copy link
Copy Markdown

Closes #1280

Problem

GARCH and ARCH return a random draw as the point forecast, and the prediction intervals are built around it. garch_forecast simulates a path (yhat = error * sqrt(sigma2hat) with error ~ N(0, 1)) and returns that path as mean, and GARCH.predict / GARCH.forecast then set lo/hi = mean -/+ z * sqrt(sigma2).

Both models are zero-mean (y_t = eps_t * sigma_t), so the forecast of the conditional mean is 0 at every horizon. Because garch_forecast also calls np.random.seed(1), the first step is always the same draw, about +1.62 * sigma, so the bands are shifted rather than just noisy.

Change

garch_forecast returns np.zeros(h) for mean. sigma2 and fitted are unchanged, so the half-width of the intervals is the same as before and they are now centred on 0.

The sigma2 recursion still uses the simulated path for future squared values. That is what #1264 fixes, and the two changes are independent.

How tested

  • Regression tests: test_garch_forecast in tests/test_garch.py now checks that mean is all zeros, and a new TestGARCH::test_garch_intervals_centred_on_zero in tests/test_models.py checks the mean and that the 95% band is symmetric around 0. Both fail on main and pass with this change.
  • pytest tests/test_garch.py tests/test_models.py::TestGARCH tests/test_models.py::TestARCH: 18 passed.
  • pre-commit run (ruff, mypy) on the changed files passes.

Repro from the issue (y = rng.standard_normal(500) * 10, h=5):

before: GARCH mean: [ 16.5331  -6.2267  -5.3759 -10.921    8.8084]
        ARCH  mean: [ 16.4802  -6.2067  -5.3587 -10.8861   8.7802]
after:  GARCH mean: [0. 0. 0. 0. 0.]
        ARCH  mean: [0. 0. 0. 0. 0.]

One-step-ahead coverage of the 95% interval on 1000 simulated GARCH(1,1) series (w=0.5, alpha=0.1, beta=0.8, length 500, fitted with GARCH(1, 1)):

before: 0.637
after:  0.951

garch_forecast returned the simulated path y_vals as the mean, so the
point forecast and the centre of every prediction interval were a single
draw of the noise. GARCH/ARCH are zero-mean, so the forecast of the
conditional mean is 0 at every horizon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Models] GARCH/ARCH point forecasts are a random draw, and the level intervals are centred on it

2 participants