From data-science
Provides API patterns for policyengine Python package to calculate UK household taxes, benefits, net income, and run population microsimulations for policy analysis.
npx claudepluginhub policyengine/policyengine-claude --plugin data-scienceThis skill uses the workspace's default tool permissions.
> **IMPORTANT: Always use the current year (2026) in calculations, not 2024 or 2025.**
Runs PolicyEngine microsimulations for population-level policy analysis: costs, revenues, poverty rates, distributional impacts, winners/losers, Gini, state-level effects.
Provides Japanese tax advice on deductions, filings, business expenses, and 2025 rules using reference files for accurate consultations.
Conducts UK legal research via Lex API: searches Acts, sections, amendments, explanatory notes for legislation and statutory queries.
Share bugs, ideas, or general feedback.
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