F3-conf-04 — Multi-horizon forecasts of Canadian quarterly inflation with overlapping targets
Scientific question
Over 40 rolling quarterly origins (2015Q4 to 2025Q3), does a time-series model of your choice forecast Canadian quarterly CPI inflation (annualised, not seasonally adjusted) better than the seasonal-naive forecast at horizons of 1, 2 and 4 quarters, and is the difference in squared-error loss at the 4-quarter horizon statistically distinguishable from zero once the dependence created by overlapping multi-step forecast errors is taken into account? The estimands are the out-of-sample RMSE and MAE of the seasonal-naive forecast and of your model at each horizon, the relative improvement 1 − RMSE_model / RMSE_snaive per horizon, and, at h = 4, the mean loss differential d̄ = mean_t (e²_snaive,t − e²_model,t) with a standard error valid under serial correlation of the loss differential (Diebold–Mariano-type test).
Inputs
data/cpi_quarterly.csv— observed data derived from the Bank of Canada Valet series V41690973 (Total CPI, Canada, index 2002 = 100, not seasonally adjusted, monthly; Statistics Canada table 18-10-0004-01); retrieved 2026-10-30 UTC; seedata/README.mdfor URL, SHA-256 and terms. Columns:quarter(first month of the quarter, YYYY-MM-01),cpi_avg(mean of the three monthly index values),infl_annualised(target series: 400 × Δln cpi_avg, percent at annual rate, 6 decimals; empty for 1995Q1). 127 complete quarters, 1995Q1 to 2026Q3.data/cpi_monthly.csv— the monthly index as served (month,cpi), 381 rows 1995-01 to 2026-09, provided for provenance; the quarterly file is the input of the analysis.
Information-availability rules
- Forecast origins: the 40 quarters t = 2015-10-01, 2016-01-01, …, 2025-07-01 (the
quartervalue). Horizons h ∈ {1, 2, 4}; the target of origin t at horizon h isinfl_annualisedof quarter t + h. - A forecast made at origin t may use only quarters dated ≤ t (expanding window: estimation, model selection and transformations included); a quarter's value is treated as known at the end of that quarter.
- Seasonal-naive baseline: the forecast for the target quarter t + h is the value of
infl_annualisedfour quarters before the target, i.e. of quarter t + h − 4 (always ≤ t for h ≤ 4). - The h = 4 test: with one forecast per origin, consecutive 4-step-ahead errors overlap, so the loss differential d_t is serially correlated; the standard error of d̄ must be computed with a heteroskedasticity-and-autocorrelation-consistent (long-run variance) estimator with a truncation lag of at least h − 1 = 3 quarters; report the lag used.
- Errors are computed on the data as given; do not claim real-time validity.
Required outputs
out/forecasts.csv— one row per (origin, horizon, model), columnsorigin_date,target_date,horizon(1, 2 or 4),forecast,actual,model(snaiveor the value ofmodel_name). Expected: 40 × 3 × 2 = 240 rows.out/results.jsonwith the keys:
| key | type | unit | meaning |
|---|---|---|---|
task_id |
string | — | "F3-conf-04" |
model_name |
string | — | short name of your model, used in the model column |
model_description |
string | — | one-sentence specification (lags, seasonal terms, direct vs iterated, estimation sample, method) |
n_origins |
integer | — | 40 |
first_origin, last_origin |
string | date | first and last origin quarters |
rmse_snaive_h1, mae_snaive_h1, rmse_snaive_h2, mae_snaive_h2, rmse_snaive_h4, mae_snaive_h4 |
number | percentage points (annualised) | seasonal-naive errors per horizon |
rmse_model_h1, mae_model_h1, rmse_model_h2, mae_model_h2, rmse_model_h4, mae_model_h4 |
number | percentage points (annualised) | your model's errors per horizon |
rel_improvement_rmse_h1, rel_improvement_rmse_h2, rel_improvement_rmse_h4 |
number | fraction | 1 − rmse_model / rmse_snaive |
loss_diff_mean_h4 |
number | squared percentage points | mean over the 40 origins of e²_snaive − e²_model at h = 4 (positive = model better) |
loss_diff_se_h4 |
number | squared percentage points | HAC standard error of loss_diff_mean_h4 |
hac_lags_h4 |
integer | quarters | truncation lag used in the HAC estimator |
hac_kernel_h4 |
string | — | kernel used (e.g. "bartlett", "rectangular") |
dm_stat_h4 |
number | — | loss_diff_mean_h4 / loss_diff_se_h4 |
dm_pvalue_h4 |
number | — | two-sided p-value of dm_stat_h4 |
feasibility |
string | — | "complete", "partial" or "infeasible" |
feasibility_reason |
string or null | — | explanation if not complete |
data_issues |
array of {"issue","handling"} | — | data problems detected and how handled (empty list if none) |
n_input_rows |
object | — | {"cpi_quarterly.csv": rows read excluding header, "cpi_monthly.csv": rows read excluding header} |
n_analysed |
integer | — | observations in the last estimation sample |
random_seed |
integer or null | — | seed used, if any |
out/report.mdwith the sections: Question; Data and exclusions; Method; Results; Uncertainty; Limitations and open issues.out/analysis.py— running~/wp29/venv/bin/python out/analysis.pyfrom the workspace root must regenerateout/results.jsonandout/forecasts.csvfromdata/only, deterministically.out/claims.json— a list of the central claims of the report: [{"id": "C1", "text": "...", "results_keys": ["..."], "status": "supported"|"unsupported"|"not_evaluated", "caveat": "..."}].
Constraints
- No network access. Python:
~/wp29/venv/bin/python(numpy, pandas, scipy, statsmodels available). Do not modifydata/. - This task is designed to be completed in well under 20 minutes of work.