Repository navigation
Share forecasting and cheapest-window charging across Pricing modules - #757
Draft
MikeBishop wants to merge 4 commits into
Draft
MikeBishop wants to merge 4 commits into
MikeBishop wants to merge 4 commits into
Conversation
ForecastingPricing reads a module's config section and cheapWindow, and provides forecastMinPrice/forecastMaxPrice, cheapWindowStart (latched once under way), cheapWindowActive, getPriceForecast and getCheapestWindow. A module supplies its prices, updateForecast() and a non-blocking _forecast(). Amber and Tesla now use it. Amber's forecast range is now absent until a forecast arrives, as Tesla's already was, and its window is worked out when policy reads it rather than only when a forecast is fetched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pricing.md now covers what the modules share: the values policy can read, cheapest-window charging, what each module reports before it has a price, and the HTTP endpoints. Each module's page keeps only its own setup, and the README lists the Pricing modules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The market-data request aWATTar already makes returns the next 24 hours of prices; the module kept only the first. It now keeps them all as its forecast, so it gets cheapWindow, the forecast range and the forecast API like Amber and Tesla, with no extra requests. aWATTar's slots are an hour, not half an hour, and the API does not document their length, so the base now takes the slot length from the module (slotMinutes, default 30) and aWATTar reads it from the data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cheapWindow.endHour is the hour the window must finish by, but the search checked only the hour a window starts in. With hours 5, startHour 22 and endHour 7, it could pick 06:00-11:00, so a module whose cheapest prices are mid-morning charged outside the night the user configured. Every slot of the window must now fall in the range; with startHour alone, only the start is checked, so the window may still run past midnight. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MikeBishop
force-pushed
the
refactor/pricing-forecast-base
branch
from
November 9, 2026 03:00
9ccff11 to
334ebea
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This change does options 1 and 2 from #755. Option 3, making Static and aWATTar leave out a price they don't know yet, waits on the discussion there, so this is a draft until that settles.
Shared base.
ForecastingPricinglives inPricing/Forecast.py, next to the window helpers it uses. It reads the module's config section (enabled,cheapWindow) and providesforecastMinPrice/forecastMaxPrice,cheapWindowStart,cheapWindowActive,getPriceForecastandgetCheapestWindow.cheapWindowStartgoes throughlatchedWindow, so a module can't search again mid-window and cut charging short. A module supplies its prices,updateForecast()to refresh, and_forecast(), which returns(slots, now, tz)without blocking. It also setsslotMinutes(default 30), so the base assumes no slot length and no refresh cadence. Amber, Tesla and aWATTar now use it.Changes to Amber's behaviour:
forecastMinPrice/forecastMaxPriceare absent until the first forecast arrives, as Tesla's already were, so the base carries the "absent, not 0" rule forward.updateForecastfetches, from the background task or the HTTP API.getPriceForecastalways fetches 48 hours. Before, a?hours=12call could leave only 12 hours cached until the next fetch.aWATTar. The market-data request it already makes returns the next 24 hours of prices, so aWATTar now uses the base too, with no extra requests: still at most one call per 15 minutes, within aWATTar's 100 a day. Its slots are an hour long; the API doesn't document this, so the module works the length out from the data, and 15-minute slots work too. The current price now comes from the slot covering the current time. It still reports
0before its first price arrives; that is option 3.One docs page.
docs/modules/Pricing.mdcovers what the modules share: the values policy can read, cheapest-window charging, what each module reports before it has a price, and the HTTP endpoints. The page notes that Static, aWATTar and Amber currently report0for a price they don't know yet. Each module's page keeps only its own setup, forecast horizon and FAQ, and the README now lists the Pricing modules. The page also corrects one claim: an unknown price fails safe only ingt/gte/lt/ltecomparisons. Withne, it matches.Cheapest window kept inside its hours.
cheapWindow.endHouris documented as the hour the window must finish by, but the search checked only the hour a window starts in. With{"hours": 5, "startHour": 22, "endHour": 7}it could pick 06:00–11:00, which on Amber's typical day (cheapest mid-morning, with solar) charges outside the night the user set. Every slot of the window must now fall in the range. WithstartHouralone, only the start is checked, so a window can still run past midnight. This was in the original Amber forecasting code, which is not in a release yet.Evidence: full unit suite (
pytest tests --ignore=tests/API --ignore=tests/integration --ignore=tests/scenarios): 2672 passed. New tests check that Amber's attribute reads never fetch, that its price range is absent before the first forecast, and that aWATTar builds its forecast and finds windows over hourly and 15-minute slots, and that a window ends byendHour. Not run against the live aWATTar or Amber APIs.Changelog: none (forecasting and the Pricing module type are not in a release yet)
🤖 Generated with Claude Code