Project write-up · Macro / Nowcasting
Nowcasting French industrial production from open data
The problem
INSEE publishes France's industrial production index (IPI) about 40 days after the month ends. Plenty of data about that month is already public long before then: electricity consumption, business surveys, company insolvencies, bond spreads. France Economic Pulse uses that data to estimate the month before INSEE does (a nowcast), and rebuilds the estimate every day.
Data, aligned to when it was really known
All five sources are free, need no API key, and are fetched by a daily GitHub Actions job.
- Electricity consumption (RTE): the main real-time signal. Heating makes French demand very sensitive to temperature, so a cold month looks like an industrial boom. I regress consumption on heating and cooling degree days (Open-Meteo, 8 large cities) and use the residual.
- Business climate (INSEE survey), insolvency openings (BODACC, year-on-year change) and the France–Germany 10-year spread (ECB).
- The target's own history, lagged two months, because that's the latest figure INSEE has published when the nowcast is made.
Every feature is aligned to the date it was published, not the month it describes, and the weather correction is refitted at each backtest step using only past data. Without that, the backtest would quietly use information the model couldn't have had.
Model: start from the simplest benchmark
The rule was that complexity has to earn its place. The benchmark is an AR(1) on IPI growth. The main model is a ridge regression on the features above. Gradient boosting is a challenger, kept only if it beats ridge out of sample. With about 175 months of overlapping data, a regularised linear model was the natural favourite.
Evaluation: walk-forward, never random splits
The backtest uses an expanding window from January 2016 to July 2026 (127 months): train on everything up to month t, predict t+1, step forward. Random k-fold splits would leak the future in a time series. Models are compared on RMSE, directional accuracy and a one-sided Diebold–Mariano test against the benchmark. March–July 2020 is excluded from the scores because COVID errors swamp everything else.
| Model | RMSE | vs AR(1) | DM p-value | Right direction |
|---|---|---|---|---|
| AR(1) benchmark | 1.33 | 1.00 | — | 48% |
| Ridge (published) | 1.26 | 0.95 | 0.07 | 59% |
| Gradient boosting | 1.35 | 1.02 | 0.63 | 54% |
Ridge has 5% lower error than the benchmark and gets the direction right about 6 times in 10. Gradient boosting does worse than the benchmark, so it stays in the table only for transparency. The published 80% range is built only from errors known at the time; over 97 months, the actual figure fell inside it 80.4% of the time.
What it doesn't show
- The gain is suggestive, not proven. p = 0.07 misses the usual 5% bar. Monthly IPI growth is mostly noise (standard deviation about 1.3%), so a small edge is what a good model can realistically show.
- The edge hasn't held recently. Over the last 24 months, ridge's RMSE is 0.95 against 0.93 for the benchmark. The site says so on its model page.
- The scores are optimistic. They use INSEE's latest revised data, which the model wouldn't have had in real time. Every release is now archived as published, so a true real-time track record builds from launch.
- Septembers are biased low. A variant that refits the weather correction on recent seasons fixes this, but it is worse over the full period, so it wasn't adopted. Variants were fixed before testing, and tuning until one wins would just overfit the backtest.
- The GDP version failed. A quarterly GDP nowcast built the same way doesn't beat a naive forecast, so it's shown only on an experimental page.
Engineering
A daily GitHub Actions run fetches only new data, stores it as Parquet in the repository, refits the model, writes the site's data file and deploys a static Astro site in French and English. If a source is down, the site still rebuilds with its last good data and the run is flagged so I get an email.