model 05 — tax-benefit microsimulation · pe-microsim · UK · US · hosted
Calculate taxes and benefits.
Compare current law with a reform for a UK or US household, or estimate population-wide revenue and distributional effects.
Statute as code, at household resolution.
The package encodes UK and US tax-and-benefit rules as parameterised statute and computes every variable for a household you describe — for the UK: income tax, National Insurance, Universal Credit, Child Benefit, Pension Credit; for the US: federal and state income tax, payroll tax, SNAP, TANF, EITC, CTC, SSI and Social Security. A reform is a flat dict of parameter path → new value, the same shape at household and population level.
Four layers: policyengine-core (the simulation framework,
originally forked from OpenFisca), policyengine-uk and
policyengine-us (the statutory rules per country), and
policyengine — the analyst-facing package this page
covers, which adds dataset management, reforms, structured outputs
and version-pinned release bundles for reproducibility.
Within the suite it is the complement to the macro engines, and it feeds two of them: the OLG model consumes its effective and marginal rates to build tax functions, and the OBR emulator takes its static costing as the direct effect and returns the second-round one. This page is the direct route to the engine itself.
Rules, entities, and a dependency graph.
Every quantity in the system — an allowance, a taper, a means test — is a variable with a formula and a set of dated parameters. Asking for one variable resolves the graph beneath it, so a household net-income figure is computed from the statutory chain rather than approximated by a schedule. Variables are attached to entities, and the entity structure differs by country: the UK has three (person, benefit unit, household), the US six (person, tax unit, SPM unit, family, marital unit, household), because US programmes are assessed on differently drawn units.
A reform re-dates a parameter and re-resolves the same graph, which is why the household and population APIs take reforms in the identical format. Each release pins the country-model and dataset versions it certifies, so a published estimate can be traced to the exact bundle that produced it.
A household in one call.
Published on PyPI. The base install is shared tooling only; add
[models] for the pinned UK + US country models, or a
single country extra.
pip install policyengine # shared tooling, no country models
pip install "policyengine[models]" # + pinned UK and US models
pip install "policyengine[uk]" # UK model only
pip install "policyengine[us]" # US model onlyDescribe the people, pick a year, read the results. Household calculations need no data access and no credentials.
import policyengine as pe
# UK: single adult earning £50,000 — current law
uk = pe.uk.calculate_household(
people=[{"age": 35, "employment_income": 50_000}],
year=2026,
)
print(uk.person[0].income_tax) # £7,486
print(uk.person[0].national_insurance) # £2,994
print(uk.household.hbai_household_net_income) # £39,520
# US: single filer in California, with a reform applied
us = pe.us.calculate_household(
people=[{"age": 35, "employment_income": 60_000}],
tax_unit={"filing_status": "SINGLE"},
household={"state_code": "CA"},
year=2026,
reform={"gov.irs.credits.ctc.amount.adult_dependent": 1000},
)
print(us.tax_unit.income_tax, us.household.household_net_income)
run the same household with and without reform and
difference the results — that is the household-level reform impact.
For the UK earner above, raising the basic rate to 25%
({"gov.hmrc.income_tax.rates.uk[0].rate": 0.25}) adds
£1,872 of income tax and cuts net income by the same
amount.
The same package runs whole representative datasets — the enhanced UK Family Resources Survey, the US CPS — so any variable can be aggregated by decile, entity or demographic:
import policyengine as pe
from policyengine.core import Simulation
from policyengine.outputs.aggregate import Aggregate, AggregateType
datasets = pe.uk.ensure_datasets(datasets=["enhanced_frs_2023_24"],
years=[2026], data_folder="./data")
simulation = Simulation(dataset=datasets["enhanced_frs_2023_24_2026"],
tax_benefit_model_version=pe.uk.model)
simulation.run()
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")HUGGING_FACE_TOKEN from an account with access to
policyengine/policyengine-uk-data before running UK
population examples. On the hosted MCP server this credential is
provisioned server-side, so hosted population scoring needs nothing
from you.
From one family to the whole distribution.
| question | how |
|---|---|
| What does this reform do to this family? | calculate_household / household_reform_impact — every tax, benefit and income variable at person, benefit-unit/tax-unit and household level. Live on the hosted MCP server and the pe-macro CLI. |
| What does it cost, and who wins? | population_reform_impact and pe.uk.economic_impact_analysis — budgetary impact, decile changes, winners and losers, poverty, inequality, regional impacts. Hosted, with the enhanced-FRS credential provisioned server-side. |
| What levers exist? | list_reform_parameters — the parameter tree you can write reforms against. |
Checked rule by rule, not against a forecast.
The verification question here is different in kind from the rest of the suite. There is no forecast error to report, because the model is not forecasting: it is applying published rules. The claim is that it applies them correctly, and that claim is checkable by hand. For the £50,000 UK earner in section 03: £50,000 − £12,570 personal allowance = £37,430 of basic-rate income; at 20% that is £7,486 of income tax, at 8% £2,994 of National Insurance, leaving £39,520 net. Raising the basic rate 5pp adds 5% of £37,430 = £1,872. Every figure printed by the code sample is deterministic arithmetic from the implemented rules. This example verifies those rules; it is not a claim that every area of legislation has complete test coverage.
What is not exact is anything resting on survey microdata — population aggregates inherit sampling error, imputation and ageing assumptions from the enhanced FRS and the CPS, so a headline budgetary cost is an estimate in a way a household calculation is not. Comparisons of PolicyEngine's static costings against official sources are set out for the UK 1p basic-rate case in the OBR emulator working paper: £6.46bn in 2026 rising to £7.38bn by 2030, inside the range of HMRC's June 2025 ready reckoner (£6.9bn rising to ~£8.2bn), toward its lower end. Suite-wide evidence: validation.
Scope and vintage.
| limit | detail |
|---|---|
| Static, no general equilibrium | Reforms are modelled statically with optional post-hoc behavioural responses. Prices, wages and output do not move. |
| UK data vintage | UK population runs use enhanced_frs_2023_24. Upstream now publishes an FRS 2024-25 vintage, which this integration has not adopted. |
| Survey-based aggregates | Population estimates inherit sampling, imputation and ageing error. HMRC's own costings use administrative Survey of Personal Incomes data and embed taxable-income elasticities, so differences of a few hundred million on a broad-based rate change are expected. |
| Gated UK microdata | A HUGGING_FACE_TOKEN with access is required locally. Household calculations are unaffected. |
| Statute-vintage divergence with OLG | This package tracks the latest release; the OLG member is pinned to policyengine-uk==2.88.0, so the two can disagree about baseline statute. A declared inconsistency. |
| Two countries | UK and US only. |
no standalone working paper for this member yet — its behaviour is documented through the country model repositories and, where it feeds the macro members, in the OBR and OLG papers.