Skip to content

Repository files navigation

Renewable Price Cannibalization in Germany

As solar and wind cover a larger share of demand, they push the day-ahead price down at exactly the times they generate, which lowers the price they themselves capture and increasingly drives prices negative. This repository measures that effect on six years of real German market data and presents it as both a static report and an interactive dashboard.

The question it answers is not "are renewables cheap" but "how much market value does the next gigawatt of solar remove, and at what point does the market start paying operators to switch off." Those are the numbers a renewable developer, a PPA desk or a grid planner works with.

Data comes from SMARD.de (Bundesnetzagentur): hourly generation, load and day-ahead price for Germany, 2019–2024. Free, no API key.

Project developed June 2026.


Headline findings (Germany, 2019–2024)

Baseload (time-average) price 95.6 EUR/MWh
Solar value factor 0.86 overall, falling from 0.93 in 2019 to 0.59 in 2024
Wind value factor 0.80
Negative-price hours 1,475
Merit-order slope 2.5 EUR/MWh per GW of residual load (fuel regime removed)
VRE energy generated at negative prices 5.6% (60.7 TWh)
Value lost vs. a flat price ~4.1 bn EUR (solar) and ~14.9 bn EUR (wind)

The story in one chart: solar's value factor drops sharply as its share of the system grows, from capturing 93% of the average price in 2019 to 59% in 2024. That decline is cannibalization, measured directly.

Value factor trend


The mechanism, in three charts

1. The cannibalization curve

Bucket every hour by how much of demand VRE covered, then read off the average price. It falls steadily, and once VRE exceeds about 90% of load the majority of hours clear at negative prices.

Cannibalization curve

2. The merit-order effect

Price is set by residual load (demand minus wind and solar): more renewables means lower residual load, a cheaper marginal plant, and a lower price. The complication is that over 2019–2024 the price level is dominated by the gas-price regime, so a raw fit gives only R²=0.13. Detrending the price over about 30 days removes the fuel regime and reveals the clean merit-order signal underneath (R²=0.41 pooled, and up to 0.75 within a single stable-fuel year). The slope itself scales with fuel cost: roughly 1 EUR/MWh per GW in 2019–20, but about 7 EUR/MWh per GW during the 2022 gas crisis.

Merit-order effect

3. Why solar is hit hardest

Solar generation is concentrated in the middle of the day, so it floods exactly the hours it is paid for and deepens the midday price trough (the "duck curve"). Wind is spread more evenly across the clock and cannibalizes less.

Duck curve


Negative prices and curtailment

Negative-price hours have risen with VRE penetration. Over the period, 5.6% of all VRE energy was generated while prices were negative, energy a rational operator without a subsidy would curtail.

Negative hours

This is a market proxy for curtailment (generation during negative-price hours). Physical curtailment also happens for grid-congestion reasons and would need TSO redispatch data, which is out of scope here.


Revenue impact

Relative to a flat (time-average) price, cannibalization costs solar about 14% and wind about 20% of its potential revenue per MWh:

Revenue impact


Interactive dashboard

A Streamlit app puts all of the above behind a live year-range filter:

pip install -r requirements.txt
python scripts/download_data.py          # one-time SMARD download (cached)
streamlit run app/streamlit_app.py

The dashboard recomputes every metric and chart for the selected window, so the value factor collapse is visible as you move the start year forward. It runs as is on Streamlit Community Cloud for a shareable live link.


Definitions

  • Capture price of a technology = Σ(price × generation) / Σ(generation), the volume-weighted price it actually receives.
  • Value factor = capture price / baseload price. Below 1 means cannibalization.
  • Residual load = load − wind − solar, the demand that dispatchable plant must cover and the fundamental price driver.
  • Merit-order slope = ∂price/∂(residual load), fit on fuel-regime-detrended price so the gas crisis does not masquerade as a renewables effect.

Running it

See RUNNING.md for full setup, including a VS Code walk-through and the dashboard. The short version:

pip install -r requirements.txt
python scripts/download_data.py    # download and cache SMARD data
python scripts/build_report.py     # compute metrics and render all charts to results/
streamlit run app/streamlit_app.py # optional interactive dashboard
pytest                             # run the tests

Repository layout

renewable-cannibalization/
├── config/settings.yaml     # data window and analysis parameters
├── src/cannibal/
│   ├── data/smard.py        # SMARD generation/price/load client (cached)
│   ├── analysis.py          # value factor, merit order, negative prices, curtailment, revenue
│   ├── plots.py             # the six report figures
│   └── config.py            # typed settings
├── app/streamlit_app.py     # interactive dashboard
├── scripts/                 # download_data and build_report
├── tests/                   # value factor, capture price, merit-order recovery, curtailment
├── notebooks/               # exploratory scripts
└── results/                 # generated charts and CSVs

Data

Field Source SMARD series ID
Day-ahead price (DE/LU) EPEX SPOT via SMARD 4169
Solar generation (actual) German TSOs via SMARD 4068
Onshore wind (actual) German TSOs via SMARD 4067
Offshore wind (actual) German TSOs via SMARD 1225
Load (actual) German TSOs via SMARD 410

All series are realised (actual) values for Germany, fetched hourly in weekly chunks and cached to data/raw. Timestamps are parsed as UTC and converted to Europe/Berlin so daylight-saving days are handled correctly.


Caveats

  • Germany / DE-LU bidding zone only. The framework generalises to any SMARD or ENTSO-E zone, so cross-country comparison is a natural extension.
  • Value factors are measured against the day-ahead price and ignore balancing, intraday and PPA structures that a real asset would also see.
  • The curtailment figure is a market proxy, not measured redispatch.
  • Merit-order detrending controls for the fuel regime but not perfectly; the per-year fits are the cleaner read.

Possible extensions

  • Cross-country value factors (DE vs. ES vs. FR) to show how grid mix and interconnection change the cannibalization slope.
  • Storage overlay: how much battery duration is needed to slow the value-factor decline (this connects to the companion BESS dispatch project).
  • Forward-looking projection of value factors under stated 2030 capacity targets.

Author

Mohammad Faisal, M.Sc. Power Engineering (Renewable Energy)

License

Released under the MIT License. See LICENSE.

Data belongs to SMARD.de / Bundesnetzagentur and is used under their terms; see the download-centre link above.

About

Analysis and Streamlit dashboard of renewable price cannibalization in Germany (2019–2024), built on real SMARD data.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages