A quant toolkit for structured power trading: the spark spread and its tolling optionality (gas-plant economics as a strip of spread options), renewable capture rates and PPA valuation, plus forward-curve shaping and scenario analysis. Python, look-ahead-free, unit-tested.
A structured-power desk doesn't trade a flat price — it trades shape and optionality: a flexible gas plant is a strip of options on the spark spread; a wind/solar PPA is worth its capture rate, not the headline forward; and risk is run on a gas × power scenario grid. This repo implements those four desk primitives end-to-end.
| Module | Desk question it answers |
|---|---|
src/spark.py |
What is the spark spread, and what is a flexible plant's optionality worth (tolling, Kirk's spread-option formula)? |
src/capture.py |
What does a wind/solar asset actually capture vs baseload, and what is a PPA worth? |
src/curve.py |
Peak/off-peak shaping of a monthly forward; the gas × power scenario grid. |
src/data.py |
LSEG loader or a documented, reproducible illustrative dataset. |
The spark spread is the margin of a gas plant: power − heat_rate·gas − var_cost
($/MWh). A plant runs only when this is positive, so its value is a strip of daily
call options on the spark spread — a tolling agreement. Each is a two-asset spread
option, priced with Kirk's approximation (the desk-standard closed form). Add a
carbon term (carbon·emission_factor) for the EU clean spark spread.
Renewables earn the price only when generating. Solar produces midday — exactly when solar floods the grid and depresses prices (the duck curve) — so it captures below baseload. The capture rate is the number that makes or breaks a PPA price:
solar: capture rate 93.8% (capture $26.2 vs baseload $27.9) ← penalised
wind : capture rate 104.9% (capture $29.3 vs baseload $27.9) ← closer to 100%
A fixed-price PPA is then marked as capture_price − strike per MWh produced, with the
breakeven strike = capture price (the fair fixed price).
Power is quoted in monthly peak/off-peak blocks but settles hourly. shape_profile
derives the average hourly shape so a monthly forward can be shaped to an hourly curve;
peak_offpeak_means gives the peak/off-peak ratio.
The standard 2-D risk view a structured desk runs — spark spread across joint gas-price and power-price shocks:
gas-50% gas-25% gas+0% gas+25% gas+50%
power+0% 12.9 6.9 1.0 -5.0 -11.0
power+20% 18.5 12.5 6.5 0.5 -5.4
The repo runs out-of-the-box on a documented, fully reproducible illustrative
dataset (src/data.synthetic_hourly) that reproduces the structural features the
analytics exploit (seasonal + diurnal power shape, the midday solar depression, an
anti-correlated wind profile, mean-reverting gas). It is not market data.
To run on real data, scripts/pull_power_data.py fetches Henry Hub gas + a U.S. ISO
power hub from LSEG (entitlement-dependent; edit the RICs to your hub). Real market
data is gitignored and never committed.
pip install -r requirements.txt
python scripts/run_analysis.py # illustrative (no entitlement needed)
python scripts/run_analysis.py --lseg # real LSEG data (after pull_power_data.py)
pytest -q- The illustrative dataset is for demonstration; capture rates and spark levels reflect the synthetic structure, not a real ISO. Re-run with LSEG data for live numbers.
- Kirk's approximation is the desk-standard spread-option proxy; for deep out-of-the-money or high-correlation regimes a Monte-Carlo or Bjerksund–Stensland spread option is more accurate.
- Forward vols are estimated from daily average prices; a real desk uses quoted option implieds.
- Research/education only — not trading advice.
power-structuring-analytics/
├── README.md · LICENSE · requirements.txt
├── src/
│ ├── data.py # LSEG loader + reproducible illustrative generator
│ ├── spark.py # spark spread, implied heat rate, Kirk tolling option
│ ├── capture.py # renewable capture rate + PPA valuation
│ └── curve.py # peak/off-peak shaping + scenario grid
├── scripts/
│ ├── pull_power_data.py # LSEG fetch (audit-first; data/ gitignored)
│ └── run_analysis.py # end-to-end report + figure
├── tests/test_analytics.py
└── docs/assets/
Built with Python (pandas, numpy, scipy, matplotlib). Methods: spread-option pricing (Kirk), generation-weighted capture analysis, load shaping.



