Claude Code Plugins

Community-maintained marketplace

Feedback

PolicyEngine-UK tax and benefit microsimulation patterns, situation creation, and common workflows

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name policyengine-uk
description PolicyEngine-UK tax and benefit microsimulation patterns, situation creation, and common workflows

PolicyEngine-UK

PolicyEngine-UK models the UK tax and benefit system, including devolved variations for Scotland and Wales.

For Users 👥

What is PolicyEngine-UK?

PolicyEngine-UK is the "calculator" for UK taxes and benefits. When you use policyengine.org/uk, PolicyEngine-UK runs behind the scenes.

What it models:

Direct taxes:

  • Income tax (UK-wide, Scottish, and Welsh variations)
  • National Insurance (Classes 1, 2, 4)
  • Capital gains tax
  • Dividend tax

Property and transaction taxes:

  • Council Tax
  • Stamp Duty Land Tax (England/NI)
  • Land and Buildings Transaction Tax (Scotland)
  • Land Transaction Tax (Wales)

Universal Credit:

  • Standard allowance
  • Child elements
  • Housing cost element
  • Childcare costs element
  • Carer element
  • Work capability elements

Legacy benefits (being phased out):

  • Working Tax Credit
  • Child Tax Credit
  • Income Support
  • Income-based JSA/ESA
  • Housing Benefit

Other benefits:

  • Child Benefit
  • Pension Credit
  • Personal Independence Payment (PIP)
  • Disability Living Allowance (DLA)
  • Attendance Allowance
  • State Pension

See full list: https://policyengine.org/uk/parameters

Understanding Variables

When you see results in PolicyEngine, these are variables:

Income variables:

  • employment_income - Gross employment earnings/salary
  • self_employment_income - Self-employment profits
  • pension_income - Private pension income
  • property_income - Rental income
  • savings_interest_income - Interest from savings
  • dividend_income - Dividend income

Tax variables:

  • income_tax - Total income tax liability
  • national_insurance - Total NI contributions
  • council_tax - Council tax liability

Benefit variables:

  • universal_credit - Universal Credit amount
  • child_benefit - Child Benefit amount
  • pension_credit - Pension Credit amount
  • working_tax_credit - Working Tax Credit (legacy)
  • child_tax_credit - Child Tax Credit (legacy)

Summary variables:

  • household_net_income - Income after taxes and benefits
  • disposable_income - Income after taxes
  • equivalised_household_net_income - Adjusted for household size

For Analysts 📊

Installation and Setup

# Install PolicyEngine-UK
pip install policyengine-uk

# Or with uv (recommended)
uv pip install policyengine-uk

Quick Start

from policyengine_uk import Simulation

# Create a household
situation = {
    "people": {
        "person": {
            "age": {2025: 30},
            "employment_income": {2025: 30000}
        }
    },
    "benunits": {
        "benunit": {
            "members": ["person"]
        }
    },
    "households": {
        "household": {
            "members": ["person"],
            "region": {2025: "LONDON"}
        }
    }
}

# Calculate taxes and benefits
sim = Simulation(situation=situation)
income_tax = sim.calculate("income_tax", 2025)[0]
universal_credit = sim.calculate("universal_credit", 2025)[0]

print(f"Income tax: £{income_tax:,.0f}")
print(f"Universal Credit: £{universal_credit:,.0f}")

Web App to Python

Web app URL:

policyengine.org/uk/household?household=12345

Equivalent Python (conceptually): The household ID represents a situation dictionary. To replicate in Python, you'd create a similar situation.

When to Use This Skill

  • Creating household situations for tax/benefit calculations
  • Running microsimulations with PolicyEngine-UK
  • Analyzing policy reforms and their impacts
  • Building tools that use PolicyEngine-UK (calculators, analysis notebooks)
  • Debugging PolicyEngine-UK calculations

For Contributors 💻

Repository

Location: PolicyEngine/policyengine-uk

To see current implementation:

git clone https://github.com/PolicyEngine/policyengine-uk
cd policyengine-uk

# Explore structure
tree policyengine_uk/

Key directories:

ls policyengine_uk/
# - variables/   - Tax and benefit calculations
# - parameters/  - Policy rules (YAML)
# - reforms/     - Pre-defined reforms
# - tests/       - Test cases

Core Concepts

1. Situation Dictionary Structure

PolicyEngine UK requires a nested dictionary defining household composition:

situation = {
    "people": {
        "person_id": {
            "age": {2025: 35},
            "employment_income": {2025: 30000},
            # ... other person attributes
        }
    },
    "benunits": {
        "benunit_id": {
            "members": ["person_id", ...]
        }
    },
    "households": {
        "household_id": {
            "members": ["person_id", ...],
            "region": {2025: "SOUTH_EAST"}
        }
    }
}

Key Rules:

  • All entities must have consistent member lists
  • Use year keys for all values: {2025: value}
  • Region must be one of the ITL 1 regions (see below)
  • All monetary values in pounds (not pence)
  • UK tax year runs April 6 to April 5 (but use calendar year in code)

Important Entity Difference:

  • UK uses benunits (benefit units): a single adult OR couple + dependent children
  • This is the assessment unit for most means-tested benefits
  • Unlike US which uses families/marital_units/tax_units/spm_units

2. Creating Simulations

from policyengine_uk import Simulation

# Create simulation from situation
simulation = Simulation(situation=situation)

# Calculate variables
income_tax = simulation.calculate("income_tax", 2025)
universal_credit = simulation.calculate("universal_credit", 2025)
household_net_income = simulation.calculate("household_net_income", 2025)

Common Variables:

Income:

  • employment_income - Gross employment earnings
  • self_employment_income - Self-employment profits
  • pension_income - Private pension income
  • property_income - Rental income
  • savings_interest_income - Interest income
  • dividend_income - Dividend income
  • miscellaneous_income - Other income sources

Tax Outputs:

  • income_tax - Total income tax liability
  • national_insurance - Total NI contributions
  • council_tax - Council tax liability
  • VAT - Value Added Tax paid

Benefits:

  • universal_credit - Universal Credit
  • child_benefit - Child Benefit
  • pension_credit - Pension Credit
  • working_tax_credit - Working Tax Credit (legacy)
  • child_tax_credit - Child Tax Credit (legacy)
  • personal_independence_payment - PIP
  • attendance_allowance - Attendance Allowance
  • state_pension - State Pension

Summary:

  • household_net_income - Income after taxes and benefits
  • disposable_income - Income after taxes
  • equivalised_household_net_income - Adjusted for household size

3. Using Axes for Parameter Sweeps

To vary a parameter across multiple values:

situation = {
    # ... normal situation setup ...
    "axes": [[{
        "name": "employment_income",
        "count": 1001,
        "min": 0,
        "max": 100000,
        "period": 2025
    }]]
}

simulation = Simulation(situation=situation)
# Now calculate() returns arrays of 1001 values
incomes = simulation.calculate("employment_income", 2025)  # Array of 1001 values
taxes = simulation.calculate("income_tax", 2025)  # Array of 1001 values

Important: Remove axes before creating single-point simulations:

situation_single = situation.copy()
situation_single.pop("axes", None)
simulation = Simulation(situation=situation_single)

4. Policy Reforms

from policyengine_uk import Simulation

# Define a reform (modifies parameters)
reform = {
    "gov.hmrc.income_tax.rates.uk.brackets[0].rate": {
        "2025-01-01.2100-12-31": 0.25  # Increase basic rate to 25%
    }
}

# Create simulation with reform
simulation = Simulation(situation=situation, reform=reform)

Common Patterns

Pattern 1: Single Person Household Calculation

from policyengine_uk import Simulation

situation = {
    "people": {
        "person": {
            "age": {2025: 30},
            "employment_income": {2025: 30000}
        }
    },
    "benunits": {
        "benunit": {
            "members": ["person"]
        }
    },
    "households": {
        "household": {
            "members": ["person"],
            "region": {2025: "LONDON"}
        }
    }
}

sim = Simulation(situation=situation)
income_tax = sim.calculate("income_tax", 2025)[0]
national_insurance = sim.calculate("national_insurance", 2025)[0]
universal_credit = sim.calculate("universal_credit", 2025)[0]

Pattern 2: Couple with Children

situation = {
    "people": {
        "parent_1": {
            "age": {2025: 35},
            "employment_income": {2025: 35000}
        },
        "parent_2": {
            "age": {2025: 33},
            "employment_income": {2025: 25000}
        },
        "child_1": {
            "age": {2025: 8}
        },
        "child_2": {
            "age": {2025: 5}
        }
    },
    "benunits": {
        "benunit": {
            "members": ["parent_1", "parent_2", "child_1", "child_2"]
        }
    },
    "households": {
        "household": {
            "members": ["parent_1", "parent_2", "child_1", "child_2"],
            "region": {2025: "NORTH_WEST"}
        }
    }
}

sim = Simulation(situation=situation)
child_benefit = sim.calculate("child_benefit", 2025)[0]
universal_credit = sim.calculate("universal_credit", 2025)[0]

Pattern 3: Marginal Tax Rate Analysis

# Create baseline with axes varying income
situation_with_axes = {
    "people": {
        "person": {
            "age": {2025: 30}
        }
    },
    "benunits": {"benunit": {"members": ["person"]}},
    "households": {
        "household": {
            "members": ["person"],
            "region": {2025: "LONDON"}
        }
    },
    "axes": [[{
        "name": "employment_income",
        "count": 1001,
        "min": 0,
        "max": 100000,
        "period": 2025
    }]]
}

sim = Simulation(situation=situation_with_axes)
incomes = sim.calculate("employment_income", 2025)
net_incomes = sim.calculate("household_net_income", 2025)

# Calculate marginal tax rate
import numpy as np
mtr = 1 - (np.gradient(net_incomes) / np.gradient(incomes))

Pattern 4: Regional Comparison

regions = ["LONDON", "SCOTLAND", "WALES", "NORTH_EAST"]
results = {}

for region in regions:
    situation = create_situation(region=region, income=30000)
    sim = Simulation(situation=situation)
    results[region] = {
        "income_tax": sim.calculate("income_tax", 2025)[0],
        "national_insurance": sim.calculate("national_insurance", 2025)[0],
        "total_tax": sim.calculate("income_tax", 2025)[0] +
                     sim.calculate("national_insurance", 2025)[0]
    }

Pattern 5: Policy Reform Impact

from policyengine_uk import Microsimulation, Reform

# Define reform: Increase basic rate to 25%
class IncreaseBasicRate(Reform):
    def apply(self):
        def modify_parameters(parameters):
            parameters.gov.hmrc.income_tax.rates.uk.brackets[0].rate.update(
                period="year:2025:10", value=0.25
            )
            return parameters
        self.modify_parameters(modify_parameters)

# Run microsimulation
baseline = Microsimulation()
reformed = Microsimulation(reform=IncreaseBasicRate)

# Calculate revenue impact
baseline_revenue = baseline.calc("income_tax", 2025).sum()
reformed_revenue = reformed.calc("income_tax", 2025).sum()
revenue_change = (reformed_revenue - baseline_revenue) / 1e9  # in billions

# Calculate household impact
baseline_net_income = baseline.calc("household_net_income", 2025)
reformed_net_income = reformed.calc("household_net_income", 2025)

Helper Scripts

This skill includes helper scripts in the scripts/ directory:

from policyengine_uk_skills.situation_helpers import (
    create_single_person,
    create_couple,
    create_family_with_children,
    add_region
)

# Quick situation creation
situation = create_single_person(
    income=30000,
    region="LONDON",
    age=30
)

# Create couple
situation = create_couple(
    income_1=35000,
    income_2=25000,
    region="SCOTLAND"
)

Common Pitfalls and Solutions

Pitfall 1: Member Lists Out of Sync

Problem: Different entities have different members

# WRONG
"benunits": {"benunit": {"members": ["parent"]}},
"households": {"household": {"members": ["parent", "child"]}}

Solution: Keep all entity member lists consistent:

# CORRECT
all_members = ["parent", "child"]
"benunits": {"benunit": {"members": all_members}},
"households": {"household": {"members": all_members}}

Pitfall 2: Forgetting Year Keys

Problem: "age": 35 instead of "age": {2025: 35}

Solution: Always use year dictionary:

"age": {2025: 35},
"employment_income": {2025: 30000}

Pitfall 3: Wrong Region Format

Problem: Using lowercase or incorrect region names

Solution: Use uppercase ITL 1 region codes:

# CORRECT regions:
"region": {2025: "LONDON"}
"region": {2025: "SCOTLAND"}
"region": {2025: "WALES"}
"region": {2025: "NORTH_EAST"}
"region": {2025: "SOUTH_EAST"}

Pitfall 4: Axes Persistence

Problem: Axes remain in situation when creating single-point simulation

Solution: Remove axes before single-point simulation:

situation_single = situation.copy()
situation_single.pop("axes", None)

Pitfall 5: Missing Benunits

Problem: Forgetting to include benunits (benefit units)

Solution: Always include benunits in UK simulations:

# UK requires benunits
situation = {
    "people": {...},
    "benunits": {"benunit": {"members": [...]}},  # Required!
    "households": {...}
}

Regions in PolicyEngine UK

UK uses ITL 1 (International Territorial Level 1, formerly NUTS 1) regions:

Regions:

  • NORTH_EAST - North East England
  • NORTH_WEST - North West England
  • YORKSHIRE - Yorkshire and the Humber
  • EAST_MIDLANDS - East Midlands
  • WEST_MIDLANDS - West Midlands
  • EAST_OF_ENGLAND - East of England
  • LONDON - London
  • SOUTH_EAST - South East England
  • SOUTH_WEST - South West England
  • WALES - Wales
  • SCOTLAND - Scotland
  • NORTHERN_IRELAND - Northern Ireland

Regional Tax Variations:

Scotland:

  • Has devolved income tax with 6 bands (starter 19%, basic 20%, intermediate 21%, higher 42%, advanced 45%, top 47%)
  • Scottish residents automatically calculated with Scottish rates

Wales:

  • Has Welsh Rate of Income Tax (WRIT)
  • Currently maintains parity with England/NI rates

England/Northern Ireland:

  • Standard UK rates: basic 20%, higher 40%, additional 45%

Key Parameters and Values (2025/26)

Income Tax

  • Personal Allowance: £12,570
  • Basic rate threshold: £50,270
  • Higher rate threshold: £125,140
  • Rates: 20% (basic), 40% (higher), 45% (additional)
  • Personal allowance tapering: £1 reduction for every £2 over £100,000

National Insurance (Class 1)

  • Lower Earnings Limit: £6,396/year
  • Primary Threshold: £12,570/year
  • Upper Earnings Limit: £50,270/year
  • Rates: 12% (between primary and upper), 2% (above upper)

Universal Credit

  • Standard allowance: Varies by single/couple and age
  • Taper rate: 55% (rate at which UC reduced as income increases)
  • Work allowance: Amount you can earn before UC reduced

Child Benefit

  • First child: Higher rate
  • Subsequent children: Lower rate
  • High Income Charge: Tapered withdrawal starting at £60,000

Version Compatibility

  • Use policyengine-uk>=1.0.0 for 2025 calculations
  • Check version: import policyengine_uk; print(policyengine_uk.__version__)
  • Different years may require different package versions

Debugging Tips

  1. Enable tracing:

    simulation.trace = True
    result = simulation.calculate("variable_name", 2025)
    
  2. Check intermediate calculations:

    gross_income = simulation.calculate("gross_income", 2025)
    disposable_income = simulation.calculate("disposable_income", 2025)
    
  3. Verify situation structure:

    import json
    print(json.dumps(situation, indent=2))
    
  4. Test with PolicyEngine web app:

    • Go to policyengine.org/uk/household
    • Enter same inputs
    • Compare results

Additional Resources

Examples Directory

See examples/ for complete working examples:

  • single_person.yaml - Single person household
  • couple.yaml - Couple without children
  • family_with_children.yaml - Family with dependents
  • universal_credit_sweep.yaml - Analyzing UC with axes

Key Differences from US System

  1. Benefit Units: UK uses benunits (single/couple + children) instead of US multiple entity types
  2. Universal Credit: Consolidated means-tested benefit (vs separate SNAP, TANF, etc. in US)
  3. National Insurance: Separate from income tax with own thresholds (vs US Social Security tax)
  4. Devolved Taxes: Scotland and Wales have different income tax rates
  5. Tax Year: April 6 to April 5 (vs calendar year in US)
  6. No State Variation: Council Tax is local, but most taxes/benefits are national (vs 50 US states)