SPB Git

spb/wp29_uqo

Public
5.0 KB · 56 lines markdown
1# F3-conf-03 — Rolling-origin ridge forecasts of US industrial production growth23## Question4Does a fixed-penalty ridge regression (λ = 50, key `lambda`) on eight standardised monthly predictors (`n_features` = 8) forecast next month's US industrial production growth (100 × Δln INDPRO, percent per month) better than the naive (last-value) forecast and the expanding historical mean? There are `n_origins` = 60 rolling origins, from `first_origin` = 2021-09-01 to `last_origin` = 2026-08-01, all at horizon h = 1.56## Data and exclusions7- Input: `data/ip_predictors.csv`, 417 data rows (`n_input_rows`), 1992-01 to 2026-09, contiguous months, no duplicates. The SHA-256 matches the README (`sha256_input`).8- Missing values: only `neworder_chg` for 1992-01 and 1992-02. These months come before the training window, so nothing was imputed.9- Exclusions (`out/exclusions.csv`): two steps take 417 rows down to 413 training rows at the last origin (`n_analysed` = 413). Step 1 drops months before 1992-03-01, as the spec requires (2 rows). Step 2 drops feature months whose target would be dated after the last origin (2 rows).10- Data issues are listed in `data_issues`:11  - The README says 1992-01 is empty for every change series. In fact `ip_growth`, `oil_chg` and `permit_chg` have values for that month. I used them as given; only `ip_growth` for 1992-01 enters, as the 2-month lag for s = 1992-03.12  - The training windows contain extreme values (2020-04 = −14.1, 2008-09 = −4.5). I kept them because the specification is fixed.13  - The data are current vintage and publication lags are ignored.1415## Method16All computations are in `out/analysis.py`, which is deterministic and uses no randomness (`random_seed` = null).1718**Baselines.**19- Naive: the forecast is `ip_growth` of month t.20- Expanding mean: the forecast is the mean of `ip_growth` over 1992-04..t.2122**Ridge model.**23- Training rows: months s with 1992-03 ≤ s ≤ t−1, target `ip_growth`_{s+1}.24- Features: ip_growth at s, s−1 and s−2, plus oil_chg, fedfunds, permit_chg, neworder_chg and tcu at s.25- At every origin the features are standardised using the training-window mean and sample SD (ddof = 1), and the target is centred on its training mean ȳ (`normalisation_window` = "expanding").26- Coefficients: b = (Z′Z + 50 I)⁻¹ Z′(y − ȳ). Forecast: ȳ + z̃_t·b.27- ȳ is the same quantity as the expanding-mean forecast. The ridge forecast is therefore the mean forecast plus a shrunken, predictor-based adjustment.2829**Evaluation.** RMSE and MAE over the 60 forecasts. Actuals are `ip_growth` at t+1 in the current vintage.3031## Results32| model | RMSE (pp/month) | MAE (pp/month) |33|---|---|---|34| naive | 0.8831 (`rmse_naive`) | 0.6990 (`mae_naive`) |35| expanding mean | 0.5439 (`rmse_mean`) | 0.4293 (`mae_mean`) |36| ridge | 0.5608 (`rmse_ridge`) | 0.4514 (`mae_ridge`) |3738- **Ridge vs naive:** ridge improves RMSE by 36.5% (`rel_improvement_vs_naive` = 0.3649).39- **Ridge vs expanding mean:** ridge is 3.1% *worse* in RMSE (`rel_improvement_vs_mean` = −0.0310) and also worse in MAE.40- **Overall:** ridge clearly beats the naive forecast but does not beat the expanding historical mean. The stronger of the two baselines is the mean (`baseline_best_rmse_model`).4142## Uncertainty43I ran Diebold–Mariano tests on the squared-error differential, with the Harvey–Leybourne–Newbold small-sample correction and a t₅₉ reference distribution. For h = 1 no HAC lags are used.44- Naive vs ridge: DM = 3.36 (`dm_stat_naive_vs_ridge`), p = 0.0014 (`dm_pvalue_naive_vs_ridge`). Ridge is significantly more accurate than naive.45- Mean vs ridge: DM = −0.54 (`dm_stat_mean_vs_ridge`), p = 0.59 (`dm_pvalue_mean_vs_ridge`). There is no significant difference; the mean's point advantage is within sampling noise.4647With only 60 forecasts, the RMSE differences are imprecise. Ridge and the mean share the same intercept, so the comparison is nested-like, and a standard DM test can be conservative in that setting.4849## Limitations and open issues50- **Data vintage:** the data are the current (revised) vintage and publication lags are ignored. The results describe how predictable the revised series is under stylised timing, not real-time forecast performance.51- **Fixed specification:** λ = 50 and the feature set are fixed by the task and were not tuned. A different λ could change the ranking against the mean; I did not explore this because the task does not ask for it.52- **Outliers:** the COVID-era outliers (2020) in the training windows are not down-weighted, and they may distort the ridge coefficients.53- **Sample size:** the evaluation sample is short (60 months) and covers a single regime (2021–2026).54- **README discrepancy:** the README does not match the data on which 1992-01 cells are empty (see Data and exclusions). The effect is negligible, since only one lag value enters.55- Verification steps are recorded in `out/state/checks.md`. They include an independent re-implementation run with all future data corrupted, which reproduced every forecast and so rules out look-ahead.56
Files · main
Menu

Theme