# F3-conf-03 — Rolling-origin ridge forecasts of US IP growth (h = 1) ## Question Does a fixed-penalty (λ = 50) ridge regression on eight standardised monthly predictors forecast next month's US industrial production growth (100·Δln INDPRO) better than (i) the naive last-value forecast and (ii) the expanding historical mean, over 60 rolling origins 2021-09 … 2026-08? Estimands: out-of-sample RMSE and MAE of each forecast, and 1 − RMSE_ridge/RMSE_baseline. ## Data and exclusions - `data/ip_predictors.csv`, 417 rows (1992-01 … 2026-09), current FRED vintage retrieved 2026-10-30. Monthly index is complete, unique and sorted. - Missing values: only `neworder_chg` for 1992-01 and 1992-02. These never enter: the earliest ridge training row is s = 1992-03 (its lags reach ip_growth of 1992-01, which is present). - Discrepancy with README: README says the 1992-01 value of every change series is empty, but `ip_growth`, `oil_chg` and `permit_chg` have values for 1992-01. They were used as given (ip_growth 1992-01 only appears as the second lag of the first training row). - The COVID-19 swings (ip_growth −14.14 % in 2020-04, +6.35 % in 2020-06) are in every training window. I kept them because the task fixes the estimator exactly. - No observations were excluded. At the last origin (2026-08) the ridge model has 413 training rows (s = 1992-03 … 2026-07). ## Method - Origins t = 2021-09-01 … 2026-08-01 (60). The target is ip_growth at t+1. Only data dated ≤ t are used at each origin. - Naive: ip_growth_t. Expanding mean: the mean of ip_growth over 1992-04 … t. - Ridge: training pairs (z_s, y_{s+1}) for 1992-03 ≤ s ≤ t−1, with z_s = (ip_s, ip_{s−1}, ip_{s−2}, oil_chg_s, fedfunds_s, permit_chg_s, neworder_chg_s, tcu_s). At every origin, features are standardised with the training-window mean and the sample SD (ddof = 1), and the target is centred on its training mean ȳ. b = (Z′Z + 50·I)⁻¹Z′(y − ȳ), with an unpenalised intercept ȳ. Forecast = ȳ + ((z_t − μ)/σ)·b. Standardisation is therefore "expanding" (recomputed from each origin's own training window). λ is fixed and was not tuned. - Errors = forecast − actual, computed on current-vintage data. Everything is in `out/analysis.py`, which is deterministic. - Supplementary uncertainty (`out/uncertainty.json`): a Diebold–Mariano test on squared-error loss differences, with the Harvey–Leybourne–Newbold correction, Newey–West lag 0 and 3, and t(59) reference. Also a moving-block bootstrap (block 6, B = 5000, seed 20261030) of 1 − RMSE ratio, reported as a 90 % percentile interval. ## Results | model | RMSE (pp/month) | MAE (pp/month) | |---|---|---| | naive | 0.8831 | 0.6990 | | expanding mean | 0.5439 | 0.4293 | | ridge (λ = 50) | 0.5608 | 0.4514 | - Relative improvement of ridge vs naive: 1 − 0.5608/0.8831 = **+0.365** (ridge has 36.5 % lower RMSE). - Relative improvement of ridge vs expanding mean: **−0.031**, meaning ridge RMSE is 3.1 % *higher* than the mean's. MAE ranks the models the same way. - Answer: ridge clearly beats the naive forecast, but it does **not** beat the expanding historical mean over 2021-09 … 2026-08. It is slightly worse, and the difference is not statistically distinguishable from zero (see below). ## Uncertainty - Ridge vs naive: DM statistic −3.36 (NW lag 0, p = 0.001) and −3.45 (lag 3, p = 0.001). The bootstrap 90 % interval for the relative improvement is [0.27, 0.42]. The advantage over naive is robust. - Ridge vs mean: DM statistic +0.54 (lag 0, p = 0.59) and +0.58 (lag 3, p = 0.56). The bootstrap 90 % interval for the relative improvement is [−0.14, 0.04]. With 60 origins we cannot tell ridge and the mean apart. - 60 monthly origins is a short evaluation sample. The bootstrap and DM tests treat the parameter-estimation error as part of the loss differential and are only approximate. They are not part of the specified estimands. ## Limitations and open issues - Current-vintage data, and publication lags are ignored (e.g., INDPRO/TCU/NEWORDER for month t are not actually known at the end of month t). Results describe how predictable the revised series is under stylised timing. They are **not** real-time forecast performance. - λ = 50 was fixed by the task and not tuned. Other penalties, or robust handling of the 2020 outliers (which strongly affect μ, σ and b), could change the ranking. I did not explore these, because that would depart from the specified estimator. - The evaluation window (post-COVID recovery, 2021–2026) is a single regime, so these conclusions may not generalise. - README/data discrepancy on 1992-01 change values (documented above); it does not affect the forecasts materially.