PRICE-02: the trading verdict skips price periods that have ended
checks / unit (pull_request) Failing after 48m38s
infra-pin / fresh (pull_request) Successful in 1s

TariffPlan.arbitrage(now=...) drops the points whose period has ended
(end <= now), so the cheapest and dearest point can still be traded.
decide() (modes off), the modes-on allowed() gate and the Home verdict
all pass their own now. Hourly feeds and the static tariff are unchanged.
The off-peak sentence and the charge/discharge sentences say "price
period". Tests: test_price02.py; test_arbitrage.py rolls its whole-day
plans to start at now; test_modes_p3.py part 1 digests leave the trade
sentence out (commands unchanged on 7a00f12, 90bc901 and this tree).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
glenn schrooyen
2026-10-22 07:19:28 +02:00
co-authored by Claude
parent 90bc9017b2
commit 4297f7f3f1
8 changed files with 456 additions and 37 deletions
@@ -0,0 +1,27 @@
# PRICE-02 - the trading verdict skips price periods that have ended
**Status: for review.** Tests: `test_price02.py`. Found by AUDIT-01 (F1).
The refresh floors the plan to the top of the hour, so on the 15-minute feed
it holds the periods of this hour that are already over. The verdict took the
cheapest and dearest over every point. With modes off it could aim at a period
that had passed and then do nothing, for up to 45 min plus the refresh lag.
With modes on the `allowed()` gate could be open only because of such a period.
Decision: `TariffPlan.arbitrage(now=...)` drops the points whose period has
ended (`end <= now`, the span `price._cover_table()` gives, as
`priceruns.firm_slots()` does). Its three callers pass their own `now`:
`arbitrage.decide()`, `Controller._mode_roles()` and `homeview.trade_verdict()`.
- Taken in the verdict, not when the plan is built: a plan built at 03:00 is
still used at 03:20, and the Home chart keeps the day's earlier bars.
- An hourly point that is still current stays in, so hourly feeds and the
static day/night plan give the same verdict as before.
- `now=None` keeps the old reach (the whole series); only tests leave it out.
- The idle sentence now reads "now is not the cheapest or the dearest price
period". Other sentences that say "hour" (AUDIT-01 T6) are not touched here.
- Not changed here: F2 (static profile sampled at HH:00) and F3 (carried value).
Measured: with price off, and on a static tariff, every command over the
modes-off replay is byte-identical. On the dynamic replay a sale quotes a
smaller "above the cheapest" figure once the first cheap period is over.