# F3-conf-04 — Multi-horizon forecasts of Canadian quarterly CPI inflation ## Question Over 40 rolling quarterly origins, 2015-10-01 to 2025-07-01 (`n_origins`, `first_origin`, `last_origin`), does a time-series model forecast Canadian quarterly CPI inflation (400 × Δln of the quarterly-average NSA CPI) better than the seasonal-naive forecast at horizons 1, 2 and 4? And at h = 4, is the mean squared-error loss differential different from zero once we allow for the serial correlation that overlapping 4-step errors create? ## Data and exclusions - `data/cpi_quarterly.csv`: 127 rows, 1995Q1–2026Q3 (`n_input_rows`). `data/cpi_monthly.csv`: 381 rows (`n_input_rows`), used only for the provenance audit. Both SHA-256 hashes match `data/README.md`. Averaging the monthly index by quarter reproduces `cpi_avg` (max abs diff 3e-7), and 400·Δln reproduces `infl_annualised` (max abs diff 3e-6). Neither file has duplicates or gaps (details in `out/state/data_audit.md`). - Exclusions (`out/exclusions.csv`): one row removed, 1995Q1, because its inflation value is undefined. That leaves 126 analysable quarters (`data_issues`). - The 2022Q2 value (12.15 %) is a genuine part of the inflation surge. I kept it (`data_issues`). - The last estimation sample (origin 2025Q3) has 121 regression observations (`n_analysed`). ## Method - **Baseline (seasonal naive):** the forecast for target t+h is the value at t+h−4. - **Model (`model_name` = `ar_seas_aic`):** `model_description`. The regression has four quarter-of-year intercepts plus p lags of inflation. At each origin, AIC picks p from 0 to 8, comparing all candidates on a common sample. The chosen model is then re-estimated by OLS on every available quarter up to and including the origin. Forecasts for h = 2 and 4 are iterated from the one-step equation. At each origin the code cuts the series at the origin before doing any estimation or selection, so no later quarter is used (this is asserted in `out/analysis.py`). AIC chose p = 1 or 2 across origins (`selected_lags_by_origin`). - **Evaluation:** errors are e = actual − forecast. RMSE and MAE are taken over the 40 origins at each horizon. - **h = 4 test (Diebold–Mariano):** d_t = e²_snaive,t − e²_model,t. Its mean is `loss_diff_mean_h4`, and its standard error comes from a Bartlett-kernel (Newey–West) long-run variance (`hac_kernel_h4`) with truncation lag 3 = h−1 (`hac_lags_h4`). The test statistic is `dm_stat_h4` and its p-value `dm_pvalue_h4` is two-sided against N(0,1). I fixed the lag and kernel before seeing any results. Nothing in the analysis is random (`random_seed` = null). ## Results | h | RMSE snaive | RMSE model | MAE snaive | MAE model | 1 − RMSE ratio | |---|---|---|---|---|---| | 1 | 2.849 (`rmse_snaive_h1`) | 2.193 (`rmse_model_h1`) | 2.137 (`mae_snaive_h1`) | 1.519 (`mae_model_h1`) | 0.230 (`rel_improvement_rmse_h1`) | | 2 | 2.847 (`rmse_snaive_h2`) | 2.463 (`rmse_model_h2`) | 2.126 (`mae_snaive_h2`) | 1.553 (`mae_model_h2`) | 0.135 (`rel_improvement_rmse_h2`) | | 4 | 2.882 (`rmse_snaive_h4`) | 2.343 (`rmse_model_h4`) | 2.181 (`mae_snaive_h4`) | 1.597 (`mae_model_h4`) | 0.187 (`rel_improvement_rmse_h4`) | RMSE and MAE are in percentage points of annualised inflation. Over this window the model has lower RMSE and MAE than the seasonal-naive forecast at all three horizons. The RMSE reductions are 23 %, 13 % and 19 %. At h = 4, the mean loss differential is d̄ = 2.818 squared pp (`loss_diff_mean_h4`), with HAC SE 2.086 (`loss_diff_se_h4`). That gives DM = 1.351 (`dm_stat_h4`) and a two-sided p = 0.177 (`dm_pvalue_h4`). The model's squared error was smaller at 29 of the 40 origins (`n_loss_diff_positive_h4`). **At conventional levels, the h = 4 improvement cannot be distinguished statistically from zero.** ## Uncertainty - The conclusion depends on the HAC choices (`sensitivity_h4`). With Bartlett lag 4, p = 0.128. With a rectangular kernel at lag 3, p = 0.097. With the Harvey–Leybourne–Newbold small-sample correction and t(39) reference, p = 0.225. With Bartlett lag 6, p = 0.041. The SE shrinks as the lag grows, which means d_t has negative autocorrelations at higher lags. The lag-6 result was not pre-specified and is the only one below 0.05; I treat it as a sensitivity check, not as evidence. The pre-specified result (lag 3, p = 0.18) stands. - 40 overlapping observations is a small sample for long-run-variance estimation, so the normal approximation is rough. Squared-error losses are also strongly affected by the 2021–2023 inflation surge, so a handful of quarters drive much of d̄. - No uncertainty is reported for the RMSE and MAE at h = 1 and 2. The task only asks for a formal test at h = 4. ## Limitations and open issues - I chose one model ahead of time and did not compare alternatives (SARIMA, direct forecasts) on the test window, so there is no search-induced bias. However, the results describe only this model. - The errors are computed on the current data vintage, and a quarter is treated as known at the end of that quarter. No real-time validity is claimed. Actual CPI publication comes about three weeks later, and basket updates may have small effects. - The DM test compares forecasts, not models. Both forecasts come from expanding-window estimates, and the model is not nested in the seasonal-naive forecast in any parameter sense, so applying standard DM asymptotics is reasonable but approximate. - Feasibility: complete (`feasibility`).