Skip to content

Undated State Pension rate reforms move State Pension the wrong way #2122

Description

@MaxGhenis

An undated State Pension rate reform moves State Pension the wrong way. Raising the new State Pension to £300 a week without a date lowers a new State Pension pensioner's modelled State Pension. The same reform with a date works.

Reproduction (origin/main 325d585)

Data: a 2-person UKSingleYearDataset for 2024-25, with these person columns:

  • men aged 70 and 90
  • months_since_last_birthday 3
  • state_pension_reported £240 and £250 a week × 52

Household columns: region LONDON, tenure_type OWNED_OUTRIGHT, rent 0, council_tax 0.

Run with Microsimulation(dataset=..., reform=...) and read state_pension for 2027:

reform NEW type (aged 70) BASIC type (aged 90)
baseline 14,145.0 14,734.2
{"gov.dwp.state_pension.new_state_pension.amount": 300} 12,480.0 14,734.2
{"...new_state_pension.amount": {"2026-01-01.2100-12-31": 300}} 16,925.9 14,734.2
{"gov.dwp.state_pension.basic_state_pension.amount": 250} 14,145.0 13,000.0

Mechanism

  • Simulation.apply_parameter_changes (policyengine_uk/simulation.py) applies a non-dict value with p.update(period="year:2000:100", ...), so it also rewrites the data year's rate.
  • basic_state_pension, new_state_pension and additional_state_pension split the data year's state_pension_reported at the data year's rate taken from the reformed parameters. They then uprate by the period's rate over that data-year rate.
  • Under an undated reform both rates are the reformed value, so the uprating ratio is 1, and the record gets its un-uprated reported amount whatever the new rate.
  • A rate of 0 moves the whole reported amount into additional_state_pension (uprating falls back to 1), so a reform setting the flat rate to 0 leaves State Pension unchanged.

Found in review of #1922.

Fix

Split the reported amount at the baseline (legislated) data-year rate, e.g. from the simulation's baseline parameters, and scale by the reformed period rate over that baseline rate. Do this in all three components, and keep #1922's partition invariant. Add tests for undated and dated reforms (rate up, rate down, rate 0) that check state_pension, not just the components. Check how policyengine.py and the API send reforms, to judge exposure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions