Provides API patterns for PolicyEngine-UK household calculations, UK taxes/benefits modeling, and population microsimulations using policyengine package.
From essentialnpx claudepluginhub policyengine/policyengine-claude --plugin data-scienceThis skill uses the workspace's default tool permissions.
examples/couple.yamlexamples/family_with_children.yamlexamples/household_calculation.pyexamples/policy_reform.pyexamples/population_simulation.pyexamples/single_person.yamlexamples/universal_credit_sweep.yamlscripts/situation_helpers.pyGuides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Analyzes BMad project state from catalog CSV, configs, artifacts, and query to recommend next skills or answer questions. Useful for help requests, 'what next', or starting BMad.
IMPORTANT: Always use the current year (2026) in calculations, not 2024 or 2025.
PolicyEngine-UK models the UK tax and benefit system, including devolved variations for Scotland and Wales.
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:
Property and transaction taxes:
Universal Credit:
Legacy benefits (being phased out):
Other benefits:
See full list: https://policyengine.org/uk/parameters
Income variables:
employment_income - Gross employment earnings/salaryself_employment_income - Self-employment profitspension_income - Private pension incomeproperty_income - Rental incomesavings_interest_income - Interest from savingsdividend_income - Dividend incomeTax variables:
income_tax - Total income tax liabilitynational_insurance - Total NI contributionscouncil_tax - Council tax liabilityBenefit variables:
universal_credit - Universal Credit amountchild_benefit - Child Benefit amountpension_credit - Pension Credit amountSummary variables:
household_net_income - Income after taxes and benefitshbai_household_net_income - HBAI-definition net incomeuv pip install policyengine
PolicyEngine provides two main ways to analyze the UK tax-benefit system:
Use calculate_household_impact() with UKHouseholdInput for quick single-household calculations.
from policyengine.tax_benefit_models.uk import (
UKHouseholdInput,
calculate_household_impact,
)
household = UKHouseholdInput(
people=[
{"age": 35, "employment_income": 50_000},
],
year=2026,
)
result = calculate_household_impact(household)
# Access results
print(f"Income tax: £{result.person[0]['income_tax']:,.0f}")
print(f"Net income: £{result.household['hbai_household_net_income']:,.0f}")
household = UKHouseholdInput(
people=[{"age": 30, "employment_income": 30_000}],
household={"region": "LONDON"},
year=2026,
)
result = calculate_household_impact(household)
household = UKHouseholdInput(
people=[
{"age": 35, "employment_income": 50_000},
{"age": 33, "employment_income": 25_000},
{"age": 8},
{"age": 5},
],
benunit={"would_claim_uc": True},
household={"region": "NORTH_WEST"},
year=2026,
)
result = calculate_household_impact(household)
print(f"Child Benefit: £{result.benunit[0]['child_benefit']:,.0f}")
print(f"Universal Credit: £{result.benunit[0]['universal_credit']:,.0f}")
household = UKHouseholdInput(
people=[{"age": 28, "employment_income": 25_000}],
benunit={"would_claim_uc": True},
household={"region": "LONDON", "rent": 15_000},
year=2026,
)
result = calculate_household_impact(household)
Results are organized by entity level:
result.person[i] - Person-level variables (indexed by person order)result.benunit[i] - Benefit unit variablesresult.household - Household-level variables# Person-level (returns dict for each person)
income_tax = result.person[0]['income_tax']
ni = result.person[0]['national_insurance']
# Benefit unit level
uc = result.benunit[0]['universal_credit']
child_benefit = result.benunit[0]['child_benefit']
# Household level
net_income = result.household['hbai_household_net_income']
Use Simulation with datasets for population-level microsimulation analysis.
from policyengine.tax_benefit_models.uk import (
uk_latest,
ensure_datasets,
PolicyEngineUKDataset,
)
# Load pre-prepared datasets
datasets = ensure_datasets(
data_folder="./data",
years=[2026, 2027, 2028, 2029, 2030],
)
dataset = datasets["enhanced_frs_2023_24_2026"]
from policyengine.core import Simulation
simulation = Simulation(
dataset=dataset,
tax_benefit_model_version=uk_latest,
)
simulation.ensure() # Runs if not cached, loads if cached
# Access output data (weighted MicroDataFrames)
output = simulation.output_dataset.data
income_tax_total = output.household['household_tax'].sum()
mean_net_income = output.household['household_net_income'].mean()
simulation.ensure() runs the simulation if not cached, or loads from cachesimulation.output_dataset.data contains weighted MicroDataFrames.sum(), .mean() directlyoutput.person, output.benunit, output.householdFor simple parameter changes:
from policyengine.core import Policy, ParameterValue
from datetime import datetime
# Get parameter from model
param = uk_latest.get_parameter("gov.hmrc.income_tax.rates.uk[0].rate")
policy = Policy(
name="Basic rate 25%",
parameter_values=[
ParameterValue(
parameter=param,
value=0.25,
start_date=datetime(2026, 1, 1),
)
],
)
# Run reform simulation
reform_sim = Simulation(
dataset=dataset,
tax_benefit_model_version=uk_latest,
policy=policy,
)
reform_sim.ensure()
For complex reforms that need programmatic control:
def my_reform_modifier(sim):
"""Modify the underlying policyengine_uk Microsimulation."""
# Modify parameters
sim.tax_benefit_system.parameters.get_child(
"gov.dwp.universal_credit.elements.child.limit.child_count"
).update(period="year:2026:10", value=float('inf'))
# Or modify inputs
employment_income = sim.calculate("employment_income", 2026)
sim.set_input("employment_income", 2026, employment_income * 1.05)
sim.tax_benefit_system.reset_parameter_caches()
policy = Policy(
name="Complex reform",
simulation_modifier=my_reform_modifier,
)
policy_combined = policy_a + policy_b # Chains modifiers
from policyengine.outputs.decile_impact import calculate_decile_impacts
results = calculate_decile_impacts(
dataset=dataset,
tax_benefit_model_version=uk_latest,
baseline_policy=None, # Current law
reform_policy=my_policy,
)
# Returns OutputCollection with .dataframe and .outputs
from policyengine.tax_benefit_models.uk.analysis import economic_impact_analysis
analysis = economic_impact_analysis(
baseline_simulation=baseline_sim,
reform_simulation=reform_sim,
)
# Returns PolicyReformAnalysis with:
# - decile_impacts
# - programme_statistics
# - baseline_poverty / reform_poverty
# - baseline_inequality / reform_inequality
from policyengine.outputs.aggregate import Aggregate, AggregateType
agg = Aggregate(
simulation=simulation,
variable="universal_credit",
aggregate_type=AggregateType.SUM,
entity="benunit",
)
agg.run()
print(f"Total UC spending: £{agg.result / 1e9:.1f}bn")
For quick parameter lookups (rates, thresholds), use the old API directly:
from policyengine_uk import CountryTaxBenefitSystem
params = CountryTaxBenefitSystem().parameters
# Personal allowance
pa = params.gov.hmrc.income_tax.allowances.personal_allowance.amount("2026-01-01")
# Basic rate (use .children["N"] for brackets)
basic_rate = params.gov.hmrc.income_tax.rates.uk.brackets.children["0"].rate("2026-01-01")
# UC standard allowance
uc_standard = params.gov.dwp.universal_credit.elements.standard_allowance.amount.single.over_25("2026-01-01")
When to use parameter lookup vs simulation:
UK uses ITL 1 regions:
NORTH_EAST, NORTH_WEST, YORKSHIRE, EAST_MIDLANDS, WEST_MIDLANDSEAST_OF_ENGLAND, LONDON, SOUTH_EAST, SOUTH_WESTWALES, SCOTLAND, NORTHERN_IRELANDRegional Tax Variations:
# WRONG - strips weights
values = output.household['income_tax'].values
mean = values.mean() # Unweighted!
# CORRECT - keep as MicroSeries
mean = output.household['income_tax'].mean() # Weighted
simulation.ensure() # Loads from cache if available
# NOT simulation.run() unless you want to force re-run
# New API uses brackets: uk[0].rate
param = uk_latest.get_parameter("gov.hmrc.income_tax.rates.uk[0].rate")
# Old API uses .children["N"]
params.gov.hmrc.income_tax.rates.uk.brackets.children["0"].rate("2026-01-01")
# New API - list of person dicts
UKHouseholdInput(people=[{"age": 35}, {"age": 8}], ...)
# Old API - nested situation dict (still works for old Simulation)
{"people": {"person1": {"age": {2026: 35}}, ...}}
Location: PolicyEngine/policyengine-uk
git clone https://github.com/PolicyEngine/policyengine-uk
cd policyengine-uk
Key directories:
policyengine_uk/variables/ - Tax and benefit calculationspolicyengine_uk/parameters/ - Policy rules (YAML)policyengine_uk/reforms/ - Pre-defined reformspolicyengine_uk/tests/ - Test casesAll UK parameters MUST have legislation.gov.uk references with exact section links.
Primary legislation:
Secondary legislation:
Reference format:
metadata:
reference:
- title: Universal Credit Regulations 2013, Schedule 4, Table 3
href: https://www.legislation.gov.uk/uksi/2013/376/schedule/4