- Client
- Solstice Grid
- Industry
- Energy
- Year
- 2026
- Duration
- 26 weeks
- Team
- 5 engineers, 2 data scientists, 1 designer
- Services
- AI engineering · Data platforms · Cloud & DevOps
Solstice was buying balancing power on day-ahead guesswork. We built the ingest and forecasting platform that reads 2.1 million meters every fifteen minutes and prices the next forty-eight hours.
The product
Rebuilt interface, live figures. Everything below is the shipped surface, not a concept.
Meters / 15 min
2.1M
MAPE D+1
6.5%
Data freshness
11m 40s
day-ahead MAPE, %
D+1 · 48h horizon
Input attribution
Substations
Forecasting ran through a spreadsheet-shaped pipeline that one analyst maintained and, realistically, one analyst understood. Meter reads arrived as nightly CSV drops, so the day-ahead position was built on data up to twenty hours old. Error sat at 9.4% MAPE, and every point of that error gets bought back on the balancing market, usually at the worst hour of the day. That is how a data-freshness problem ends up on a P&L.
We replaced the drops with a streaming ingest that takes reads at fifteen-minute cadence and back-fills late arrivals without corrupting a sealed settlement window — meters drop offline and return with four hours of history behind them, and the old pipeline simply lost it. Forecasting moved to a per-substation ensemble: gradient boosting over weather, calendar and local load history, sitting on a shared regional temporal model. It retrains nightly and has to beat the incumbent on a held-out week before it is allowed to promote itself. Every forecast ships with its input attribution, because the first question from the trading desk is never what the number is — it is why it moved.
Day-ahead error went from 9.4% to 6.5% MAPE, and the intraday position now updates hourly rather than never. Solstice puts avoided balancing cost at roughly $4.6M a year; we would call that directionally right rather than precise. The pipeline has held its fifteen-minute freshness SLO unattended for eleven months, through two substation outages and one very cold February.
Solid lines carry request traffic. Dashed lines are asynchronous — replication, shadow reads and audit trails that must never sit on the critical path.
def promote(candidate, incumbent, holdout): challenger = mape(candidate.predict(holdout.x), holdout.y) baseline = mape(incumbent.predict(holdout.x), holdout.y) if challenger > baseline - MIN_GAIN: return Rejected(candidate.id, gain=baseline - challenger) registry.promote(candidate.id, mape=challenger) return Promoted(candidate.id, mape=challenger)A candidate model promotes itself only by beating the incumbent on a week it has never seen, by more than the noise floor.
Smart meters ingested every fifteen minutes, late arrivals back-filled
Lower day-ahead forecast error, 9.4% MAPE down to 6.5%
Annual balancing cost avoided, as reported by the trading desk
Figures reported by Solstice Grid after launch.
- 01Streaming meter ingest with late-arrival back-fill and sealed settlement windows
- 02Per-substation ensemble over a shared regional temporal model
- 03Automated retrain-and-promote gate scored against the incumbent
- 04Explainability view showing input attribution per interval
- 05Operator console with a fifteen-minute freshness SLO and paging
- Python
- Rust
- Kafka
- ClickHouse
- Airflow
- Kubernetes
- Next.js
“We used to defend the forecast. Now we interrogate it. The model tells us which inputs moved, and the trading desk trusts the number.”