Skip to content

fix(prediction): price a gone dispatch at the tariff's own maximum, never below the slot's rate - #5411

Merged
chalfontchubby merged 2 commits into
fix/rate-threshold-saving-boost-5050-v2from
fix/dispatch-gone-tariff-max
Oct 7, 2026
Merged

chalfontchubby merged 2 commits into
fix/rate-threshold-saving-boost-5050-v2from
fix/dispatch-gone-tariff-max

Conversation

@chalfontchubby

Copy link
Copy Markdown
Collaborator

🤖 This PR was written by Claude.

Stacked on #5163. It uses the tariff's own import maximum that #5163 works out, so the diff here is only the last commit.

Related to #5392.

Problem

In the PV10 worst case, prediction.py assumes an Intelligent Octopus dispatch slot more than 30 minutes ahead may go away, and prices it at rate_max. rate_max includes any saving session or Axle reward anywhere in the forecast, so an event elsewhere in the day inflates every dispatch slot's worst case. In #5392 a +100p Axle event priced each such minute at 132.25p.

Fix

A gone dispatch now pays the greater of:

  • the tariff's own highest import rate, without event rewards (rate_import_tariff_max, kept by set_rate_thresholds());
  • the slot's own rate, so an event on that slot still counts, and the worst case can never be cheaper than the nominal case.

rate_scan() resets the tariff maximum to the raw one, so a rescan without fresh thresholds falls back to the old, higher price. A debug replay of a file without the field falls back to rate_max in the same way.

The C++ kernel gets the same change and its parity revision goes to 17. CI rebuilds the checked-in binaries.

This doesn't settle #5392 by itself. That report's root cause is the fixed off-peak minutes being marked as dispatches that may vanish (#5396), which is being reworked separately. This PR only removes the event reward from the worst-case price.

Testing

  • test_dispatch_gone_priced_at_tariff_max: the tariff max with an event in the horizon, Prediction uses it, and a rescan resets it.
  • test_pv10_dispatch_gone_never_cheaper_than_the_slot: with every slot at 200p and the tariff max at 25p, PV10 costs the same as nominal.
  • Kernel parity: an added case with the tariff max below the slot's rate. Python and kernel agree, and removing the floor from the kernel alone fails it.
  • ./run_all --quick, --test debug_cases and ./run_pre_commit green.

🤖 Generated with Claude Code

…ever below the slot's rate (#5392)

In the PV10 worst case an Intelligent Octopus dispatch slot that may go away is priced at rate_max.
rate_max includes any saving session or Axle reward, so in #5392 a +100p Axle event put every such
minute at 132.25p - an event elsewhere in the day inflating a worst case it has nothing to do with.

set_rate_thresholds() keeps the event-excluded import maximum from #5163 as rate_import_tariff_max,
and Prediction uses it as its rate_max. A gone dispatch now pays the greater of that and the slot's
own rate, so an event on the slot itself still counts and the worst case can never be cheaper than
the nominal case. rate_scan() resets the tariff maximum to the raw one, so a rescan without fresh
thresholds falls back to the old, higher price. A debug replay of a file without the field falls
back to rate_max the same way.

The same change is made in the C++ kernel, whose parity revision goes to 17; the checked-in kernel
binaries are rebuilt by CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chalfontchubby
chalfontchubby merged commit d4d2d06 into fix/rate-threshold-saving-boost-5050-v2 Oct 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant