SPB Git

spb/wp29_uqo

Public

# F3-conf-03 — Rolling-origin ridge forecasts of US industrial production growth with standardised predictors

# Scientific question

Does a fixed-penalty ridge regression on eight standardised monthly predictors forecast next month's US industrial production growth better than two simple baselines — the naive (last value) forecast and the expanding historical mean — over 60 rolling origins from 2021-09 to 2026-08? The estimands are the out-of-sample RMSE and MAE of the three forecasts (naive, expanding mean, ridge) at horizon h = 1 month, and the relative improvements of the ridge forecast over each baseline, 1 − RMSE_ridge / RMSE_baseline.

# Inputs

  • data/ip_predictors.csv — observed data derived from six FRED series (INDPRO, MCOILWTICO, FEDFUNDS, PERMIT, NEWORDER, TCU; retrieved 2026-10-30 UTC; current vintage; see data/README.md for URLs, retrieval times, transformations, SHA-256 and licence). Columns: month (YYYY-MM-01), ip_growth (target: 100 × Δln of the industrial production index, percent per month), oil_chg (100 × Δln WTI monthly price), fedfunds (effective federal funds rate, percent, level), permit_chg (100 × Δln building permits), neworder_chg (100 × Δln core capital goods orders), tcu (capacity utilisation, percent of capacity, level). 417 monthly rows, 1992-01 to 2026-09; the first one or two rows of the change columns are empty.

# Information-availability rules

  • Forecast origins: the 60 months t = 2021-09-01, …, 2026-08-01 (the month value). Horizon h = 1: the target of origin t is ip_growth of month t + 1.
  • At origin t, every value dated ≤ t is treated as available (publication lags are ignored in this exercise, as stated in data/README.md); no value dated > t may be used in any way. In particular, every statistic used to transform or scale the data (means, standard deviations, target mean) must be computed from the training window of that origin only, and recomputed at every origin.
  • Baselines (both required): naive = ip_growth of month t; expanding mean = the arithmetic mean of ip_growth over the months 1992-04-01 to t inclusive.
  • The ridge model (fully specified; implement exactly this):
    • training rows at origin t: all months s with 1992-03-01 ≤ s and s + 1 ≤ t, with target y_{s+1} = ip_growth of month s + 1 and feature vector z_s = (ip_growth_s, ip_growth_{s−1}, ip_growth_{s−2}, oil_chg_s, fedfunds_s, permit_chg_s, neworder_chg_s, tcu_s) (8 features);
    • standardise each feature as (z − μ_j) / σ_j, with μ_j the mean and σ_j the sample standard deviation (denominator n − 1) over the training rows of that origin; centre the target with its training mean ȳ;
    • coefficients b = (Z′Z + λ I)⁻¹ Z′ (y − ȳ) with λ = 50, Z the standardised training feature matrix (no intercept column; the intercept is ȳ and is not penalised);
    • forecast = ȳ + ((z_t − μ) / σ) · b, where z_t is the feature vector at the origin month t, standardised with the same μ, σ.
  • Errors are computed on the current-vintage data as given; do not claim real-time validity.

# Required outputs

  • out/forecasts.csv — one row per (origin, model), columns origin_date, target_date, horizon (always 1), forecast, actual, model ∈ {naive, mean, ridge}. Expected: 60 × 3 = 180 rows.
  • out/results.json with the keys:
key type unit meaning
task_id string — "F3-conf-03"
n_origins integer — 60
first_origin, last_origin string date first and last origin dates
lambda number — ridge penalty used (50)
normalisation_window string — how the standardisation statistics were computed: "expanding" (training window of each origin), "full_sample", or "none"
n_features integer — number of ridge features (8)
rmse_naive, mae_naive number percent per month naive-forecast errors
rmse_mean, mae_mean number percent per month expanding-mean errors
rmse_ridge, mae_ridge number percent per month ridge-forecast errors
rel_improvement_vs_naive number fraction 1 − rmse_ridge / rmse_naive
rel_improvement_vs_mean number fraction 1 − rmse_ridge / rmse_mean
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 — {"ip_predictors.csv": number of data rows read, excluding the header}
n_analysed integer — training rows at the last origin
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.
Files · 452964ee0fbbaa40fbbed2c87a54ca798ffd6a85
Menu

Theme