# F3-conf-04 — Multi-horizon forecasts of Canadian quarterly CPI inflation ## Question Over 40 expanding-window origins (2015-10-01 to 2025-07-01; `first_origin`, `last_origin`, `n_origins`), does a time-series model beat the seasonal-naive forecast of Canadian quarterly CPI inflation (400·Δln of the quarterly-average, not seasonally adjusted CPI) at horizons 1, 2 and 4? And at h = 4, is the mean squared-error loss differential different from zero once we use a HAC (Diebold–Mariano-type) standard error that allows for the serial correlation created by overlapping 4-step errors? ## Data and exclusions - `data/cpi_quarterly.csv` has 127 rows and `data/cpi_monthly.csv` has 381 (`n_input_rows`). Both SHA-256 hashes match `data/README.md`. The quarterly averages, recomputed from the monthly file, match to 3e-7. Inflation recomputed from `cpi_avg` matches to 3e-6, which is just rounding (`data_issues`). - Exclusions (`out/exclusions.csv`): 1995Q1 is dropped because inflation cannot be computed without a previous quarter (127 → 126). No outliers were treated: the 2020 COVID drop and the 2021–22 surge are kept. - The last estimation sample (origin 2025-07-01) covers 122 quarterly inflation observations, 1995Q2–2025Q3 (`n_analysed`). The direct regressions at that origin use 121, 120 and 118 rows for h = 1, 2, 4 (`n_regression_rows_last_origin`). - The full audit is in `out/state/data_audit.md`. ## Method - **Seasonal-naive baseline:** forecast for quarter t+h = y_{t+h−4}. - **Model `dirSAR_bic`** (`model_name`, `model_description`): a direct h-step OLS regression of y_{s+h} on four target-quarter dummies plus p lags y_s, …, y_{s−p+1}. The lag count p ∈ {0,…,4} is chosen by BIC on a common sample, separately at each origin and horizon. The model is then re-estimated on all usable rows, using only quarters ≤ origin. Model selection is part of the expanding window. - **Which lags BIC chose** (`selected_p_counts`): - h = 1: p = 0 at 25 origins, p = 1 at 15. - h = 2: p = 0 at 17 origins, p = 1 at 23. - h = 4: p = 0 at all 40 origins. At this horizon the model is therefore just the expanding-window mean of each quarter. - **Errors:** e = actual − forecast, computed on the data as given. No real-time validity is claimed. - **h = 4 test:** - The loss differential is d_t = e²_snaive − e²_model. - The standard error of d̄ uses a Newey–West long-run variance with a Bartlett kernel and truncation lag 4 (`hac_kernel_h4`, `hac_lags_h4`). The lag was fixed before seeing results and is ≥ h−1 = 3. - The p-value is two-sided from the standard normal. - **Sensitivity checks** (`sensitivity_dm_h4`): Bartlett with lags 3 and 8, rectangular with lag 3, and Student-t(39) p-values. The analysis uses no randomness (`random_seed` = null). ## Results All errors are in percentage points of annualised inflation. Forecasts are in `out/forecasts.csv` (240 rows). | h | RMSE snaive | MAE snaive | RMSE model | MAE model | 1 − RMSE ratio | |---|---|---|---|---|---| | 1 | 2.849 (`rmse_snaive_h1`) | 2.137 (`mae_snaive_h1`) | 2.199 (`rmse_model_h1`) | 1.561 (`mae_model_h1`) | 0.228 (`rel_improvement_rmse_h1`) | | 2 | 2.847 (`rmse_snaive_h2`) | 2.126 (`mae_snaive_h2`) | 2.364 (`rmse_model_h2`) | 1.536 (`mae_model_h2`) | 0.170 (`rel_improvement_rmse_h2`) | | 4 | 2.882 (`rmse_snaive_h4`) | 2.181 (`mae_snaive_h4`) | 2.353 (`rmse_model_h4`) | 1.602 (`mae_model_h4`) | 0.184 (`rel_improvement_rmse_h4`) | At all three horizons the model's point RMSE and MAE are lower than those of the seasonal-naive forecast. At h = 4: - Mean loss differential d̄ = 2.770 squared pp (`loss_diff_mean_h4`), with HAC SE 1.907 (`loss_diff_se_h4`). - DM statistic 1.45 (`dm_stat_h4`), two-sided p = 0.146 (`dm_pvalue_h4`). **At conventional levels, the h = 4 improvement cannot be distinguished from zero.** ## Uncertainty - **The h = 4 conclusion depends on the HAC choice** (`sensitivity_dm_h4`): | HAC choice | p-value | |---|---| | Bartlett, lag 3 | 0.197 | | Rectangular, lag 3 | 0.114 | | Bartlett, lag 4 (primary) | 0.146 (t(39): 0.154) | | Bartlett, lag 8 | 0.024 | The lag-8 result means the estimated long-run variance shrinks when longer lags are included, i.e. there are negative autocorrelations at longer lags. I keep the pre-specified lag 4 as the main result and do not choose a lag after seeing the results. - **h = 1 and h = 2 are also not significant.** These supplementary DM tests (Bartlett, lag = h; `dm_tests_h1_h2_bartlett_L_h`) give p = 0.105 at h = 1 and 0.269 at h = 2. So the point improvements at those horizons are not statistically established either. - **The sample is small.** It has 40 overlapping errors and a few extreme quarters (2020–2022), which dominate squared losses. The normal approximation for the DM statistic is therefore rough. ## Limitations and open issues - There is a single evaluation window of 40 origins, dominated by the COVID and 2021–22 inflation episodes. The ranking could differ in calmer periods. - The comparison is pseudo-out-of-sample on the current vintage, not real-time. - The model is chosen by me. Alternatives (seasonal ARIMA, iterated AR) were not evaluated, and no search over models was done on the evaluation sample. - At h = 4 the selected model is just expanding-window quarterly means. Its edge over seasonal-naive comes from averaging out noise in the single year-ago value. - DM-test significance at h = 4 is sensitive to the HAC truncation lag, as reported above. - Everything required was produced (`feasibility` = "complete").