Day-ahead load forecaster for the Texas grid. It pulls real ERCOT data (no API keys), stores it in SQLite, and trains a gradient-boosting model to predict tomorrow's hourly load. Best held-out MAPE so far is 3.44%, and it turns out the classical model wins.
Held-out test on the most recent 90 days (2,160 hours, summer 2026), trained on the prior ~27 months with a chronological split:
| Day-ahead forecast | MAPE | MAE |
|---|---|---|
| SARIMA (56-day rolling refit) | 3.44% | 2,146 MW |
| HistGradientBoosting | 3.62% | 2,205 MW |
| Persistence (yesterday) | 4.54% | 2,750 MW |
| Seasonal naive (last week) | 8.81% | 5,415 MW |
I expected the GBM to win with its temperature and holiday features, but a plain SARIMA riding the daily cycle beats it on a summer window where recent days are extremely informative. The GBM trains once on full history while SARIMA refits daily, which matters.
Against ERCOT's own published day-ahead forecast (120 shared hours): gridwatch 2.13% MAPE, ERCOT 1.39%. They should win, they run zonal weather-calibrated models. The point was to measure the gap (~0.7 pp), not assume it.
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
gridwatch ingest --backfill # ~2.5 years of data, a few minutes
gridwatch train # train + held-out metrics (--model sarima for the other one)
gridwatch forecast # next 24 hours
gridwatch report # regenerate plots + metrics.json
pytest # offline tests, no networkA daily cron of gridwatch ingest && gridwatch forecast --retrain keeps
everything fresh.
Features are strictly leakage-free (every load-derived feature is at least 24 h old at issue time, and a test enforces it). Two caveats on the numbers: the backtest uses observed temperatures where a live run would use a weather forecast, which flatters the model a little, and the ERCOT comparison scores each forecast against its own target series, since settlement-metered load and total system demand differ by ~7-8% right now (private-use networks). The ERCOT benchmark window is short (their MIS only keeps about a week of forecast vintages) but grows as the cron accumulates them. Point forecasts only, no prediction intervals yet. Next: net-load forecasting and quantiles.
