Skip to content

Plan discharges at a worse price while leaving better adjacent prices completely unused, right before the main high-price export window #4272

Description

@mbuhansen

Description
When the battery needs more capacity than the discharge rate can deliver within the best-priced export window, Predbat pulls the extra discharge from a much cheaper, non-adjacent hour instead of from the better-priced hours immediately next to the main window. This leaves higher-priced hours completely unused while discharging at a worse price further away in time.

Environment
Predbat version: v8.45.3
Inverter: Kostal (soc_max 42.53 kWh, discharge rate 10.8 kW)
Relevant config: combine_rate_threshold(2.0), metric_min_improvement_export(3.0øre), metric_min_improvement_export_freeze(3.0øre), metric_battery_cycle(4-6øre/kWh), inverter_loss(5%), battery_loss(4%), battery_loss_discharge(4%), inverter_hybrid(True)
Evidence
Plan computed 2026-07-18 19:03:29, Export windows filtered:

18-07 19:50:00 - 18-07 20:00:00 @ 78.88øre -> target 91%
18-07 20:00:00 - 18-07 21:00:00 @ 92.28øre -> (not present, target stays 91%)
18-07 21:00:00 - 18-07 22:00:00 @ 100.80øre -> (not present, target stays 91%)
18-07 22:00:00 - 19-07 01:00:00 @ 104.01øre -> target 9%
So the battery is planned to discharge from 91% down to 9% (82 percentage points ≈ 34.9 kWh of a 42.53 kWh battery) entirely within the 22:00-01:00 (~104øre) window, while the 92.28øre (20:00-21:00) and 100.80øre (21:00-22:00) hours in between are left completely untouched (SoC held flat), and a small early discharge is instead taken at 78.88øre (19:50-20:00) — the worst price of the four candidate hours.

Analysis
The discharge rate is 10.8 kW, so the 3-hour window (22:00-01:00) can deliver at most 3 × 10.8 = 32.4 kWh — less than the 34.9 kWh the plan wants to move in that window. So it makes sense that some discharge has to happen outside the main window due to the power limit.

However, if extra time is needed, it should be taken from the best available adjacent price first (100.80øre @ 21:00, then 92.28øre @ 20:00) before resorting to a cheaper, more distant hour. Instead the plan skips both of those and discharges at 78.88øre, which is worse than both of the skipped hours. This looks like the price-level sweep in the optimiser (Optimise price set X, pass normal/trim/low) accepts a lower price threshold that happens to improve the metric, without checking whether an already-available higher-priced adjacent hour would have covered the same shortfall more cheaply.

Internal trace showing the threshold being accepted:

2026-07-18 19:03:25.362214: Optimise price set 92.2øre, pass normal, price 92.28øre ... best_metric -5023.92øre (no improvement)
2026-07-18 19:03:25.393257: Optimise price set 78.8øre, pass normal, price 78.88øre ... best_metric -5090.34øre (improves)
Expected behavior
When the discharge rate limit forces some export volume outside the primary high-price window, Predbat should fill that overflow from the highest-priced unused adjacent hours first (here: 100.80øre, then 92.28øre) before considering any cheaper hour further away.

Actual behavior
The plan leaves two better-priced hours (92.28øre, 100.80øre) completely unused and instead discharges at a worse price (78.88øre) two hours earlier to make up the shortfall.

predbat (5).log

predbat_debug.yaml (1).txt

predbat_plan (1).html

Metadata

Metadata

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions