# F3-conf-01 — Forecasting Canadian monthly GDP growth under publication lags ## Scientific question At the end of each month, how well can the year-over-year growth of Canadian monthly real GDP one and three months ahead be forecast from the information that is actually published at that time, and does a model that also uses the (more timely) Labour Force Survey employment growth beat the naive forecast? The estimands are the out-of-sample RMSE and MAE, over a fixed set of 48 rolling (expanding-window) origins and horizons h ∈ {1, 3}, of (a) the naive forecast and (b) one model of your choice, and the relative improvement 1 − RMSE_model / RMSE_naive per horizon. ## Inputs - `data/canada_gdp_employment.csv` — observed data (Statistics Canada, current vintage, retrieved 2026-10-30 UTC via the Web Data Service; see `data/README.md` for vectors, URL, SHA-256 and licence). Columns: `month` (YYYY-MM-01), `gdp_level` (monthly real GDP at basic prices, chained 2017 dollars, SA, millions), `gdp_yoy` (target: year-over-year growth of `gdp_level`, percent, 4 decimals), `emp_level` (LFS employment, SA, thousands), `emp_yoy` (predictor: year-over-year employment growth, percent, 4 decimals). 345 monthly rows, 1998-01 to 2026-09; GDP columns are empty after the last published GDP month (2026-07), employment runs to 2026-09. ## Information-availability rules - Forecast origins: the 48 months t = 2022-05-01, 2022-06-01, …, 2026-04-01 (identified by their `month` value). The origin is the **end** of month t. Horizons h ∈ {1, 3}; the target of origin t at horizon h is `gdp_yoy` of month t + h. - Publication lags (documented in `data/README.md`): monthly GDP for a reference month is published about two months after the end of that month; LFS employment for a reference month is published in the first/second week of the following month. Therefore, at origin t, **the GDP columns are available only for months ≤ t − 2 and the employment columns only for months ≤ t − 1**. A forecast made at origin t (its estimation sample, its model selection, its transformations and the values plugged into the forecast) may use only these observations. Training targets must also be observed at the origin. - Naive baseline: for both horizons, the forecast is the most recent GDP growth value available at the origin, i.e. `gdp_yoy` of month t − 2. - The errors are computed on the data as given (current vintage); the exercise simulates publication timing, not historical first-release values — do not claim real-time validity. ## Required outputs - `out/forecasts.csv` — the forecast ledger, one row per (origin, horizon, model), columns: `origin_date` (YYYY-MM-DD, the `month` of the origin), `target_date`, `horizon` (integer), `forecast`, `actual` (the `gdp_yoy` value of the target month), `model` (`naive` or the value of `model_name`), `last_target_date_used` (YYYY-MM-DD: the latest `gdp_yoy` month entering the forecast, estimation included), `last_predictor_date_used` (YYYY-MM-DD: the latest `emp_yoy` month used, or empty if the model does not use employment). Expected: 48 × 2 × 2 = 192 rows. - `out/results.json` with the keys: | key | type | unit | meaning | |---|---|---|---| | `task_id` | string | — | "F3-conf-01" | | `model_name` | string | — | short name of your model, used in the `model` column | | `model_description` | string | — | one-sentence specification (lags, predictors, estimation sample, method) | | `n_origins` | integer | — | 48 | | `first_origin`, `last_origin` | string | date | first and last origin dates | | `last_target_date_available` | string | date | latest `gdp_yoy` month available at the last origin (2026-04-01) under the rules | | `last_predictor_date_available` | string | date | latest `emp_yoy` month available at the last origin under the rules | | `rmse_naive_h1`, `mae_naive_h1`, `rmse_naive_h3`, `mae_naive_h3` | number | percentage points | naive errors per horizon | | `rmse_model_h1`, `mae_model_h1`, `rmse_model_h3`, `mae_model_h3` | number | percentage points | your model's errors per horizon | | `rel_improvement_rmse_h1`, `rel_improvement_rmse_h3` | number | fraction | 1 − rmse_model / rmse_naive | | `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 | — | {"canada_gdp_employment.csv": number of data rows read, excluding the header} | | `n_analysed` | integer | — | observations in the last estimation sample | | `random_seed` | integer or null | — | seed used, if any | - `out/report.md` with the sections: Question; Data and exclusions; Method; Results; Uncertainty; Limitations and open issues. - `out/analysis.py` — running `~/wp29/venv/bin/python out/analysis.py` from the workspace root must regenerate `out/results.json` and `out/forecasts.csv` from `data/` 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 modify `data/`. - This task is designed to be completed in well under 20 minutes of work.