Skip to main content

CastValue — Quantifying the EUR/MW Value of Forecasts in Energy Trading

CastValue is an open, multi-benchmark framework that measures what a forecast is actually worth in a real trading/dispatch decision — in euros, not error metrics.

This repository currently contains one fully built benchmark (castvalue.bess.daa, described below) plus a second one under active development (castvalue.pv.imbalance, a PV imbalance-cost benchmark) and a placeholder for a third (castvalue.bess.multimarket, scope only — see src/castvalue/bess/multimarket/README.md). The rest of this README describes the original, fully-built BESS day-ahead-only benchmark.

Most forecast providers sell on MAE. Most traders buy on MAE. CastValue shows why that is the wrong metric, and replaces it with one that directly reflects trading P&L.


The Problem

A forecast with a low MAE can destroy more trading value than one with a high MAE. This is not a theoretical edge case — it happens regularly, and this repository demonstrates it empirically on real German DAA price data.

The reason: what matters for BESS dispatch is not how close the forecast price is to the actual price, but whether the forecast correctly ranks the cheap and expensive hours of the day. A forecast can be numerically accurate yet systematically wrong about timing — causing the optimizer to charge when it should discharge, and vice versa.


What CastValue Measures

CastValue is defined as the annual DAA trading margin generated by optimizing BESS dispatch on a given forecast, minus the margin generated by a naive persistence baseline (yesterday's prices as today's forecast):

CastValue(k) = AnnualMargin(forecast k) − AnnualMargin(persistence)

A positive CastValue means the forecast adds real EUR/MW above what any trader could achieve with zero forecasting effort. A negative CastValue means the forecast is worse than doing nothing.


Why This Matters

For BESS traders and asset managers: Stop evaluating forecast providers on MAE. CastValue tells you directly how much each forecast contributes to your annual trading margin, in the currency that appears on your P&L. Use this tool to compare your current provider against alternatives — and against the theoretical ceiling of perfect foresight.

For forecast providers: Benchmark and showcase your forecast in the metric your clients care about. A CastValue number is a commercially credible claim: it tells potential customers exactly how many EUR/MW per year your forecast is worth, backed by a transparent, reproducible methodology.


The MAE Paradox

The backtester surfaces a systematic finding we call the MAE Paradox: days with high forecast error (high MAE) do not reliably lead to high profit loss, and days with low forecast error do not reliably lead to high profit capture.

We classify days into two categories:

  • Bad Forecast, Good Capture — high MAE, but the optimizer still dispatched at the right times
  • Good Forecast, Bad Capture — low MAE, but a subtle timing shift caused the optimizer to miss the most profitable spreads

This has direct implications for how forecast quality should be specified in commercial contracts.


Methodology

The backtester runs a linear programme (LP) for each day of the dataset, optimising BESS charge/discharge decisions under realistic physical and market frictions:

  • Round-Trip Efficiency (85%) in the LP energy balance — every MWh delivered to the grid requires ~1.176 MWh purchased, so the LP only schedules trades that survive RTE losses.
  • Opportunity-cost bidding — charge bids = avg forecast discharge price × RTE; discharge asks = avg forecast charge price ÷ RTE. These are the realistic limit prices a desk would post given the forecast.
  • Out-of-the-money rejection — if the actual clearing price is unfavourable vs. the posted bid/ask, the MTU does not fill.
  • Volume-balanced paired rejection — when OTM fills leave charge and discharge volumes unbalanced, worst-margin MTUs are dropped on the oversubscribed side until volumes match. This forces end-of-day SOC = start-of-day SOC, so forecast accuracy directly drives the fill rate.
  • Perfect Foresight is defined as the LP optimum given RTE, with bids placed at clearing prices (100% fill rate). It still pays the RTE loss — a "perfect scheduling" benchmark, not a frictionless one.

Three scenarios are evaluated for each day:

Scenario Forecast used
Persistence Yesterday's actual prices
Electricity Maps Electricity Maps DAA price forecast (benchmarked provider)
Perfect Foresight Actual prices (theoretical ceiling)

Trading decisions are optimised on forecast prices. Realised margin is always calculated on actual prices.

The dataset covers DE-LU at native 15-minute resolution from 18 October 2025 through 22 February 2026 (126 days).

For the full mathematical formulation see docs/methodology/DAA_DE_LU_Formulation_v1.1.pdf.

Earlier methodology (v1.0): the original single-cycle deterministic dispatch (no RTE, no OTM, fixed daily SOC=0) is preserved at the v1.0 git tag. Check out that tag to access the v1.0 notebook, optimizer, formulation PDF, and whitepaper.


Repository Structure

CastValue/
├── README.md
├── LICENSE
├── DAA_optimizer.py                     # backward-compat shim -> castvalue.bess.daa.optimizer (deprecated)
├── pyproject.toml
├── src/castvalue/
│   ├── core/                            # shared machinery: coalitions, Shapley/Harsanyi, pricing, ceiling checks
│   ├── bess/
│   │   ├── daa/optimizer.py             # this benchmark: Pyomo/GLPK LP optimizer
│   │   └── multimarket/                 # planned, not implemented — raises NotImplementedError
│   └── pv/imbalance/                    # PV imbalance-cost benchmark, under active development
├── examples/
│   └── DAA_DE_LU_Bench.ipynb            # main analysis notebook for this benchmark
├── data/
│   └── master_energy_data_v2.csv        # 15-min DE-LU dataset (Oct 2025 → Feb 2026)
├── docs/
│   ├── methodology/                     # DAA_DE_LU_Formulation_v1.1.pdf/.tex
│   ├── whitepaper/                      # whitepaper PDF
│   └── img/                             # wp_annual_results.png, wp_cumulative_margin.png, wp_example_day.png
└── tests/
    └── bess/daa/golden/                 # golden regression fixtures guarding this benchmark's numerics

The notebook is structured in four sections:

  1. Optimisation & backtest — runs the LP for all days and all scenarios
  2. Annual results — cumulative margin chart and CastValue bar chart
  3. Example day — price forecasts, dispatch decisions, and realised margin for a single day
  4. The MAE Paradox — scatter plot and deep-dive on the paradox between forecast accuracy and profit capture

How to Benchmark Your Own Forecast

To run CastValue on your own forecast data, prepare a CSV with the following columns:

Column Description
timestamp_utc Quarter-hour timestamps in UTC
daa_actuals Actual DAA settlement prices [EUR/MWh]
daa_persistence_forecast Persistence forecast (previous day's actuals) [EUR/MWh]
daa_em_forecast Your forecast [EUR/MWh] (rename to your provider)

Replace the daa_em_forecast column in data/master_energy_data_v2.csv with your own data, update the scenario name in examples/DAA_DE_LU_Bench.ipynb, and run all cells. The notebook expects native 15-minute resolution.

Installing

pip install "castvalue_bench[bess]"

This benchmark also needs a GLPK solver binary on your system (not a pip package) — e.g. conda install -c conda-forge glpk, then point Optimizer(solverpath_exe=...) at the installed glpsol binary. Running the notebook additionally needs pandas, matplotlib, and jupyter.


About

CastValue is developed by FlexUp GmbH. For enquiries about forecast benchmarking or CastValue licensing, please get in touch.

Metadata

Release files for castvalue-bench 1.3.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for castvalue-bench 1.3.3
File Size Uploaded Provenance
castvalue_bench-1.3.3.tar.gz 67.9 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for castvalue-bench 1.3.3
File Interpreter ABI Platform
castvalue_bench-1.3.3-py3-none-any.whl Python 3 none any Details

Total release size: 127.2 kB

Release files / castvalue_bench-1.3.3.tar.gz

Download URL castvalue_bench-1.3.3.tar.gz
Size 67.9 kB
Tags Source
SHA-256 checksum
How to use checksums
edf98ac43fe307d2252b9283d0b9689dbaa4de9bf29a525046da051ecd99bb20
BLAKE2b-256 checksum
How to use checksums
5810b2828a0f980bfc5baf61b3b3c52e6a28f3b54fd4c1a1311b8f5d1ba75a6d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

Release files / castvalue_bench-1.3.3-py3-none-any.whl

Download URL castvalue_bench-1.3.3-py3-none-any.whl
Size 59.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7d752790c932742bceedf721fdcebe657fa11aa382e5f92fe93735316b6fdc53
BLAKE2b-256 checksum
How to use checksums
db6f5e71924315b5844ff242e71d7984bbe04fdef7f3919f7f3c2d657f2d0c83
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

Security for castvalue-bench 1.3.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for castvalue_bench-1.3.3.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 26, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page