A dashboard for viewing historic NESO demand flexibility service (DFS) data and same-day forecasting.
This repository contains:
- A logistic regression model for DFS event prediction.
- A linear regression (OLS) model for maximum accepted DFS price prediction (conditional on event).
- A Streamlit dashboard to visualise:
- Historical DFS events, LoLP/DRM signals, interconnectors, and system price.
- Forecast results and model evaluation metrics.
You can try the hosted version here:
https://dfs-forecast.streamlit.app
| Model | Purpose | Evaluation Metric | Baseline Result |
|---|---|---|---|
| Logistic Regression | Probability of DFS event | AUC | ≈ 0.74 |
| Linear Regression | Maximum accepted price (conditional on event) | R² | ≈ 0.31 |
- Event prediction hit rate (threshold 0.7): ≈ 33% for events, ≈ 92% for non-events
- Model period: Jan–Sep 2025 (earlier months removed due to extreme pricing at year-round DFS scheme launch)
- NESO Data Portal
- DFS Utilisation: accepted/rejected offers, volumes, and prices
- Interconnector trades and clearing prices
- Elexon BMRS
- Derated Margin (DRM) & Loss of Load Probability (LoLP) forecasts
- System Price and Net Imbalance Volume (NIV)
All inputs must be available by 10:00 on the day of forecast:
| Input | Timing Rule |
|---|---|
| DRM / LoLP | 8h and 12h forecasts used; after 18:00, 8h values default to 12h |
| Interconnectors | 1-day lagged volumes and clearing prices |
| System Price & NIV | 1-day lagged values |
| DFS Volume | 1-day lagged values |
| Forecast Window | 16:00–21:00 (evening peak only) |
drm_forecast_12hdrm_forecast_8hlolp_forecast_12hlolp_forecast_8hinterconnector_volume_1d_laginterconnector_cp_1d_laginterconnector_dispatched_1d_lag(binary)system_price_1d_lagniv_1d_lagdfs_volume_1d_lag
We model the probability of an event as:
- Trained with maximum likelihood estimation (MLE)
- Solver:
liblinear(stable for small datasets) - Balanced class weighting
- 5-fold time-series cross-validation
Classification rule:
Predict event dfs_event_pred when P(event) > 0.7
Conditional on event:
- Trained on periods where
dfs_event_pred == 1 - Pipeline:
SimpleImputer(median)→StandardScaler()→LinearRegression(fit_intercept=True) - Evaluated on time-ordered 15% test set
| Metric | Value | Comment |
|---|---|---|
| Mean AUC | 0.74 | Passable separability (AUC = 0.5 → no better than coin-flip) |
| Event hit-rate (p>0.7) | 33% | Conservative threshold used |
| R² (Price model) | 0.31 | Weak fit, ~31% variance explained |
| MAE | ~18.4 £/MWh | Acceptable first-pass |
Feature Importance (top 3):
- Event prediction:
drm_forecast_8h,dfs_volume_1d_lag,drm_forecast_12h - Max price prediction:
lolp_forecast_12h,lolp_forecast_8h,system_price_1d_lag
- Price forecast targets the maximum accepted price.
- Evening-only model; not generalised across all SPs.
- Regime change: early months excluded (prices > £700/MWh).
- No look-ahead bias: only features known by 10:00 are used.
# 1. Clone this repo
git clone https://github.com/<sam-secher>/<dfs-analysis>.git
cd <dfs-analysis>
# 2. Create and activate the environment
conda env create -f environment.yml
conda activate dfs-forecast
# 3. Launch the Streamlit app
streamlit run app.py