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.
| 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.
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.
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.
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.
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.
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.
Relative to a flat (time-average) price, cannibalization costs solar about 14% and wind about 20% of its potential revenue per MWh:
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.pyThe 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.
- 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.
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 testsrenewable-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
| 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 |
- SMARD.de (Bundesnetzagentur), open data portal: https://www.smard.de/en
- Market data download centre and terms of use: https://www.smard.de/en/downloadcenter/download-market-data/
- Day-ahead prices are set by EPEX SPOT market coupling for the German-Luxembourg bidding zone.
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.
- 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.
- 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.
Mohammad Faisal, M.Sc. Power Engineering (Renewable Energy)
- GitHub: github.com/CodingxFaisal
- Email: mohammad.faisal@gmx.de
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.





