Day-ahead forecasting: accuracy and limits
Five models forecast the hourly DE-LU day-ahead price using only what a bidder knows when the order book closes at 12:00 on the day before delivery. Per-hour Ridge regression is the most accurate overall and holds up best in the tail hours that drive storage value. The more complex pooled LightGBM model is close in ordinary hours but breaks down in negative-price and scarcity hours: model complexity did not buy tail performance.
Retrospective benchmark,
Results
Ridge's mean absolute error is
Controls against look-ahead. The forecast uses only data public before
12:00 on D-1: lagged prices, the calendar and residual load from two days
earlier. Model settings are frozen before the test window, and every refit uses
only earlier days. Published numbers come from a committed, hashed release that
gpa export --check verifies against the site. Live forecasts that miss the
gate are refused, never backdated.
Does it pick the right hours?
A battery needs the order of the hours more than their level. Ridge places the
day's cheapest hour within an hour of the realised one on
Where the models fail
Complexity did not buy tail performance. In the scarcest 5% of hours, Ridge still beats the best naive
forecast (
Across years. Ridge's error ranges from
The comparison is between two specific designs: a separate linear model for each hour, and one tree model pooled across hours, both with the same limited inputs. It does not show that linear models generally beat tree models. It shows that this pooled tree model did not extrapolate to price levels rarely seen in training.
Breakdown by price regime, market block, year and hour
Negative-price hours and the top 5% of evaluation prices are regimes assigned after the fact; they are never model inputs.
Would operator forecasts help?
Adding the operators' day-ahead forecasts of load, wind and solar, on the same test window and protocol:
They help, but this source cannot supply them in time. EU rules only require wind and solar forecasts by 18:00 Brussels time on D-1, six hours after the gate, and a probe of Energy-Charts found neither at checks two to nearly five hours after the gate on each of the five delivery days it covered (25 to 29 September 2026). The gain is therefore an upper bound: it credits the model with forecasts this public source only has hours after the auction. A desk closes that gap with commercial forecasts issued before the gate.
Design: target, inputs and test protocol
- Target: the duration-weighted hourly average of DE-LU day-ahead prices, in EUR/MWh. Since delivery on 1 October 2025 the auction clears quarter-hours, so the hourly figure is an analytical aggregate rather than a traded product.
- Gate: 12:00 market time on D-1, when the order book closes. Prices for the previous delivery day have already cleared and are available.
- Inputs: lagged prices, calendar variables and residual load (demand minus wind and solar) from at least two delivery days earlier. Realised delivery-day values never enter a forecast.
- Protocol: training starts
and validation ; the test runs from to . The ridge penalty and tree settings are chosen on the validation window and frozen. Every model refits using only dates before the forecast day. All five models are scored on the same hours out of in the period. - Models: Ridge fits one regression per delivery hour; LightGBM fits one model across hours, with the hour as a feature. The naive forecasts repeat the previous day, the previous week or the last similar day: what a desk falls back on without a model.
- Data handling: only complete hours enter the panel, and missing observations stay missing. The two occurrences of the autumn clock-change hour are averaged. Stored inputs carry the providers' latest revisions, so the exact values visible at each historical gate cannot be verified.
Predictive intervals
Each model's 80% interval uses quantiles of its own previous 56 errors at the same clock hour; current-day errors never enter the calibration.
Coverage falls short of the nominal 80% for every model. Pinball loss and interval width are shown because a very wide interval can cover prices while saying little.
Inspect a week
Twelve evenly spaced weeks from the test period.
Prospective pilot
Since 23 September 2026, a scheduled job issues Ridge and the three naive
forecasts before each gate and stores the inputs each forecast used. Runs that
arrive after the gate are refused rather than backdated. A second Ridge arm with
the operator forecasts, ridge_da, found nothing to read in 11 pre-gate runs
across six delivery days and was retired on 28 September, for the reason above.
Each delivery day counts once, at the best outcome any attempt reached:
Ledger as of
Reproduce
pip install -e ".[dev]"
gpa backtest --scope all
gpa fundamentals-ablation
gpa export --check
The frozen release under data/experiments/ stores the predictions, scores,
parameter searches, ridge coefficients and input panel, together with a hash of
the forecasting code. The input-panel SHA-256 is gpa export --check confirms every published table still matches it.