# F3-conf-04 — Multi-horizon forecasts of Canadian quarterly CPI inflation ## Question Over 40 expanding-window origins from 2015Q4 to 2025Q3 (`n_origins`, `first_origin`, `last_origin`), does a simple time-series model forecast Canadian quarterly CPI inflation better than the seasonal-naive forecast at horizons 1, 2 and 4 quarters? The inflation measure is 400·Δln of the quarterly-average CPI, not seasonally adjusted, in % at an annual rate. And at h = 4, is the mean squared-error loss differential distinguishable from zero once we allow for the serial correlation created by overlapping 4-step errors? ## Data and exclusions - `data/cpi_quarterly.csv` (127 rows) and `data/cpi_monthly.csv` (381 rows) were read (`n_input_rows`). Both SHA-256 hashes match `data/README.md`. The quarterly file can be rebuilt exactly from the monthly file (full audit in `out/state/data_audit.md`). - One exclusion (`out/exclusions.csv`): 1995Q1 has no inflation value, leaving 126 usable quarters (`data_issues`). The extreme quarters (2008Q4 −6.0, 2022Q2 +12.1) are real episodes and were kept. - Data run to 2026Q3. Quarters after an origin are used only as evaluation targets: each origin's information set is cut to quarters ≤ origin before any estimation, and a truncation test confirms this (`out/state/checks.md`, check 6). ## Method - **Baseline (seasonal naive):** the forecast for quarter t+h is the observed value of quarter t+h−4. - **Model (`model_name` = `direct_sdum_ybar4`; see `model_description`):** one OLS regression per horizon (direct forecasting). The regressors are four dummies for the target quarter (no intercept) and the average of the four most recent quarterly inflation rates at the origin. The model is re-estimated at every origin on an expanding window that uses only pairs whose target quarter is ≤ the origin. The specification was fixed before looking at any out-of-sample results: no model selection and no tuning. At the last origin the h=1 estimation sample has 118 observations (`n_analysed`). The method is deterministic (`random_seed` = null). - **Evaluation:** RMSE and MAE over the 40 origins for each horizon, plus the relative improvement 1 − RMSE_model/RMSE_snaive. - **h = 4 test:** d_t = e²_snaive,t − e²_model,t. Its mean has a Newey–West (Bartlett kernel) HAC standard error with truncation lag 4 ≥ h−1 = 3 (`hac_lags_h4`, `hac_kernel_h4`). The DM statistic is d̄/SE, with a two-sided p-value from N(0,1) (`dm_pvalue_h4`). Sensitivity variants are in `sensitivity_dm_h4`: Bartlett lag 3 and lag 6, rectangular lag 3, and the Harvey–Leybourne–Newbold correction with t(39) critical values. - Script: `out/analysis.py`. All forecasts: `out/forecasts.csv` (240 rows). ## Results | h | RMSE snaive | RMSE model | MAE snaive | MAE model | 1 − RMSE ratio | |---|---|---|---|---|---| | 1 | 2.849 (`rmse_snaive_h1`) | 2.301 (`rmse_model_h1`) | 2.137 (`mae_snaive_h1`) | 1.528 (`mae_model_h1`) | 0.192 (`rel_improvement_rmse_h1`) | | 2 | 2.847 (`rmse_snaive_h2`) | 2.441 (`rmse_model_h2`) | 2.126 (`mae_snaive_h2`) | 1.571 (`mae_model_h2`) | 0.142 (`rel_improvement_rmse_h2`) | | 4 | 2.882 (`rmse_snaive_h4`) | 2.395 (`rmse_model_h4`) | 2.181 (`mae_snaive_h4`) | 1.670 (`mae_model_h4`) | 0.169 (`rel_improvement_rmse_h4`) | Units are percentage points of annualised quarterly inflation. On both RMSE and MAE, the model's point accuracy is better than seasonal naive at all three horizons, with RMSE reductions of about 14–19%. At h = 4 the mean loss differential is d̄ = 2.57 squared pp (`loss_diff_mean_h4`), with HAC SE 1.92 (`loss_diff_se_h4`). That gives DM = 1.34 (`dm_stat_h4`) and a two-sided p = 0.18 (`dm_pvalue_h4`). **At h = 4 the improvement is not statistically distinguishable from zero at conventional levels.** ## Uncertainty - The h=4 test is sensitive to how the long-run variance is estimated. The sample autocorrelations of d_t are +0.22 at lag 1, −0.28 at lag 3 and −0.41 at lag 4 (`out/state/checks.md`); these values come from the verification script and are not stored in results.json. Results by variant (`sensitivity_dm_h4`): - Bartlett lag 3: SE 2.16, p = 0.23. - Rectangular lag 3: SE 1.78, p = 0.15. - Bartlett lag 6: SE 1.43, p = 0.07. - HLN-corrected primary statistic with t(39): p = 0.23. - No variant reaches p < 0.05, so the conclusion "not significant at 5%" holds for every lag and kernel tried. - Only 40 overlapping evaluation points are available, so the effective sample is small and the normal approximation is rough. - d_t is dominated by a few quarters in the 2021–2023 inflation surge (largest |d_t| at targets 2021Q2, 2023Q2, 2023Q1 and 2022Q2, one of which favours snaive). The point estimates of relative improvement are therefore fragile to the evaluation window. - No significance tests were run at h = 1 and h = 2 (the task does not require them). Improvements at those horizons are point estimates only. ## Limitations and open issues - Errors are computed on the current data vintage. The analysis does not claim real-time forecasting performance; actual CPI publication comes about three weeks after the quarter ends. - Only one pre-specified model was evaluated. No claim is made that it is the best model available. - The model ignores breaks such as COVID-19 and the 2021–22 surge. Its forecast is a seasonal profile plus a level that adapts to recent annual inflation, estimated over the full history since 1996. - The HAC lag (4) was chosen by us as h, one above the minimum h−1 = 3. Because results depend on this choice, alternatives are reported. - `n_analysed` refers to the h = 1 regression at the last origin. The h = 2 and h = 4 samples are 117 and 115 (`n_analysed_by_horizon_last_origin`).