Forecasts Octopus Agile electricity import and export prices up to 14 days ahead using an ensemble machine learning model. Covers all Agile regions (A–P) plus a national aggregate.
| Source | Data |
|---|---|
| Elexon BMRS | UK nuclear availability, demand |
| NESO | Wind, solar, embedded wind and demand forecasts; daily operating margin reserve (OPMR) |
| ENTSO-E Transparency Platform | French nuclear generation (interconnector signal) |
| Open-Meteo | UK and French temperature, wind speed, radiation (forecast + ensemble) |
| Octopus Energy | Agile tariff prices (actuals) |
| Nord Pool | GB60 day-ahead prices |
| Yahoo Finance | TTF natural gas futures |
Three-model ensemble (CatBoost, LightGBM, ExtraTrees) trained on a rolling 90-day window of half-hourly forecasts. Training samples are weighted by linear z-score so extreme prices (spikes, negative) are prioritised. Features include a fixed base (UK generation mix, demand, NESO operating margin reserve surplus, calendar flags) plus an experimentally selected optional set (currently French weather: fr_wind, fr_rad). Forecast intervals are derived empirically from holdout residuals binned by horizon and from Open-Meteo ensemble weather perturbations.
Each retrain also computes SHAP explanations from the LightGBM ensemble member (shap.TreeExplainer): a global mean-|SHAP| feature-importance chart (holdout set), shown on /v2/stats/, and per-slot signed feature contributions in £/MWh, stored on each ForecastData row. On the forecast page (/v2/<region>/), clicking any point on the price chart shows a "Why this price?" panel with the top SHAP contributors for that half-hour slot.
The project runs on Python with Django 4.2. Dependencies are managed with conda.
- Miniconda or Anaconda
- Git
conda env create -f environment.yml
conda activate agile_predictCopy .env.example to .env and fill in the required values:
cp .env.example .envpython manage.py migrate
python manage.py runserverThe dev server starts at http://localhost:8000.
python manage.py updateThis fetches all upstream data, retrains the ensemble, and writes new ForecastData and AgileData rows.
Key flags:
| Flag | Description |
|---|---|
--feature_set <name> |
Override the stored optimal feature set for this run |
--features col1,col2,... |
Specify exact feature columns instead of a named set |
--drop_feature <col> |
Remove a column from the selected feature set (repeatable) |
--force_experiment |
Force the feature-set experiment regardless of the 14-day schedule |
--debug |
Verbose logging of intermediate data and shapes |
Every 14 days the update run automatically evaluates all candidate feature sets defined in prices/forecast_features.py (EXPERIMENT_FEATURE_SETS) using walk-forward cross-validation (5 folds, 21-day train / 3-day test). Each set is scored on a weighted combination of MAE and RMSE, with short-horizon forecasts (<3 days) weighted 3× and medium-horizon (<7 days) weighted 2×. The winning set is persisted in the database and used for all subsequent runs until the next experiment.
All candidate sets include opmr_surplus as a fixed base feature. The experiment evaluates what optional features to add on top: generation, fr_weather, weather, fuel, fr_weather_nuclear, fr_weather_gas, weather_fuel, weather_fuel_fr, weather_fuel_fr_weather, full.
To force a re-evaluation immediately:
python manage.py update --force_experimentTo lock in a specific set for a single run (bypasses the experiment result):
python manage.py update --feature_set weather_fuel_frHosted on fly.io (app name: prices). Deploy with:
fly deploy --app pricesMigrations run automatically as the release_command before each rolling update.
The daily forecast update and Agile price refresh are triggered by cron jobs on the self-hosted Proxmox CT that POST to the fly.io web app (bin/cron_update.sh, bin/cron_latest_agile.sh). The CT also runs a local dev server started at boot via bin/runserver.sh.
The chart UI defaults to the v2 interface (/v2/<region>/). Forecast comparison overlays from AgileForecast and X2R can be toggled on; if a live fetch fails the UI falls back to the most recent stored data and shows a status indicator.
config/ Django settings, URLs, and shared utilities (data fetching, model helpers)
prices/ Main app: models, views, forecast pipeline, management commands
api/ REST API (forecast, accuracy, metadata endpoints)
templates/ Jinja-style Django templates (v2 UI and classic UI)
home_assistant/ Example HA sensor and Apex chart YAML