Skip to content

Conversation

@vahid-ahmadi
Copy link
Collaborator

@vahid-ahmadi vahid-ahmadi commented Dec 4, 2025

Description

This PR implements the £2,000 salary sacrifice pension cap from the Autumn Budget 2025, which takes effect from April 2029.

Summary

  • Adds parameter salary_sacrifice_pension_cap (£2,000 from 2029-04-06, infinity before)
  • Excess above cap is redirected to regular employee pension contributions
  • Applies 13% employer haircut on excess (employers spread NI costs across workers)
  • Revenue impact: ~£3.6 billion (NI: £2.0bn, Income Tax: £1.1bn due to pension relief caps)

Key changes

  • New parameter: gov.hmrc.national_insurance.salary_sacrifice_pension_cap
  • New variables: salary_sacrifice_returned_to_income, employee_pension_contributions_reported
  • Modified variables: employment_income (adds excess for NI), employee_pension_contributions (adds excess for
    income tax relief)
  • Simulation: Added input aliasing for employee_pension_contributions →
    employee_pension_contributions_reported

Usage

  # Reform (with cap - current law)
  reform = Microsimulation()
  # Baseline (no cap)
  baseline = Microsimulation(reform={
      "gov.hmrc.national_insurance.salary_sacrifice_pension_cap": {
          "2029": float("inf"),
          "2030": float("inf"),
      }
  })


  revenue = reform.calculate("gov_balance", 2030).sum() - baseline.calculate("gov_balance", 2030).sum()

Test plan

  • Revenue impact test (~£3.3bn ± £1.5bn)
  • NI increases with reform (>£1bn)
  • Income tax impact test
  • Excess redirected to pension contributions
  • Employer haircut (13%) applied correctly
  • Affected population (~3.3 million workers)

@vahid-ahmadi vahid-ahmadi self-assigned this Dec 4, 2025
@vahid-ahmadi vahid-ahmadi changed the title Edit salary sacrifice3 Edit salary sacrifice implementation Dec 4, 2025
The previous implementation incorrectly applied a 13% targeted haircut
to only affected workers' excess contributions. This is economically
incorrect because employers cannot target only affected workers without
those workers negotiating to recoup the loss.

The correct approach (from the blog at policyengine.org/uk/research/uk-salary-sacrifice-cap):
- Full excess above £2,000 cap is redirected to regular pension contributions
- Employers spread their increased NI costs across ALL workers
- This results in a ~0.16% broad-base haircut on everyone's employment income

Changes:
- New parameter: salary_sacrifice_broad_base_haircut_rate (default 0.0016)
- New variable: salary_sacrifice_broad_base_haircut (reduces all workers' income)
- Remove old employer_salary_sacrifice_haircut parameter (targeted 13%)
- Update salary_sacrifice_returned_to_income to redirect full excess
- Update NI variables to use full excess (no targeted haircut)
- Add employment_income to include broad_base_haircut in adds
- Update tests to reflect new methodology

This allows parametric simulation of the salary sacrifice cap reform:
- Adjust the cap amount via salary_sacrifice_pension_cap
- Adjust the broad-base haircut rate via salary_sacrifice_broad_base_haircut_rate

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@MaxGhenis
Copy link
Collaborator

Fixed: Broad-base haircut implementation

The previous implementation had a fundamental error in how the employer haircut was modeled.

The Problem

The old code applied a 13% targeted haircut to only affected workers' excess:

# OLD (wrong)
excess = max_(intended_ss - cap, 0)
haircut = 0.13  # 13% of excess
return excess * (1 - haircut)  # Only 87% redirected

This is economically incorrect because employers cannot target only salary sacrificers without those workers negotiating to recoup the loss.

The Fix

Per the blog methodology:

Broad-based employer response: Employers spread the increased National Insurance costs across all employees... With £13.8 billion in excess contributions generating £2.1 billion in additional employer NI (at 15%), this translates to a 0.16% reduction in employment income across all workers.

The new implementation:

  1. Full excess is redirected - the £13.8bn excess goes entirely to employee pension contributions
  2. Broad-base haircut - ALL workers get a 0.16% reduction in employment income (new variable salary_sacrifice_broad_base_haircut)
  3. Parametric - users can adjust both the cap and the haircut rate

New Parameters

  • gov.hmrc.national_insurance.salary_sacrifice_pension_cap - the £2,000 cap (unchanged)
  • gov.contrib.behavioral_responses.salary_sacrifice_broad_base_haircut_rate - 0.0016 (0.16%)

Verification

from policyengine_uk import Microsimulation
sim = Microsimulation()
print(sim.calculate('salary_sacrifice_broad_base_haircut', 2030).sum() / 1e9)  # -£2.3bn
print(sim.calculate('salary_sacrifice_returned_to_income', 2030).sum() / 1e9)  # £13.6bn

The broad-base haircut is -£2.3bn (total employment income ~£1.4T × 0.16%), and full excess of £13.6bn is redirected.

@vahid-ahmadi
Copy link
Collaborator Author

vahid-ahmadi commented Dec 4, 2025

Fixed: Broad-base haircut implementation

Is there any reference for the 0.16% rate?

@vahid-ahmadi
Copy link
Collaborator Author

@PolicyEngine please review this pr.

@policyengine
Copy link
Contributor

policyengine bot commented Dec 8, 2025

⚙️ Running tests to verify the implementation...

@vahid-ahmadi vahid-ahmadi merged commit 6ca74dc into master Dec 8, 2025
2 checks passed
@vahid-ahmadi vahid-ahmadi deleted the edit-salary-sacrifice3 branch December 8, 2025 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants