The PPA Viability Scanner models five revenue strategies for a utility-scale wind or solar project over a 25-year project lifetime. It answers the question: "Given this project's costs and market context, which offtake strategy delivers the best risk-adjusted return?"
The engine runs all five scenarios simultaneously with identical cost and generation assumptions — only the revenue structure changes. This allows an apples-to-apples comparison of NPV, IRR, payback period, and LCOE across strategies.
Where t is the year (1-indexed), CF is the capacity factor,
and d is the annual degradation rate. Year 1 produces at full capacity;
degradation compounds from year 2 onward.
| Technology | Degradation d | Source |
|---|---|---|
| Onshore Wind | 0.5% / yr | NREL ATB 2024 |
| Solar PV | 0.7% / yr | NREL ATB 2024 |
Where cf0 = −CAPEX (initial investment),
and cft = Revenuet − OPEXt for years 1–25.
r is the discount rate (WACC).
Solved numerically using Newton-Raphson iteration with multi-start
([0.10, 0.01, 0.25, −0.05]), max 200 iterations.
Returns null if no root converges.
Both numerator and denominator are discounted at the WACC. OPEX is constant in real terms (no escalation). This is the same LCOE for all five scenarios because costs and generation are identical — only revenue changes.
Where decay = −1.5% / yr (real). This models long-term price erosion
from increasing renewable penetration and the merit-order effect. By year 25,
the forward merchant price is approximately 69% of the year-1 baseload.
100% spot-price exposure. No PPA contract.
All revenue comes from the spot market at the technology-weighted capture rate.
The capture rate (entso_capture_rate) reflects how much of the
baseload price the technology actually captures given its generation profile.
Risk level: HIGH — fully exposed to market volatility and price decay.
Fixed-price PPA for the contract term, then merchant tail.
During the PPA term, every MWh is sold at the fixed strike price. After the PPA expires, the project sells at prevailing merchant prices. If the PPA term covers the full 25 years, there is zero merchant exposure.
Risk level: NONE (full-term PPA) or MODERATE (partial-term).
PPA for contract term, then stressed merchant tail.
Identical to Fixed PPA during the contract term. After expiry, the merchant tail is stressed by −20% to model P90-like downside risk. This is a sensitivity scenario — the "what if merchant prices are worse than expected" case.
Risk level: HIGH — post-PPA merchant revenues are stressed.
Partial PPA + partial merchant simultaneously.
A fraction (blendRatio = 70% default) is sold under PPA at the fixed
price. The remaining 30% is sold on the spot market. The key innovation: the
residual capture rate accounts for the PPA "cream-skimming" the
best hours (typically peak), leaving the merchant portion with lower-value hours.
The residual capture rate is computed from the hour-band price structure and the technology's generation profile. Solar, concentrated in peak hours, suffers more cannibalisation when peak-hour PPAs remove the best prices from the merchant stack.
Risk level: MODERATE — split exposure reduces both upside and downside.
One-way floor CfD — guarantees minimum price, keeps upside.
The CfD acts as a one-way floor. If the market reference price falls below the
strike, the CfD tops up the difference. If the market price is above the strike,
the generator keeps the upside. The strike price defaults to
baseload × 1.05 (+5% premium).
Risk level: LOW (full-term) or MODERATE (partial-term).
| Constant | Value | Source |
|---|---|---|
| Hours per year | 8,760 | IRENA LCOE methodology |
| Project lifetime | 25 years | IRENA 2024, standard RE finance tenure |
| Wind degradation | 0.5% / yr | NREL ATB 2024 |
| Solar degradation | 0.7% / yr | NREL ATB 2024 |
| Forward merchant decay | −1.5% / yr (real) | BNEF / Aurora / IEA consensus |
| Downside stress factor | 0.80 (−20%) | Project finance P90 convention |
| Default blend ratio | 70% PPA / 30% merchant | Market convention (60–80% typical) |
| CfD strike premium | +5% over baseload | UK CfD AR6, DE EEG conventions |
| Default PPA term | 15 years | Market convention for utility-scale RE |
| Default WACC | 8% | IRENA 2024, emerging-market proxy |
The engine splits each day into three hour-bands (CET/CEST) and assigns technology-specific generation shares based on observed European generation profiles:
| Band | Hours | Solar share | Wind share |
|---|---|---|---|
| Peak | 08:00–19:59 | 55% | 35% |
| Shoulder | 06:00–07:59 + 20:00–21:59 | 40% | 35% |
| Off-peak | 22:00–05:59 | 5% | 30% |
Source: ENTSO-E generation profiles 2023–2024, aggregated across EU bidding zones. Rounding to nearest 5% for transparency. These are European averages and may differ for non-European markets.
| Capture Rate | Cannibalisation Risk |
|---|---|
| ≥ 90% | LOW — technology retains most market value |
| 75% – 89% | MODERATE — some value erosion |
| < 75% | HIGH — significant cannibalisation |
Source: BNEF 2024 "Capture Rate Trends", Aurora 2024, ENTSO-E market reports. <75% is commonly cited as "problematic" for new-build investment decisions in mature RE markets (DE, DK, ES).
| Data | Table | Source |
|---|---|---|
| Spot price by hour-band | entso_price_buckets |
ENTSO-E Transparency Platform (aggregated monthly by zone, band) |
| Capture rate by technology | entso_capture_rate |
ENTSO-E Transparency Platform (technology-weighted price / baseload) |
| Country CAPEX | country_capex |
IRENA Renewable Cost Database 2024 (USD/kW, converted to EUR via live FX) |
| Country OPEX | country_opex |
IRENA Renewable Cost Database 2024 (USD/kW/yr, converted to EUR) |
| Country WACC / financing | country_financing |
IRENA 2024, Damodaran country risk premiums |
| FX rates (USD→EUR) | KV cache | @fawazahmed0/currency-api (daily, cached in Cloudflare KV) |
All internal calculations are performed in EUR. CAPEX and OPEX from
IRENA are denominated in USD and converted at the point of data fetching using a
live exchange rate from @fawazahmed0/currency-api. The FX rate and
timestamp are included in the API response for transparency.
If FX data is unavailable, the engine falls back to a 1:1 USD/EUR rate and flags the result as stale.
After computing all five scenarios, the engine produces a comparison summary:
| Stage | Scope | Status |
|---|---|---|
| Stage 1 | Data layer, schemas, frontend skeleton | ✅ Complete |
| Stage 2 | 5 revenue scenarios + calculation engine + API | ✅ Complete |
| Stage 3 | PVGIS / GWA integration for capacity factor | Planned |
| Stage 4 | Country-specific capture rate projections | Planned |
| Stage 5 | PDF report export and shareable links | Planned |
workers/api/src/ppa_constants.js — All constants with source citationsworkers/api/src/ppa_math.js — Pure math functions (NPV, IRR, AEP, capture rate)workers/api/src/ppa_scenarios.js — Five scenario functions + orchestratorworkers/api/src/ppa.js — Cloudflare Worker route handlers + data assemblyworkers/api/src/__tests__/ppa_scenarios.test.js — 42 tests across 5 suitesQuestions? Contact the preFeasibility team or open an issue on GitHub.