Functional Weave
Code in TypeScript

energy.price-cap

Ofgem energy price cap unit rate and daily standing charge for a fuel and payment method on a date (GB average).

1.0.2 · published 2026-10-03 by charlie · Anterra

Pinned by 19 tests, run in TypeScript, Python and Rust.

What it does

The Ofgem default tariff cap ("energy price cap") in force on a date: the unit rate per kWh and the daily standing charge a supplier may charge a household on a standard variable tariff, for electricity or gas.

priceCap("electricity", "direct-debit", "2026-05-01")
  -> unitRate 24670 (24.67p/kWh), standingCharge 57210 (57.21p/day), vatBasisPoints 500

For example

  • priceCap(electricity, direct-debit, 2026-05-01) → fuel electricity, payment method direct-debit, region GB, unit rate 24,670, standing charge 57,210, vat basis points 5%, valid from 2026-04-01, valid to 2026-06-30 electricity in May 2026: 24.67p/kWh and 57.21p a day
  • priceCap(gas, direct-debit, 2026-05-01) → fuel gas, payment method direct-debit, region GB, unit rate 5,740, standing charge 29,090, vat basis points 5%, valid from 2026-04-01, valid to 2026-06-30 gas in May 2026: 5.74p/kWh and 29.09p a day
  • priceCap(electricity, direct-debit, 2025-04-01) → fuel electricity, payment method direct-debit, region GB, unit rate 27,030, standing charge 53,800, vat basis points 5%, valid from 2025-04-01, valid to 2025-06-30 the first day of a cap period takes the new cap

The function

The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.

export function priceCap(fuel: EnergyFuel, paymentMethod: PaymentMethod, onDate: string): PriceCap
fuelEnergyFuelelectricity (single-rate) or gas
paymentMethodPaymentMethoddirect-debit is the only method this version carries figures for
onDatedatethe day of supply
returnsPriceCap

The types it declares, generated into your project

export type EnergyFuel = "electricity" | "gas";

export type PaymentMethod = "direct-debit" | "standard-credit" | "prepayment";

/** The capped rates for one cap period, as Ofgem publishes them. */
export interface PriceCap {
  readonly fuel: string;
  readonly paymentMethod: string;
  /** GB: the England, Scotland and Wales average */
  readonly region: string;
  /** thousandths of a penny per kWh: 24.67p is 24670 */
  readonly unitRate: number;
  /** thousandths of a penny per day: 57.21p is 57210 */
  readonly standingCharge: number;
  /** VAT included in both figures: 500, or 0 where none applied */
  readonly vatBasisPoints: number;
  /** first day of the cap period */
  readonly validFrom: string;
  /** last day of the cap period */
  readonly validTo: string;
}

Your code names it in one line, in the file that uses it

import { priceCap } from "#fune/energy.price-cap@^1";
impl/typescript.ts · 47 lines · open · raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import { PRICE_CAP, PRICE_CAP_HISTORY } from "./energy_price_cap_data.ts";  ← this capability’s own data, compiled from data/price-cap.json into the same file by fune build
import { type EnergyFuel, type PaymentMethod, type PriceCap } from "./energy_price_cap_types.ts";

const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
const FUELS = ["electricity", "gas"];
const METHODS = ["direct-debit", "standard-credit", "prepayment"];

/** The Ofgem price cap unit rate and standing charge in force on `onDate`. */
export function priceCap(fuel: EnergyFuel, paymentMethod: PaymentMethod, onDate: string): PriceCap {
  if (!ISO_DATE.test(onDate)) {
    throw new RangeError(`onDate must be an ISO date (YYYY-MM-DD), received "${onDate}"`);
  }
  if (!FUELS.includes(fuel)) {
    throw new RangeError(`unknown fuel "${fuel}": expected electricity or gas`);
  }
  if (!METHODS.includes(paymentMethod)) {
    throw new RangeError(`unknown payment method "${paymentMethod}": expected direct-debit, standard-credit or prepayment`);
  }
  const rows = PRICE_CAP.filter((r) => r.fuel === fuel && r.paymentMethod === paymentMethod);
  if (rows.length === 0) {
    throw new RangeError(`no price cap figures for ${fuel} paid by ${paymentMethod}: this version carries GB-average Direct Debit figures only`);
  }
  const row = rows.find((r) => r.validFrom <= onDate && onDate <= r.validTo);
  if (row === undefined) {
    // A pruned build must not answer an old date with a newer cap.
    if (PRICE_CAP_HISTORY !== "full") {
      const earliest = rows.reduce((e, r) => (r.validFrom < e ? r.validFrom : e), rows[0].validFrom);
      if (onDate < earliest) {
        throw new RangeError(
          `no price cap for ${fuel}/${paymentMethod} on ${onDate}: this build was installed with history=${PRICE_CAP_HISTORY}, ` +
            `so it only carries periods from ${earliest}. Reinstall with history=full for older dates.`
        );
      }
    }
    throw new RangeError(`no price cap for ${fuel}/${paymentMethod} on ${onDate}`);
  }
  return {
    fuel,
    paymentMethod,
    region: row.region,
    unitRate: row.unitRate,
    standingCharge: row.standingCharge,
    vatBasisPoints: row.vatBasisPoints,
    validFrom: row.validFrom,
    validTo: row.validTo,
  };
}

Install

fune build

With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and nothing else, pins them in fune.lock, downloads only the TypeScript package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:

fune add energy.price-cap
Download for TypeScript energy.price-cap-1.0.2-typescript.fune · 22,171 bytes sha256 28d3a8a8c0ddeae7649bd6ad0ea025b3dea09d9b86afce94396d81421dde2813

The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./energy.price-cap-1.0.2-typescript.fune, or fetch it from a terminal with fune pull energy.price-cap@1.0.2:typescript.

The whole function, every language, is one file too: energy.price-cap-1.0.2.fune, 27,981 bytes, sha256 b5ceea9f411ec53104d7e2ca1a14be3bffe7b245a623d40c0148b2e68f993042. It installs into a project of any language.

Customise it in your app

The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.

before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.

// fune: before energy.price-cap

after — your function gets the result and the arguments, and returns the final result.

// fune: after energy.price-cap

replace — it requires no other capability, so there is no dependency to replace.

step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show energy.price-cap --steps.

// fune: step energy.price-cap after <n|label>

Tests

A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.

CaseArgumentsExpected
electricity in May 2026: 24.67p/kWh and 57.21p a day electricity, direct-debit, 2026-05-01 → fuel electricity, payment method direct-debit, region GB, unit rate 24,670, standing charge 57,210, vat basis points 5%, valid from 2026-04-01, valid to 2026-06-30
gas in May 2026: 5.74p/kWh and 29.09p a day gas, direct-debit, 2026-05-01 → fuel gas, payment method direct-debit, region GB, unit rate 5,740, standing charge 29,090, vat basis points 5%, valid from 2026-04-01, valid to 2026-06-30
the first day of a cap period takes the new cap electricity, direct-debit, 2025-04-01 → fuel electricity, payment method direct-debit, region GB, unit rate 27,030, standing charge 53,800, vat basis points 5%, valid from 2025-04-01, valid to 2025-06-30
the last day of a cap period keeps the old one electricity, direct-debit, 2025-03-31 → fuel electricity, payment method direct-debit, region GB, unit rate 24,860, standing charge 60,970, vat basis points 5%, valid from 2025-01-01, valid to 2025-03-31
gas, July to September 2025 gas, direct-debit, 2025-08-15 → fuel gas, payment method direct-debit, region GB, unit rate 6,330, standing charge 29,820, vat basis points 5%, valid from 2025-07-01, valid to 2025-09-30
gas standing charge jumps in October 2025 gas, direct-debit, 2025-10-01 → fuel gas, payment method direct-debit, region GB, unit rate 6,290, standing charge 34,030, vat basis points 5%, valid from 2025-10-01, valid to 2025-12-31
electricity, January to March 2026 electricity, direct-debit, 2026-02-28 → fuel electricity, payment method direct-debit, region GB, unit rate 27,690, standing charge 54,750, vat basis points 5%, valid from 2026-01-01, valid to 2026-03-31
electricity from 1 October 2026 carries no VAT electricity, direct-debit, 2026-10-01 → fuel electricity, payment method direct-debit, region GB, unit rate 26,320, standing charge 54,830, vat basis points 0%, valid from 2026-10-01, valid to 2026-12-31
gas from 1 October 2026 still includes 5% VAT gas, direct-debit, 2026-12-31 → fuel gas, payment method direct-debit, region GB, unit rate 7,970, standing charge 29,680, vat basis points 5%, valid from 2026-10-01, valid to 2026-12-31
the earliest period carried, October 2024 gas, direct-debit, 2024-10-01 → fuel gas, payment method direct-debit, region GB, unit rate 6,240, standing charge 31,660, vat basis points 5%, valid from 2024-10-01, valid to 2024-12-31
Show the other 9 tests
CaseArgumentsExpected
electricity, July to September 2026 electricity, direct-debit, 2026-09-30 → fuel electricity, payment method direct-debit, region GB, unit rate 26,110, standing charge 57,190, vat basis points 5%, valid from 2026-07-01, valid to 2026-09-30
a period not yet announced is an error, not the latest cap electricity, direct-debit, 2027-01-01 → error: no price cap for electricity/direct-debit on 2027-01-01
before the earliest period carried gas, direct-debit, 2024-09-30 → error: no price cap for gas/direct-debit on 2024-09-30
prepayment figures are not carried electricity, prepayment, 2026-05-01 → error: no price cap figures for electricity paid by prepayment
an unknown fuel oil, direct-debit, 2026-05-01 → error: unknown fuel "oil"
an unknown payment method gas, cash, 2026-05-01 → error: unknown payment method "cash"
a malformed date gas, direct-debit, 2026-5-1 → error: onDate must be an ISO date
a onDate with a trailing newline is refused gas, direct-debit, 2026-09-16 → error: onDate must be an ISO date (YYYY-MM-DD), received "2026-09-16 "
a onDate in Arabic-Indic digits is refused gas, direct-debit, ٢٠٢٦-٠٩-١٦ → error: onDate must be an ISO date (YYYY-MM-DD), received "٢٠٢٦-٠٩-١٦"

More from the author

## Scope of the data

- **GB average, Direct Debit, single-rate electricity.** These are the figures Ofgem publishes as the headline cap for each period: the average across England, Scotland and Wales for a customer paying by Direct Debit. Nine periods, **1 October 2024 to 31 December 2026**. - **Not regional.** The cap differs by the 14 electricity regions. Ofgem publishes the regional rates only in its interactive tables and spreadsheets, which this version does not carry; a regional bill needs the supplier's own rates (which may be below the cap anyway). - **Not standard credit or prepayment.** The types list them because the cap is set for each, but Ofgem does not publish GB-average unit rates for them in a form that could be checked here, so asking for one is an error rather than a guess. They will be new rows, not new code. - **Not Economy 7 / multi-rate.** Ofgem caps a multi-register meter as a whole rather than publishing a GB-average day and night rate.

## Units and VAT

Rates are integers in **thousandths of a penny**, matching the rest of the `energy.*` capabilities (`energy.tariff-time-of-use`, `energy.bill-calculate`); Ofgem's figures are rounded to two decimal places, so the last digit is always 0.

The figures **include VAT**, and `vatBasisPoints` says how much: 5% on everything, except electricity from 1 October 2026, which the government zero-rated for domestic customers in Great Britain from 1 October 2026 to 31 March 2027 (Ofgem: "It does not include VAT from 1 October 2026 to 31 March 2027"). To get the pre-VAT rate for a bill, divide by `1 + vatBasisPoints / 10000`.

## Dates

A period runs `validFrom` to `validTo` inclusive (calendar quarters). A date outside every period this version carries is an error: the cap for a period not yet announced is not today's cap. A build installed with `history=current` keeps only periods still running and refuses older dates with an explanation.

## Sources

Ofgem news releases, "Changes to energy price cap between ..." (each gives the Direct Debit GB-average unit rate and standing charge for electricity and gas, and states the VAT they include):

- 1 January to 31 March 2026: https://www.ofgem.gov.uk/news/changes-energy-price-cap-between-1-january-and-31-march-2026 - 1 April to 30 June 2026: https://www.ofgem.gov.uk/news/changes-energy-price-cap-between-1-april-and-30-june-2026 - 1 July to 30 September 2026: https://www.ofgem.gov.uk/news/changes-energy-price-cap-between-1-july-and-30-september-2026 - 1 October to 31 December 2026: https://www.ofgem.gov.uk/news/changes-energy-price-cap-between-1-october-and-31-december-2026

Ofgem, "Energy price cap" consumer page, as archived by the Internet Archive (https://web.archive.org/web/2025*/https://www.ofgem.gov.uk/information-consumers/energy-advice-households/energy-price-cap) in February, May, August and November 2025, each showing the current and next period: October to December 2024, and each quarter of 2025.

Ofgem, "Energy price cap (default tariff) levels", https://www.ofgem.gov.uk/energy-regulation/domestic-and-non-domestic/energy-pricing-rules/energy-price-cap/energy-price-cap-default-tariff-levels (the regional and per-payment-method models this version does not carry).

1.0.1 fixes Python accepting a trailing newline or non-ASCII digits in onDate; adds tests.

## Notices

Contains public sector information licensed under the Open Government Licence v3.0 (https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/).

Source: Ofgem (Crown copyright), reused under the Open Government Licence v3.0.

1.0.2 adds its attribution notices (NOTICE). The code and the tests are unchanged.

Files

PathBytes
NOTICE260
README.md4,075
data/price-cap.json5,846
impl/python.py2,260
impl/rust.rs3,311
impl/typescript.ts2,144
vectors.json4,696