Electricity Market Price Formation — Welfare-Maximising Capacity Investment & Dispatch with Zero Variable Cost Intermittency
Get Flexing — How to Use This Model Documentation & README on GitHub Contact me
Real electricity demand varies hour by hour. Rather than modelling all 8,760 hours, the model uses a Load Duration Curve that groups hours by demand level into three blocks.
| Block | Represents | Default |
|---|---|---|
| Winter Peak | Coldest, highest-demand hours | 300h |
| Shoulder | Spring, autumn, milder winter | 3,000h |
| Low Demand | Summer & overnight | 5,460h |
Set the hours for each block (should sum to 8,760). Demand levels come from the tier definitions.
Each LDC block is split into two sub-blocks to capture intermittency:
| Parameter | Meaning |
|---|---|
| % Hours | Fraction of block hours in this sub-block (must sum to 100%) |
| % Availability | Fraction of installed renewables capacity available |
This creates intra-block price differentials that make storage valuable within a block — charging when renewables are plentiful, discharging when scarce.
Each block is split into Low Renewables and High Renewables sub-blocks to capture intermittency. % Hours must sum to 100 per block.
Demand in each block is divided into three tiers by willingness to pay:
| Parameter | Meaning |
|---|---|
| Quantity (GW) | GW of this tier present during the block |
| VoLL | Max price this demand will pay; above this it is curtailed |
| Shift Cost | Cost to move demand to a later, cheaper block (downward only) |
Expandable demand activates when prices fall below its value — representing new consumption attracted by cheap power.
The model determines how much of each technology to build. No capacities are pre-set.
Storage conversion: power cost = (connection/1000)×CRF; energy cost = (cell cost/1000)×CRF, amortised over cycles/year. CRF uses discount rate and asset life.
The model determines optimal capacities. No fixed capacities are pre-set.
Zero variable cost generation (wind, solar). Output depends on the Renewables Availability Profile above.
Firm baseload with zero carbon emissions. Planned outage reduces availability in all periods. Peak unavailability is an additional derating applied only during winter peak sub-blocks.
CapEx is annualised using the capital recovery factor (CRF) with the discount rate: power cost = (connection/1000)×CRF; energy cost = (cell cost/1000)×CRF, then amortised over cycles.
Sized to peak system generation. The constraint binds at the sub-block with the highest total dispatched power.
Takes system clearing prices and optimises a single household’s demand, storage, and curtailment.
Raw demand blocks as entered, before any optimisation, shifting, or storage.
Total generation by source in each sub-block after optimisation, sorted by GW.
Mouse-over within blocks for more information.
Clearing prices sorted high to low, with demand served, shifted, and storage activity per sub-block.
Each bar shows GW × hours for that sub-block. Storage charging appears as demand; storage discharging appears as supply.
Comments, bug reports, and ideas for new features are very welcome.
If you'd like to make a public suggestion about the model, please open a GitHub issue. If you'd prefer to contact Tony directly, use the email link below.
This is a beta project. Feedback welcome!
Please get in touch with comments or suggestions.