feat(billing): price house coal + heat network as documented proxies

Coal and heat networks have no national retail/cap rate, so the snapshot
left them null and BillDerivation raised UnpricedFuel — dropping those
certs from an offline cohort run. Add researched proxy rates (fuel-input
basis, sources + arithmetic in the JSON _note/_gaps): COAL 7.13 p/kWh
(NEP Nov 2025 coal uprated + DESNZ DUKES house-coal GCV) and HEAT_NETWORK
16.0 p/kWh + 69.4 p/day (Insite Energy operator sample; indicative, schemes
vary ~8-30). Both flagged proxy/indicative — sense-check estimates, not
market rates. Existing curated fuels are unchanged.

Replaces the unpriced-raises pin for these two with a positive rate pin;
off-peak stays unpriced pending the day/night accessor. Golden cohort now
runs 57/57 offline with zero errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-04 10:21:16 +00:00
parent 8b5ab1c59e
commit c6eaa53931
2 changed files with 21 additions and 12 deletions

View file

@ -16,12 +16,13 @@
"SMOKELESS": { "unit_rate_p_per_kwh": 10.0, "standing_charge_p_per_day": 0.0 },
"WOOD_LOGS": { "unit_rate_p_per_kwh": 8.83, "standing_charge_p_per_day": 0.0 },
"WOOD_PELLETS": { "unit_rate_p_per_kwh": 7.99, "standing_charge_p_per_day": 0.0, "_note": "bagged pellets; blown bulk is 6.76 p/kWh" },
"COAL": null,
"HEAT_NETWORK": null
"COAL": { "unit_rate_p_per_kwh": 7.13, "standing_charge_p_per_day": 0.0, "proxy": true, "_note": "PROXY, not a market rate. No current GB retail house-coal price (NEP Apr 2026 blank; domestic house-coal sale restricted since 2021). NEP Nov 2025 coal 48.50p/kg uprated by smokeless movement -> 52.39p/kg / DUKES house-coal GCV 7.3502 kWh/kg = 7.13 p/kWh." },
"HEAT_NETWORK": { "unit_rate_p_per_kwh": 16.0, "standing_charge_p_per_day": 69.4, "indicative": true, "_note": "INDICATIVE, not a regulated rate. Delivered-heat charge; no national tariff/cap. Insite Energy Nov 2024 operator sample avg 16.03 p/kWh + 69.42 p/day; schemes vary widely (~8-30 p/kWh per CMA/Heat Trust)." }
},
"_gaps": {
"COAL": "no standard domestic price (traditional house coal sale for domestic use is illegal in England)",
"HEAT_NETWORK": "scheme-specific; no national tariff or price-cap unit rate",
"COAL": "PROXY rate (see _note): no current national retail price; sense-check estimate so coal-heated certs model rather than erroring.",
"HEAT_NETWORK": "INDICATIVE rate (see _note): scheme-specific, no national tariff/cap; treat the bill as indicative.",
"ELECTRICITY_OFF_PEAK": "day/night split; priced once the off-peak slice adds the day/night accessor"
}
},
"_proxy_research": "COAL + HEAT_NETWORK proxies sourced 2026-06 via deep research (NEP retail + DESNZ DUKES gross CVs for coal; Insite Energy / CMA / Heat Trust for heat networks). Fuel-input basis. Existing fuels left on their prior curated values."
}

View file

@ -32,14 +32,22 @@ def test_snapshot_prices_metered_and_delivered_fuels_plus_seg() -> None:
assert rates.seg_export_p_per_kwh == 15.0
@pytest.mark.parametrize(
"fuel", [Fuel.HEAT_NETWORK, Fuel.COAL, Fuel.ELECTRICITY_OFF_PEAK]
)
def test_unpriced_fuels_raise_rather_than_defaulting(fuel: Fuel) -> None:
# Arrange — house coal + heat network have no national rate, and off-peak
# needs the day/night split a later slice adds (ADR-0014).
def test_coal_and_heat_network_carry_proxy_rates() -> None:
# Arrange — house coal + heat network have no national rate, but the
# snapshot now prices them as documented proxies (see the JSON _note) so
# those certs model instead of erroring.
rates = FuelRatesStaticFileRepository().get_current()
# Act / Assert
assert rates.unit_rate_p_per_kwh(Fuel.COAL) == 7.13
assert rates.unit_rate_p_per_kwh(Fuel.HEAT_NETWORK) == 16.0
assert rates.standing_charge_p_per_day(Fuel.HEAT_NETWORK) == 69.4
def test_off_peak_remains_unpriced_pending_the_day_night_accessor() -> None:
# Arrange — off-peak still needs the day/night split a later slice adds (ADR-0014).
rates = FuelRatesStaticFileRepository().get_current()
# Act / Assert
with pytest.raises(UnpricedFuel):
rates.unit_rate_p_per_kwh(fuel)
rates.unit_rate_p_per_kwh(Fuel.ELECTRICITY_OFF_PEAK)