mincer_zarnowitz#

cultivars.forecast.comparison.mincer_zarnowitz(realized, forecast, *, horizon=1)[source]#

Mincer-Zarnowitz efficiency regression of outcomes on point forecasts.

\[y_t = a + b\, f_t + u_t, \qquad H_0 : (a, b) = (0, 1),\]

the forecast is unbiased and cannot be improved by a linear recalibration of itself. Multi-step errors overlap, so the Wald covariance is HAC with Bartlett weights through horizon - 1 lags; at those horizons the test over-rejects in short windows (about 13% at nominal 5% for four steps on 100 origins), and the record says so.

Parameters:
  • realized (ArrayLike) – (T,) outcomes.

  • forecast (ArrayLike) – (T,) point forecasts, aligned origin by origin – BacktestResult.point[:, h - 1, j] against BacktestResult.realized[:, h - 1, j].

  • horizon (int) – The forecast horizon behind the series.

Return type:

MincerZarnowitzTest

Returns:

The MincerZarnowitzTest.

Raises:

Note

A constant forecast has no slope to estimate and is refused as singular rather than reported as inefficient. The regression says nothing about a rival; a forecast can pass it and still lose every comparison, and fail it while winning them all, so read the verdict as “could this forecast be recalibrated” and not as “is this forecast good”.

See also

  • MincerZarnowitzTest – the record, with the estimates and the fit.

  • encompassing() – whether a rival would earn weight in a combination, the other reading of “could be improved”.

  • Calibration – the density-forecast analogue.

References

Mincer, J. A., & Zarnowitz, V. (1969). The evaluation of economic forecasts. In J. A. Mincer (Ed.), Economic Forecasts and Expectations (pp. 3-46). NBER.

Example

A forecast equal to the conditional mean passes; the same forecast reported at twice its scale overreacts and fails:

>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> f = rng.standard_normal(200)
>>> verdict = mincer_zarnowitz(f + 0.5 * rng.standard_normal(200), f)
>>> round(verdict.slope, 1), bool(verdict.pvalue > 0.05)
(1.0, True)
>>> overreacting = mincer_zarnowitz(0.5 * f + 0.5 * rng.standard_normal(200), f)
>>> round(overreacting.slope, 1), bool(overreacting.pvalue < 0.01)
(0.5, True)
>>> mincer_zarnowitz(f, np.ones(200))
Traceback (most recent call last):
    ...
cultivars.exceptions.NumericalError: the forecasts are constant; the efficiency ...