From d0917f4ca3ff2e6d8dbfbbbc54234b176afe9cf8 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 22 Oct 2026 13:50:24 -0400 Subject: [PATCH 1/2] Project Scottish benefits using September CPI and annual 5p rounding --- changelog.d/scottish-september-cpi.fixed.md | 1 + .../pawhp/amount/higher.yaml | 18 ++- .../amount/higher_shared_with_80_or_over.yaml | 18 ++- .../amount/higher_shared_with_under_80.yaml | 18 ++- .../pawhp/amount/lower.yaml | 18 ++- .../pawhp/amount/lower_shared.yaml | 18 ++- .../scottish_child_payment/amount.yaml | 26 +++- .../scottish_child_payment.yaml | 12 +- .../september_cpi_uprating.yaml | 104 +++++++++++++ .../tests/test_scottish_benefit_uprating.py | 142 ++++++++++++++++++ .../test_scottish_child_payment_baby_bonus.py | 4 +- .../tests/test_september_cpi_uprating.py | 25 ++- 12 files changed, 379 insertions(+), 25 deletions(-) create mode 100644 changelog.d/scottish-september-cpi.fixed.md create mode 100644 policyengine_uk/tests/policy/baseline/gov/social_security_scotland/september_cpi_uprating.yaml create mode 100644 policyengine_uk/tests/test_scottish_benefit_uprating.py diff --git a/changelog.d/scottish-september-cpi.fixed.md b/changelog.d/scottish-september-cpi.fixed.md new file mode 100644 index 0000000000..dd7568d232 --- /dev/null +++ b/changelog.d/scottish-september-cpi.fixed.md @@ -0,0 +1 @@ +Project Scottish Child Payment and Pension Age Winter Heating Payment by the previous September's CPI with annual nearest-5p rounding, replacing outdated Scottish Child Payment forecast anchors from 2027. diff --git a/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/higher.yaml b/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/higher.yaml index e94ee4bfaf..933d8e284b 100644 --- a/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/higher.yaml +++ b/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/higher.yaml @@ -20,4 +20,20 @@ values: metadata: unit: currency-GBP label: PAWHP higher (80+) amount - uprating: gov.economic_assumptions.indices.obr.consumer_price_index + # September CPI and nearest-5p rounding follow Scottish Ministers' current + # policy; s86A leaves the choice of relevant prices to Ministers. + # Apply each year's rise to the previous rounded rate each April. + uprating: + parameter: gov.benefit_uprating_cpi + at_defined_interval: + start: "0001-01-01" + end: "0002-01-01" + enactment: "0002-04-01" + rounding: + type: nearest + interval: 0.05 + reference: + - title: Social Security (Scotland) Act 2018, section 86A (relevant prices chosen by Scottish Ministers) + href: https://www.legislation.gov.uk/asp/2018/9/section/86A + - title: Social Security Assistance in Scotland - up-rating for inflation 2026-2027, sections 2 and 3 (September CPI and nearest 5p) + href: https://www.gov.scot/publications/social-security-assistance-scotland-up-rating-inflation-2026-27/pages/3/ diff --git a/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/higher_shared_with_80_or_over.yaml b/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/higher_shared_with_80_or_over.yaml index d1524024fb..963a6943d0 100644 --- a/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/higher_shared_with_80_or_over.yaml +++ b/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/higher_shared_with_80_or_over.yaml @@ -15,4 +15,20 @@ values: metadata: unit: currency-GBP label: PAWHP shared amount 80 and over, living with 80 and over - uprating: gov.economic_assumptions.indices.obr.consumer_price_index + # September CPI and nearest-5p rounding follow Scottish Ministers' current + # policy; s86A leaves the choice of relevant prices to Ministers. + # Apply each year's rise to the previous rounded rate each April. + uprating: + parameter: gov.benefit_uprating_cpi + at_defined_interval: + start: "0001-01-01" + end: "0002-01-01" + enactment: "0002-04-01" + rounding: + type: nearest + interval: 0.05 + reference: + - title: Social Security (Scotland) Act 2018, section 86A (relevant prices chosen by Scottish Ministers) + href: https://www.legislation.gov.uk/asp/2018/9/section/86A + - title: Social Security Assistance in Scotland - up-rating for inflation 2026-2027, sections 2 and 3 (September CPI and nearest 5p) + href: https://www.gov.scot/publications/social-security-assistance-scotland-up-rating-inflation-2026-27/pages/3/ diff --git a/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/higher_shared_with_under_80.yaml b/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/higher_shared_with_under_80.yaml index 22941d5904..8161898a0a 100644 --- a/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/higher_shared_with_under_80.yaml +++ b/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/higher_shared_with_under_80.yaml @@ -15,4 +15,20 @@ values: metadata: unit: currency-GBP label: PAWHP shared amount 80 and over, living with under 80 - uprating: gov.economic_assumptions.indices.obr.consumer_price_index + # September CPI and nearest-5p rounding follow Scottish Ministers' current + # policy; s86A leaves the choice of relevant prices to Ministers. + # Apply each year's rise to the previous rounded rate each April. + uprating: + parameter: gov.benefit_uprating_cpi + at_defined_interval: + start: "0001-01-01" + end: "0002-01-01" + enactment: "0002-04-01" + rounding: + type: nearest + interval: 0.05 + reference: + - title: Social Security (Scotland) Act 2018, section 86A (relevant prices chosen by Scottish Ministers) + href: https://www.legislation.gov.uk/asp/2018/9/section/86A + - title: Social Security Assistance in Scotland - up-rating for inflation 2026-2027, sections 2 and 3 (September CPI and nearest 5p) + href: https://www.gov.scot/publications/social-security-assistance-scotland-up-rating-inflation-2026-27/pages/3/ diff --git a/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/lower.yaml b/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/lower.yaml index cb4748ec94..fefb64a989 100644 --- a/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/lower.yaml +++ b/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/lower.yaml @@ -20,4 +20,20 @@ values: metadata: unit: currency-GBP label: PAWHP lower (under-80) amount - uprating: gov.economic_assumptions.indices.obr.consumer_price_index + # September CPI and nearest-5p rounding follow Scottish Ministers' current + # policy; s86A leaves the choice of relevant prices to Ministers. + # Apply each year's rise to the previous rounded rate each April. + uprating: + parameter: gov.benefit_uprating_cpi + at_defined_interval: + start: "0001-01-01" + end: "0002-01-01" + enactment: "0002-04-01" + rounding: + type: nearest + interval: 0.05 + reference: + - title: Social Security (Scotland) Act 2018, section 86A (relevant prices chosen by Scottish Ministers) + href: https://www.legislation.gov.uk/asp/2018/9/section/86A + - title: Social Security Assistance in Scotland - up-rating for inflation 2026-2027, sections 2 and 3 (September CPI and nearest 5p) + href: https://www.gov.scot/publications/social-security-assistance-scotland-up-rating-inflation-2026-27/pages/3/ diff --git a/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/lower_shared.yaml b/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/lower_shared.yaml index e75c704469..47abd8993d 100644 --- a/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/lower_shared.yaml +++ b/policyengine_uk/parameters/gov/social_security_scotland/pawhp/amount/lower_shared.yaml @@ -15,4 +15,20 @@ values: metadata: unit: currency-GBP label: PAWHP shared amount under 80 - uprating: gov.economic_assumptions.indices.obr.consumer_price_index + # September CPI and nearest-5p rounding follow Scottish Ministers' current + # policy; s86A leaves the choice of relevant prices to Ministers. + # Apply each year's rise to the previous rounded rate each April. + uprating: + parameter: gov.benefit_uprating_cpi + at_defined_interval: + start: "0001-01-01" + end: "0002-01-01" + enactment: "0002-04-01" + rounding: + type: nearest + interval: 0.05 + reference: + - title: Social Security (Scotland) Act 2018, section 86A (relevant prices chosen by Scottish Ministers) + href: https://www.legislation.gov.uk/asp/2018/9/section/86A + - title: Social Security Assistance in Scotland - up-rating for inflation 2026-2027, sections 2 and 3 (September CPI and nearest 5p) + href: https://www.gov.scot/publications/social-security-assistance-scotland-up-rating-inflation-2026-27/pages/3/ diff --git a/policyengine_uk/parameters/gov/social_security_scotland/scottish_child_payment/amount.yaml b/policyengine_uk/parameters/gov/social_security_scotland/scottish_child_payment/amount.yaml index be0d0e2729..280d46f998 100644 --- a/policyengine_uk/parameters/gov/social_security_scotland/scottish_child_payment/amount.yaml +++ b/policyengine_uk/parameters/gov/social_security_scotland/scottish_child_payment/amount.yaml @@ -7,19 +7,31 @@ values: 2024-04-01: 26.70 2025-04-01: 27.15 2026-04-01: 28.20 - 2027-04-01: 28.85 - 2028-04-01: 29.45 - 2029-04-01: 30.05 - 2030-04-01: 30.65 + # Project from the last published rate. The January 2026 SFC forecasts + # for 2027-30 predate the September 2026 CPI outturn and are superseded + # here by the September-CPI projection, not treated as announced rates. metadata: unit: currency-GBP period: week label: Scottish Child Payment weekly amount - uprating: gov.economic_assumptions.indices.obr.consumer_price_index + # September CPI and nearest-5p rounding follow Scottish Ministers' current + # policy; s86A leaves the choice of relevant prices to Ministers. + # Apply each year's rise to the previous rounded rate each April. + uprating: + parameter: gov.benefit_uprating_cpi + at_defined_interval: + start: "0001-01-01" + end: "0002-01-01" + enactment: "0002-04-01" + rounding: + type: nearest + interval: 0.05 reference: + - title: Social Security (Scotland) Act 2018, section 86A (relevant prices chosen by Scottish Ministers) + href: https://www.legislation.gov.uk/asp/2018/9/section/86A + - title: Social Security Assistance in Scotland - up-rating for inflation 2026-2027, sections 2 and 3 (September CPI and nearest 5p) + href: https://www.gov.scot/publications/social-security-assistance-scotland-up-rating-inflation-2026-27/pages/3/ - title: The Scottish Child Payment Regulations 2020, Regulation 20 href: https://www.legislation.gov.uk/ssi/2020/351/regulation/20 - title: Scottish Government - Scottish Child Payment href: https://www.gov.scot/policies/social-security/scottish-child-payment/ - - title: Scottish Fiscal Commission - Scotland's Economic and Fiscal Forecasts January 2026 - href: https://fiscalcommission.scot/wp-content/uploads/2026/01/Scotlands-Economic-and-Fiscal-Forecasts-January-2026-revised-13-01-2026.pdf diff --git a/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/scottish_child_payment.yaml b/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/scottish_child_payment.yaml index b17b33d543..fe1751b62f 100644 --- a/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/scottish_child_payment.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/scottish_child_payment.yaml @@ -277,7 +277,7 @@ # [parent, child_1] scottish_child_payment: [0, 1466] -- name: SCP with 2027 amount (£28.85/week) +- name: SCP with 2027 September CPI projection (£29.20/week) period: 2027 absolute_error_margin: 10 input: @@ -295,11 +295,11 @@ members: [parent, child_1] region: SCOTLAND output: - # £28.85/week * 52 weeks = £1,500.20 + # £28.20 * 1.035 rounded to 5p = £29.20/week; annualised using 365.25/7 weeks # [parent, child_1] - scottish_child_payment: [0, 1500] + scottish_child_payment: [0, 1523.61] -- name: SCP with 2028 amount (£29.45/week) +- name: SCP with 2028 September CPI projection (£29.80/week) period: 2028 absolute_error_margin: 10 input: @@ -317,9 +317,9 @@ members: [parent, child_1] region: SCOTLAND output: - # £29.45/week * 52 weeks = £1,531.40 + # £29.20 * 1.021 rounded to 5p = £29.80/week; annualised using 365.25/7 weeks # [parent, child_1] - scottish_child_payment: [0, 1531] + scottish_child_payment: [0, 1554.92] - name: CTC no longer qualifies from 2025 (after April 2024) period: 2025 diff --git a/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/september_cpi_uprating.yaml b/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/september_cpi_uprating.yaml new file mode 100644 index 0000000000..0ab829fd5e --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/september_cpi_uprating.yaml @@ -0,0 +1,104 @@ +# April 2027 projections, not enacted 2027 rates. Scottish Ministers' current +# policy is the previous September's CPI, rounded to the nearest 5p: +# https://www.gov.scot/publications/social-security-assistance-scotland-up-rating-inflation-2026-27/pages/3/ +# September 2026 CPI was 3.5%; these expectations are hand-computed from the +# enacted 2026 amounts, independently of the model's generated index. +# Annual model periods use the fiscal-year parameter amounts. + +- name: Scottish Child Payment projects to 29.20 a week in April 2027 + # 28.20 x 1.035 = 29.187 -> 29.20, then 29.20 x 365.25 / 7 = 1523.614286. + period: 2027 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 30 + child: + age: 5 + would_claim_scp: true + benunits: + family: + members: [parent, child] + universal_credit: 5000 + households: + household: + members: [parent, child] + country: SCOTLAND + output: + is_scp_eligible: [false, true] + scottish_child_payment: [0, 1523.61] + +- name: PAWHP higher full amount projects to 327.80 in April 2027 + # 316.70 x 1.035 = 327.7845 -> 327.80. + period: 2027 + absolute_error_margin: 0.01 + input: + age: 82 + country: SCOTLAND + pawhp_eligible: true + is_on_pawhp_relevant_benefit: true + output: + pension_age_winter_heating_payment: 327.80 + +- name: PAWHP lower full amount projects to 218.55 in April 2027 + # 211.15 x 1.035 = 218.54025 -> 218.55. + period: 2027 + absolute_error_margin: 0.01 + input: + age: 70 + country: SCOTLAND + pawhp_eligible: true + is_on_pawhp_relevant_benefit: true + output: + pension_age_winter_heating_payment: 218.55 + +- name: PAWHP shared amounts for mixed ages use September CPI in April 2027 + # Under 80: 105.55 x 1.035 = 109.24425 -> 109.25. + # Over 80 beside under 80: 211.15 x 1.035 = 218.54025 -> 218.55. + period: 2027 + absolute_error_margin: 0.01 + input: + people: + younger: + age: 70 + pawhp_eligible: true + is_on_pawhp_relevant_benefit: false + older: + age: 82 + pawhp_eligible: true + is_on_pawhp_relevant_benefit: false + benunits: + couple: + members: [younger, older] + households: + household: + members: [younger, older] + country: SCOTLAND + output: + pension_age_winter_heating_payment: [109.25, 218.55] + pawhp: 327.80 + +- name: PAWHP shared amount for both over 80 projects to 163.90 in April 2027 + # 158.35 x 1.035 = 163.89225 -> 163.90 for each person. + period: 2027 + absolute_error_margin: 0.01 + input: + people: + first: + age: 82 + pawhp_eligible: true + is_on_pawhp_relevant_benefit: false + second: + age: 85 + pawhp_eligible: true + is_on_pawhp_relevant_benefit: false + benunits: + couple: + members: [first, second] + households: + household: + members: [first, second] + country: SCOTLAND + output: + pension_age_winter_heating_payment: [163.90, 163.90] + pawhp: 327.80 diff --git a/policyengine_uk/tests/test_scottish_benefit_uprating.py b/policyengine_uk/tests/test_scottish_benefit_uprating.py new file mode 100644 index 0000000000..4d65577835 --- /dev/null +++ b/policyengine_uk/tests/test_scottish_benefit_uprating.py @@ -0,0 +1,142 @@ +"""Scottish rates follow September CPI and compound rounded annual awards. + +September CPI and nearest-5p rounding are Scottish Ministers' current policy, +not a statutory choice of index (2018 Act s86A(5)). The 2025-26 and 2026-27 +uprating reports supply the historical anchors below. The April 2027 +projections use September 2026 CPI of 3.5%, published by ONS in October. +""" + +from decimal import Decimal, ROUND_HALF_UP + +import pytest +from policyengine_core.parameters import load_parameter_file + +from policyengine_uk import Simulation +from policyengine_uk.model_api import Scenario +from policyengine_uk.system import system +from policyengine_uk.tax_benefit_system import COUNTRY_DIR + + +SCOTLAND = "gov.social_security_scotland" +SCP = f"{SCOTLAND}.scottish_child_payment.amount" +PAWHP = f"{SCOTLAND}.pawhp.amount" +INDEX = "gov.benefit_uprating_cpi" + +# Published amounts from the Scottish Government's annual uprating reports. +# The last two PAWHP rates coincide but are separate statutory awards. +PUBLISHED = { + SCP: (27.15, 28.20), + f"{PAWHP}.higher": (305.10, 316.70), + f"{PAWHP}.lower": (203.40, 211.15), + f"{PAWHP}.higher_shared_with_80_or_over": (152.55, 158.35), + f"{PAWHP}.lower_shared": (101.70, 105.55), + f"{PAWHP}.higher_shared_with_under_80": (203.40, 211.15), +} + +# Independent hand calculations: 2026 amount x 1.035, nearest 5p. +# 28.20 -> 29.187; 316.70 -> 327.7845; 211.15 -> 218.54025; +# 158.35 -> 163.89225; 105.55 -> 109.24425. +APRIL_2027 = { + SCP: 29.20, + f"{PAWHP}.higher": 327.80, + f"{PAWHP}.lower": 218.55, + f"{PAWHP}.higher_shared_with_80_or_over": 163.90, + f"{PAWHP}.lower_shared": 109.25, + f"{PAWHP}.higher_shared_with_under_80": 218.55, +} + + +@pytest.mark.parametrize("name, amounts", PUBLISHED.items()) +def test_published_2025_and_2026_scottish_rates_are_preserved(name, amounts): + parameter = system.parameters.get_child(name) + for year, expected in zip((2025, 2026), amounts): + assert parameter(f"{year}-04-30") == expected + + +@pytest.mark.parametrize("name, expected", APRIL_2027.items()) +def test_april_2027_scottish_rates_use_published_september_cpi(name, expected): + assert system.parameters.get_child(name)("2027-04-30") == pytest.approx( + expected, abs=1e-9 + ) + + +@pytest.fixture(scope="module") +def raw_parameters(): + return load_parameter_file(str(COUNTRY_DIR / "parameters"), name="") + + +@pytest.mark.parametrize("name", PUBLISHED) +def test_scottish_projection_starts_after_the_last_enacted_rate(raw_parameters, name): + """SFC 2027-30 forecasts must not override the new September projection.""" + parameter = raw_parameters.get_child(name) + assert max(value.instant_str for value in parameter.values_list) == "2026-04-01" + uprating = parameter.metadata["uprating"] + assert uprating["parameter"] == INDEX + assert uprating["rounding"] == {"type": "nearest", "interval": 0.05} + + +@pytest.mark.parametrize("name, amounts", PUBLISHED.items()) +def test_later_scottish_projections_compound_the_previous_rounded_amount(name, amounts): + """Check every projected year, including beyond the former 2030 anchor. + + The independent calculation reads annual CPI growth, rather than the + cumulative index ratio, and rounds after each year's increase. + """ + parameters = system.parameters + parameter = parameters.get_child(name) + previous = Decimal(str(amounts[1])) + increment = Decimal("0.05") + for year in range(2027, 2040): + rate = parameters.gov.economic_assumptions.yoy_growth.september_cpi_uprating( + f"{year}-01-01" + ) + unrounded = previous * (1 + Decimal(str(rate))) + expected = (unrounded / increment).quantize( + Decimal("1"), rounding=ROUND_HALF_UP + ) * increment + assert parameter(f"{year}-04-30") == pytest.approx(float(expected), abs=1e-9), ( + name, + year, + ) + previous = expected + + +@pytest.mark.parametrize("september", [0.035, 0.05]) +def test_april_2027_uses_september_cpi_even_when_calendar_cpi_differs(september): + """Observed September CPI controls 2027; calendar 2027 affects later forecasts.""" + simulation = Simulation( + situation={ + "people": {"person": {"age": {2027: 40}}}, + "benunits": {"benunit": {"members": ["person"]}}, + "households": {"household": {"members": ["person"]}}, + }, + scenario=Scenario( + applied_before_data_load=True, + parameter_changes={ + "gov.economic_assumptions.statutory_uprating_inputs.cpi_september": { + "year:2026-09-01:1": september + }, + "gov.economic_assumptions.yoy_growth.obr.consumer_price_index": { + "year:2027-01-01:1": 0.11 + }, + }, + ), + ) + expected = ( + APRIL_2027 + if september == 0.035 + else { + SCP: 29.60, + f"{PAWHP}.higher": 332.55, + f"{PAWHP}.lower": 221.70, + f"{PAWHP}.higher_shared_with_80_or_over": 166.25, + f"{PAWHP}.lower_shared": 110.85, + f"{PAWHP}.higher_shared_with_under_80": 221.70, + } + ) + parameters = simulation.tax_benefit_system.parameters + for name, amount in expected.items(): + assert parameters.get_child(name)("2027-04-30") == pytest.approx( + amount, abs=1e-9 + ), name + assert parameters.get_child(name)("2026-04-30") == PUBLISHED[name][1] diff --git a/policyengine_uk/tests/test_scottish_child_payment_baby_bonus.py b/policyengine_uk/tests/test_scottish_child_payment_baby_bonus.py index e3e6843036..a66519aed4 100644 --- a/policyengine_uk/tests/test_scottish_child_payment_baby_bonus.py +++ b/policyengine_uk/tests/test_scottish_child_payment_baby_bonus.py @@ -67,6 +67,6 @@ def test_baby_bonus_applies_when_switched_on(): ) _, baby, child = scp(2027, scenario) - # £40.00/week * 52 weeks vs the £28.85/week ordinary rate * 52 weeks. + # £40.00/week vs the September-CPI projected ordinary rate £29.20/week. assert baby == pytest.approx(2_080, abs=10) - assert child == pytest.approx(1_500, abs=10) + assert child == pytest.approx(1_523.61, abs=0.01) diff --git a/policyengine_uk/tests/test_september_cpi_uprating.py b/policyengine_uk/tests/test_september_cpi_uprating.py index f6d4413164..f86fe6228b 100644 --- a/policyengine_uk/tests/test_september_cpi_uprating.py +++ b/policyengine_uk/tests/test_september_cpi_uprating.py @@ -170,8 +170,8 @@ def test_uc_childcare_caps_rise_by_september_cpi_in_april_2027(children, expecte def test_every_parameter_on_the_index_rises_by_september_cpi_after_its_last_value(): """After its last published value, each parameter uprated by the index - rises by exactly the April rise: 0% in April 2016, 3.5% in April 2027, - and 2.1% in April 2028.""" + rises by the April rise: 0% in April 2016, 3.5% in April 2027, + and 2.1% in April 2028, with any configured benefit-specific rounding.""" parameters = system.parameters # The processed tree, including its baseline copy, is uprated, so read # the YAML files for the last published dates. @@ -185,9 +185,24 @@ def test_every_parameter_on_the_index_rises_by_september_cpi_after_its_last_valu after = parameter(f"{year}-04-30") if not before: continue - assert after / before - 1 == pytest.approx( - rise(parameters, year), abs=2e-5 - ), (parameter.name, year) + uprating = parameter.metadata.get("uprating") + rounding = uprating.get("rounding") if isinstance(uprating, dict) else None + if rounding: + # Scottish benefits round each annual increase to the nearest + # 5p. Check the rounded amount rather than an exact growth rate. + assert rounding["type"] == "nearest" + interval = rounding["interval"] + index = parameters.gov.benefit_uprating_cpi + factor = index(f"{year}-04-30") / index(f"{year - 1}-04-30") + expected = round(before * factor / interval) * interval + assert after == pytest.approx(expected, abs=PENNY_TENTH), ( + parameter.name, + year, + ) + else: + assert after / before - 1 == pytest.approx( + rise(parameters, year), abs=2e-5 + ), (parameter.name, year) checked += len(years) > 0 assert checked >= 50 From 80e21df7b42d9e0c3b4051077471c54a207cc7c3 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 22 Oct 2026 15:22:50 -0400 Subject: [PATCH 2/2] Annualise Scottish Child Payment over 52 weeks in the new expectations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The award formulas annualise with policyengine-core's 52-week constant, not 365.25/7, so April 2027's £29.20/week is £1,518.40 a year and 2028's £29.80/week is £1,549.60. The baby-bonus reform cases' ordinary-rate children now read the September-CPI projection (£29.80 in 2028) rather than the SFC forecast's £29.45. Independent review P1 findings 1 and 2. Co-Authored-By: Claude Opus 5.5 --- .../social_security_scotland/scottish_child_payment.yaml | 8 ++++---- .../social_security_scotland/september_cpi_uprating.yaml | 2 +- .../scp_baby_bonus/scottish_child_payment_baby_bonus.yaml | 8 ++++---- .../tests/test_scottish_child_payment_baby_bonus.py | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/scottish_child_payment.yaml b/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/scottish_child_payment.yaml index fe1751b62f..1e605c1822 100644 --- a/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/scottish_child_payment.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/scottish_child_payment.yaml @@ -295,9 +295,9 @@ members: [parent, child_1] region: SCOTLAND output: - # £28.20 * 1.035 rounded to 5p = £29.20/week; annualised using 365.25/7 weeks + # £28.20 * 1.035 rounded to 5p = £29.20/week; annualised over 52 weeks = £1,518.40 # [parent, child_1] - scottish_child_payment: [0, 1523.61] + scottish_child_payment: [0, 1518.40] - name: SCP with 2028 September CPI projection (£29.80/week) period: 2028 @@ -317,9 +317,9 @@ members: [parent, child_1] region: SCOTLAND output: - # £29.20 * 1.021 rounded to 5p = £29.80/week; annualised using 365.25/7 weeks + # £29.20 * 1.021 rounded to 5p = £29.80/week; annualised over 52 weeks = £1,549.60 # [parent, child_1] - scottish_child_payment: [0, 1554.92] + scottish_child_payment: [0, 1549.60] - name: CTC no longer qualifies from 2025 (after April 2024) period: 2025 diff --git a/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/september_cpi_uprating.yaml b/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/september_cpi_uprating.yaml index 0ab829fd5e..a2f8077707 100644 --- a/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/september_cpi_uprating.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/september_cpi_uprating.yaml @@ -26,7 +26,7 @@ country: SCOTLAND output: is_scp_eligible: [false, true] - scottish_child_payment: [0, 1523.61] + scottish_child_payment: [0, 1518.40] - name: PAWHP higher full amount projects to 327.80 in April 2027 # 316.70 x 1.035 = 327.7845 -> 327.80. diff --git a/policyengine_uk/tests/policy/reforms/scp_baby_bonus/scottish_child_payment_baby_bonus.yaml b/policyengine_uk/tests/policy/reforms/scp_baby_bonus/scottish_child_payment_baby_bonus.yaml index 995d2ccdb1..48dcbca7be 100644 --- a/policyengine_uk/tests/policy/reforms/scp_baby_bonus/scottish_child_payment_baby_bonus.yaml +++ b/policyengine_uk/tests/policy/reforms/scp_baby_bonus/scottish_child_payment_baby_bonus.yaml @@ -51,9 +51,9 @@ members: [parent, toddler] region: SCOTLAND output: - # Standard rate only: £29.45/week * 52 = £1,531.40 + # Standard rate only: £29.80/week * 52 = £1,549.60 # [parent, toddler] - scottish_child_payment: [0, 1531] + scottish_child_payment: [0, 1549.60] - name: SCP baby bonus - mixed ages (2028) period: 2028 @@ -79,9 +79,9 @@ region: SCOTLAND output: # Baby (age 0): £40.80/week total * 52 = £2,121.60 - # Older child (age 5): £29.45/week * 52 = £1,531.40 + # Older child (age 5): £29.80/week * 52 = £1,549.60 # [parent, baby, older_child] - scottish_child_payment: [0, 2122, 1531] + scottish_child_payment: [0, 2122, 1549.60] - name: SCP baby bonus - not in Scotland (2028) period: 2028 diff --git a/policyengine_uk/tests/test_scottish_child_payment_baby_bonus.py b/policyengine_uk/tests/test_scottish_child_payment_baby_bonus.py index a66519aed4..bd9f7d0457 100644 --- a/policyengine_uk/tests/test_scottish_child_payment_baby_bonus.py +++ b/policyengine_uk/tests/test_scottish_child_payment_baby_bonus.py @@ -69,4 +69,4 @@ def test_baby_bonus_applies_when_switched_on(): # £40.00/week vs the September-CPI projected ordinary rate £29.20/week. assert baby == pytest.approx(2_080, abs=10) - assert child == pytest.approx(1_523.61, abs=0.01) + assert child == pytest.approx(1_518.40, abs=0.01)