Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/2072.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Corrects the Pension Credit standard minimum guarantee projection to follow the statutory May-July earnings input with a non-negative floor.
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
description: Minimum income guarantee (basic amount) for Pension Credit.
description: >-
Minimum income guarantee (basic amount) for Pension Credit. Values after the
last enacted rate use the non-negative May-July average weekly earnings input
from the annual statutory review as a forecast floor; they are not enacted
future rates.
SINGLE:
values:
2015-04-06: 151.2
Expand All @@ -14,7 +18,7 @@ SINGLE:
2025-04-01: 221.85
2026-04-01: 238
metadata:
uprating: gov.benefit_uprating_cpi
uprating: gov.economic_assumptions.indices.statutory_earnings_floor
COUPLE:
values:
2015-04-06: 230.85
Expand All @@ -30,7 +34,7 @@ COUPLE:
2025-04-01: 338.61
2026-04-01: 363.25
metadata:
uprating: gov.benefit_uprating_cpi
uprating: gov.economic_assumptions.indices.statutory_earnings_floor
metadata:
period: week
label: Pension Credit minimum guarantee
Expand All @@ -40,3 +44,9 @@ metadata:
href: https://www.legislation.gov.uk/uksi/2002/1792/regulation/6/2021-04-12
- title: Benefit and pension rates 2026 to 2027
href: https://www.gov.uk/government/publications/benefit-and-pension-rates-2026-to-2027/proposed-benefit-and-pension-rates-2026-to-2027
- title: Social Security Administration Act 1992 section 150A
href: https://www.legislation.gov.uk/ukpga/1992/5/section/150A
- title: Social Security Administration (Northern Ireland) Act 1992 section 132A
href: https://www.legislation.gov.uk/ukpga/1992/8/section/132A
- title: A14/2025 Housing Benefit uprating for the financial year ending March 2027
href: https://www.gov.uk/government/publications/housing-benefit-adjudication-circulars-2025/a142025-housing-benefit-uprating-for-the-financial-year-ending-march-2027
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Build the State Pension uprating series from the statutory inputs.
"""Build statutory earnings and State Pension uprating series.

Each April the basic and new State Pension rise by the highest of:

Expand Down Expand Up @@ -29,10 +29,13 @@
earnings path started from its level in the year before the guarantee first
applied. Current law leaves it off.

The output parameter ``gov.economic_assumptions.yoy_growth.triple_lock`` holds
the rate taking effect in April of each year, keyed to 1 January as the other
growth series are, and runs to one year past the last year of the
economic-assumption series.
The output parameters hold the rates taking effect in April of each year,
keyed to 1 January as the other growth series are. ``triple_lock`` applies the
State Pension rule. ``statutory_earnings_floor`` holds the non-negative
earnings component for amounts, including the Pension Credit standard minimum
guarantee, that section 150A protects without extending the triple lock to
them. Both run to one year past the last year of the economic-assumption
series.
"""

from dataclasses import dataclass
Expand Down Expand Up @@ -155,6 +158,13 @@ def uprating_rates(years: Dict[int, UpratingYear]) -> Dict[int, float]:
return rates


def statutory_earnings_floor_rates(
years: Dict[int, UpratingYear],
) -> Dict[int, float]:
"""Return the non-negative earnings rate from each statutory review."""
return {year: max(inputs.earnings, 0.0) for year, inputs in years.items()}


def _flag(parameter: Parameter, instant: str) -> bool:
value = parameter(instant)
return bool(value) if value is not None else False
Expand Down Expand Up @@ -200,22 +210,53 @@ def read_uprating_years(parameters: ParameterNode) -> Dict[int, UpratingYear]:


def add_triple_lock(parameters: ParameterNode) -> ParameterNode:
"""Add ``gov.economic_assumptions.yoy_growth.triple_lock``."""
rates = uprating_rates(read_uprating_years(parameters))
new_parameter = Parameter(
"""Add the statutory earnings floor and State Pension triple lock."""
years = read_uprating_years(parameters)
triple_lock_rates = uprating_rates(years)
earnings_floor_rates = statutory_earnings_floor_rates(years)
triple_lock = Parameter(
"gov.economic_assumptions.yoy_growth.triple_lock",
data={
"description": (
"State Pension uprating taking effect in April of each year."
),
"values": {f"{year}-01-01": rate for year, rate in rates.items()},
"values": {
f"{year}-01-01": rate for year, rate in triple_lock_rates.items()
},
"metadata": {
"unit": "/1",
"label": "State Pension uprating rate",
},
},
)
parameters.gov.economic_assumptions.yoy_growth.add_child("triple_lock", triple_lock)
statutory_earnings_floor = Parameter(
"gov.economic_assumptions.yoy_growth.statutory_earnings_floor",
data={
"description": (
"Non-negative May-July average weekly earnings growth used "
"as the statutory uprating floor under section 150A."
),
"values": {
f"{year}-01-01": rate for year, rate in earnings_floor_rates.items()
},
"metadata": {
"unit": "/1",
"label": "Statutory earnings uprating floor",
"reference": [
{
"title": (
"Social Security Administration Act 1992 section 150A"
),
"href": (
"https://www.legislation.gov.uk/ukpga/1992/5/section/150A"
),
}
],
},
},
)
parameters.gov.economic_assumptions.yoy_growth.add_child(
"triple_lock", new_parameter
"statutory_earnings_floor", statutory_earnings_floor
)
return parameters
54 changes: 51 additions & 3 deletions policyengine_uk/tests/test_triple_lock_statutory_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
earnings growth from the previous year. These tests check that forecasts of
those inputs follow the OBR where it publishes them, fall back to
calendar-year growth after, respond to macro scenarios applied before the
data load, and can be set directly; and that the earnings-path guarantee
models one reading of the plan announced in September 2026 as a
parameter reform.
data load, and can be set directly. They also check that Pension Credit uses
the same May-July earnings input without inheriting the CPI and 2.5% elements
of the State Pension triple lock, and that the earnings-path guarantee models
one reading of the plan announced in September 2026 as a parameter reform.
"""

import numpy as np
Expand Down Expand Up @@ -43,10 +44,21 @@ def uprating(parameters, year):
return parameters.gov.economic_assumptions.yoy_growth.triple_lock(f"{year}-01-01")


def statutory_earnings_floor(parameters, year):
return parameters.gov.economic_assumptions.yoy_growth.statutory_earnings_floor(
f"{year}-01-01"
)


def new_state_pension_weekly(parameters, year):
return parameters.gov.dwp.state_pension.new_state_pension.amount(f"{year}-06-01")


def minimum_guarantee_weekly(parameters, relation_type, year):
guarantee = parameters.gov.dwp.pension_credit.guarantee_credit.minimum_guarantee
return getattr(guarantee, relation_type)(f"{year}-06-01")


def statutory_inputs(parameters):
return parameters.gov.economic_assumptions.statutory_uprating_inputs

Expand All @@ -63,6 +75,42 @@ def test_april_2027_uses_published_may_july_2026_earnings():
)


def test_pension_credit_uses_the_state_pension_statutory_earnings_input():
parameters = system.parameters

assert statutory_inputs(parameters).awe_total_pay_may_july(
"2026-07-01"
) == pytest.approx(0.039)
assert statutory_earnings_floor(parameters, 2027) == pytest.approx(0.039)
assert minimum_guarantee_weekly(parameters, "SINGLE", 2027) == pytest.approx(
238 * 1.039, rel=1e-5
)
assert minimum_guarantee_weekly(parameters, "COUPLE", 2027) == pytest.approx(
363.25 * 1.039, rel=1e-5
)


def test_pension_credit_earnings_floor_rounds_and_never_reduces_the_guarantee():
parameters = parameters_under(
{
f"{INPUTS}.awe_total_pay_may_july": {
"2027": 0.0124,
"2028": -0.004,
}
}
)

assert [
statutory_earnings_floor(parameters, year) for year in (2028, 2029)
] == pytest.approx([0.012, 0.0])
for relation_type in ("SINGLE", "COUPLE"):
guarantee_2027 = minimum_guarantee_weekly(parameters, relation_type, 2027)
guarantee_2028 = minimum_guarantee_weekly(parameters, relation_type, 2028)
guarantee_2029 = minimum_guarantee_weekly(parameters, relation_type, 2029)
assert guarantee_2028 == pytest.approx(guarantee_2027 * 1.012, rel=1e-5)
assert guarantee_2029 == pytest.approx(guarantee_2028, rel=1e-5)


def test_every_published_year_has_its_own_value():
"""A missing year would silently carry the previous year's figure."""
inputs = statutory_inputs(system.parameters)
Expand Down
Loading