Cutting demand charges and energy costs at a mid-size SPP-grid metal finisher — measured against 12 months of real wholesale prices.
kW demand by hour, based on the latest 48h price forecast.
"Before" = realistic baseline: jobs spread across the scenario's plating lines (1 for Small, 2 for Mid, 3 for Large, 5 for Multi-plant) starting at shift open, with no demand-management thinking. That's what plants actually look like today.
Two stacked layers make up the total. Each one is real money, computed against the same 12 months of SPP prices.
The 24/7 background equipment that sets the floor of your demand profile. Sized for the selected scenario.
Would extending the day shift to capture cheap overnight hours pay for itself?
Tells you when to run plating loads during the day to minimize two costs at once: the kWh charge (when wholesale electricity is cheap) and the demand charge (your peak kW during the month).
XGBoost regressor on hourly LMP with calendar + weather + demand + lag features. 80/20 chronological split. The metric above is WMAPE (weighted absolute percentage error: sum of errors divided by sum of actual prices) — the industry-standard metric for ISO LMP forecasting. WMAPE is used instead of plain MAPE because SPP LMP regularly dips below zero during high-wind hours, which breaks the percentage math in MAPE.
For each of the last 365 days, both schedulers replay against that day's real SPP LMP. The savings number above is the year sum of (flat-baseline cost − optimized cost).
Click into any of these to see exactly which rows produced the numbers on this page. Each table is straight from the source-of-truth Supabase database. Use the CSV button to pull a copy into Excel and verify the math yourself.
Source: backtest_results table. One row per (date × scenario × scheduler). The hero annual-savings number above is the sum of the savings column for the selected scenario + scheduler across the rolling 12-month window. savings = realistic_baseline_cost − optimized_cost (the database column is still named flat_cost for legacy reasons but stores the realistic-baseline cost).
| Date | Scenario | Scheduler | Optimized | Realistic baseline | Savings | Energy | Demand | Peak kW |
|---|
Source: hourly_prices table. Pulled via the gridstatus library from SPP's day-ahead LMP feed at the SPP North Hub node. The forecast model is trained on this column. By default this shows the most recent 168 hours (one week).
| Timestamp (UTC) | Node | $/MWh | Source |
|---|
Source: model_runs table. Every time the XGBoost model is retrained, a row is logged with the train window, the holdout test window, and the holdout WMAPE. The pill at the top of the page reads from the most recent row.
| Trained at (UTC) | WMAPE | RMSE | Train window | Test window | Artifact |
|---|
Source: rate_schedule table. Demand charge ($15.03/kW @ 115kV) is from the LES 2026 Industrial Rate Schedules PDF. Energy charges (cents/kWh by season + on/off-peak) are reasonable estimates pending the full PDF parse — the headline savings number depends almost entirely on the demand charge, not the energy charges.
| Field | Value |
|---|
Source: config/workload_scenarios.json (committed to the repo). These are the jobs the scheduler places into the 48-hour planning window. Each scenario is a fixed list of jobs sized to hit the labeled daily MWh total. The scenario shown is whichever one you have selected in the top control bar.
| Job ID | kWh required | Duration (h) | Rated kW | Deadline (h) | Lights-out eligible |
|---|
Want a heads-up when V2 ships? DM me on LinkedIn.