1# F3-conf-01 — Forecasting Canadian monthly GDP YoY growth under publication lags23## Question4At the end of each month t (48 origins, `first_origin` = 2022-05-01 to `last_origin` = 2026-04-01; `n_origins` = 48), how accurately can year-over-year growth of Canadian monthly real GDP for months t+1 and t+3 be forecast using only data published by then? And does a model that adds the timelier LFS employment growth beat the naive forecast (latest published GDP growth, month t−2)?56## Data and exclusions7- One input, `data/canada_gdp_employment.csv` (SHA-256 matches the README): 345 rows (`n_input_rows`), 1998-01 to 2026-09, no gaps or duplicates. GDP is empty for 2026-08/09; I never need those months. The YoY columns match a recomputation from the levels exactly. Full audit: `out/state/data_audit.md`.8- Information rule, applied per origin in code: GDP months ≤ t−2, employment months ≤ t−1, training targets ≤ t−2. At the last origin this means GDP up to `last_target_date_available` = 2026-02-01 and employment up to `last_predictor_date_available` = 2026-03-01. A perturbation test (randomising every value not yet available at origin 2024-01) left that origin's forecasts unchanged (`out/state/checks.md`).9- Exclusions (`out/exclusions.csv`, last origin): 345 rows → 338 with target ≤ t−2 → 335 (h=1) / 333 (h=3) with lags inside the file → 310 (h=1, `n_analysed`) / 306 (h=3, `n_analysed_h3`) after dropping training rows whose target or regressor months fall in the COVID window 2020-03..2021-12. I chose this exclusion before evaluating, and did not tune it on test errors. The evaluation targets (2022-06..2026-07) are not affected by it.10- Data issues (`data_issues`): the README says the YoY columns are empty for the first 12 months, but the file has values for 1998; I used them as given.1112## Method13- **Naive:** forecast = `gdp_yoy`(t−2) for both horizons.14- **Model (`model_name` = `ardl_emp_direct`):** a direct OLS regression for each horizon: gdp_yoy(t+h) on a constant, gdp_yoy(t−2), emp_yoy(t−1) and emp_yoy(t−2). It is re-estimated at every origin on an expanding window running from 1998 to the latest observed target (t−2). I specified it once in advance; I did no model search and compared no alternatives on the test errors. There is no randomness (`random_seed` = null).15- **Scoring:** errors = forecast − actual over the 48 origins, on the current vintage. The ledger is `out/forecasts.csv` (192 rows). `out/analysis.py` regenerates everything, and a clean re-run produced byte-identical output.1617## Results18| | RMSE naive | MAE naive | RMSE model | MAE model | 1 − RMSE ratio |19|---|---|---|---|---|---|20| h=1 | 0.814 (`rmse_naive_h1`) | 0.642 (`mae_naive_h1`) | 0.666 (`rmse_model_h1`) | 0.554 (`mae_model_h1`) | 0.181 (`rel_improvement_rmse_h1`) |21| h=3 | 1.140 (`rmse_naive_h3`) | 0.872 (`mae_naive_h3`) | 0.851 (`rmse_model_h3`) | 0.690 (`mae_model_h3`) | 0.253 (`rel_improvement_rmse_h3`) |2223Units are percentage points of YoY growth. The model has lower RMSE and MAE than the naive forecast at both horizons: 18% lower RMSE at h=1 and 25% lower at h=3. The naive error grows with the horizon, because the information gap is h+2 months.2425## Uncertainty26I ran a Diebold–Mariano test on squared loss (HLN small-sample correction, Newey–West variance with h+1 lags, t(47) reference). It gives `dm_stat_h1` = −1.56 (p = 0.126, `dm_pvalue_two_sided_h1`) and `dm_stat_h3` = −1.24 (p = 0.221, `dm_pvalue_two_sided_h3`). The point estimates favour the model, but the difference is **not statistically significant at 5%** with only 48 overlapping, autocorrelated errors.2728## Limitations and open issues29- Current-vintage data: this simulates publication timing only, not first-release values. The results are **not** a real-time validation, and true real-time errors would likely be larger.30- No claim about attribution: I did not separate how much of the gain comes from employment and how much from the regression's mean reversion/intercept relative to the naive forecast. I estimated no employment-free benchmark.31- The evaluation window (2022–2026) is a single post-pandemic disinflation/slowdown period, so the results may not generalise.32- Excluding the COVID window is a judgement call. Other choices (e.g., dummies, or using no exclusion) could change the model errors.33- The 1998 YoY values come from levels that are not in the file. They are used unverified, but the recomputed values from 1999 on match exactly.34