Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/uc-capital-corporate-wealth.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Universal Credit capital test: excluded `corporate_wealth` from the countable capital sources as an interim approximation pending a dataset-side pension split (policyengine-uk-data#452). The dataset builds that variable as private pension wealth (which Schedule 10 paragraph 10 of the Universal Credit Regulations 2013 disregards) bundled with shares, investment ISAs and collective investments (which the law counts); counting the bundle failed the capital test for claimants whose only large asset is a pension, and excluding it leaves the share-like components uncounted until the dataset separates them. Housing Benefit, JSA, ESA, Income Support and Pension Credit still count the bundle through their own source lists, and the CI regression fixture for the UC taper reform was re-baselined to the new caseload.
2 changes: 1 addition & 1 deletion docs/book/programs/gov/dwp/universal-credit.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
{
"cell_type": "markdown",
"metadata": {},
"source": "## How PolicyEngine computes Universal Credit\n\nPolicyEngine computes Universal Credit at the benefit-unit level in three stages:\n\n1. **Maximum entitlement** (`uc_maximum_amount`): sum the elements the benunit qualifies for — `uc_standard_allowance`, `uc_child_element`, `uc_disability_elements`, `uc_carer_element`, `uc_housing_costs_element`, and `uc_childcare_element`.\n2. **Means test**: the maximum entitlement is reduced by the benunit's countable earnings above its work allowance (`uc_work_allowance`) at the published taper rate (the `gov.dwp.universal_credit.means_test.reduction_rate` parameter, currently 55%), plus its unearned income. Asset rules apply on top of this — capital above the lower threshold deems a tariff income, and capital above the upper threshold disqualifies the benunit entirely.\n3. **Benefit cap**: the post-means-test award (`universal_credit_pre_benefit_cap`) is finally reduced by `benefit_cap_reduction` to produce `universal_credit`. The benefit cap only applies to benunits without a benefit-cap exemption (working enough hours, having a qualifying disability benefit, etc.).\n\nThe take-up step is handled by the input variable `would_claim_uc`, which is populated stochastically when the dataset is built so that PolicyEngine's caseload aggregates match published DWP claimant numbers rather than the full eligible population.\n\nParameters live in `policyengine_uk/parameters/gov/dwp/universal_credit/` and the per-element formulas in `policyengine_uk/variables/gov/dwp/universal_credit/`."
"source": "## How PolicyEngine computes Universal Credit\n\nPolicyEngine computes Universal Credit at the benefit-unit level in three stages:\n\n1. **Maximum entitlement** (`uc_maximum_amount`): sum the elements the benunit qualifies for — `uc_standard_allowance`, `uc_child_element`, `uc_disability_elements`, `uc_carer_element`, `uc_housing_costs_element`, and `uc_childcare_element`.\n2. **Means test**: the maximum entitlement is reduced by the benunit's countable earnings above its work allowance (`uc_work_allowance`) at the published taper rate (the `gov.dwp.universal_credit.means_test.reduction_rate` parameter, currently 55%), plus its unearned income. Asset rules apply on top of this — capital above the lower threshold deems a tariff income, and capital above the upper threshold disqualifies the benunit entirely. Countable capital is configured by `gov.dwp.universal_credit.means_test.capital.sources` (savings and property other than the home); `corporate_wealth` is excluded as an interim approximation because the dataset bundles pension wealth, which the regulations disregard, with shares and ISAs, which they count (see the parameter description and policyengine-uk-data#452).\n3. **Benefit cap**: the post-means-test award (`universal_credit_pre_benefit_cap`) is finally reduced by `benefit_cap_reduction` to produce `universal_credit`. The benefit cap only applies to benunits without a benefit-cap exemption (working enough hours, having a qualifying disability benefit, etc.).\n\nThe take-up step is handled by the input variable `would_claim_uc`, which is populated stochastically when the dataset is built so that PolicyEngine's caseload aggregates match published DWP claimant numbers rather than the full eligible population.\n\nParameters live in `policyengine_uk/parameters/gov/dwp/universal_credit/` and the per-element formulas in `policyengine_uk/variables/gov/dwp/universal_credit/`."
},
{
"cell_type": "code",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,26 @@ description: >
treated as income or disregarded under regulation 48 and Schedule 10. The
current mappings are `savings` for liquid capital,
`other_residential_property_value` for residential property other than the
home, `non_residential_property_value` for other land and premises, and
`corporate_wealth` for shares and similar investment assets. `main_residence_value`
is excluded because Schedule 10 paragraph 1 disregards premises occupied as
the person's home. Other Schedule 10 disregards within these broad asset
classes are not yet split out separately. Where only household-level capital
is available for a multi-benunit household, the model uses a household-wide
home, and `non_residential_property_value` for other land and premises.
`main_residence_value` is excluded because Schedule 10 paragraph 1 disregards
premises occupied as the person's home. `corporate_wealth` is excluded as an
interim approximation pending a dataset-side pension split
(policyengine-uk-data#452). In law, shares, investment ISAs and collective
investments are countable capital with no general Schedule 10 disregard, but
the dataset builds `corporate_wealth` as total private pension wealth less
current defined benefit pension wealth plus employee shares and options, UK
shares, investment ISAs and collective investments, and Schedule 10
paragraph 10 disregards the value of rights under an occupational, personal
or other registered pension scheme. Pension wealth is most of that bundle,
so counting it fails the capital test for claimants whose only large asset
is a pension; excluding it leaves the share-like components uncounted until
the dataset separates them from pensions, at which point the share-like
remainder should return to this list. Other disregards within these broad
asset classes (business assets under Schedule 10 paragraphs 7 and 8, life
policies under paragraph 9, owner-managed company holdings under regulation
77, and personal-injury and compensation sums under regulations 75 and 76)
are not yet split out separately. Where only household-level capital is
available for a multi-benunit household, the model uses a household-wide
adult-share proxy for residual capital because the dataset does not identify
which adults hold the beneficial interest in each capital asset. The
denominator is all unreported adults in the household, including adults in
Expand All @@ -31,9 +45,10 @@ metadata:
href: https://www.legislation.gov.uk/uksi/2013/376/regulation/48/1
- title: The Universal Credit Regulations 2013 Schedule 10 para. 1
href: https://www.legislation.gov.uk/uksi/2013/376/schedule/10/paragraph/1
- title: The Universal Credit Regulations 2013 Schedule 10 para. 10
href: https://www.legislation.gov.uk/uksi/2013/376/schedule/10/paragraph/10
values:
2013-04-29:
- savings
- other_residential_property_value
- non_residential_property_value
- corporate_wealth
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@ description: >
from capital, for example rental, interest or dividends. In the current
model, those map to `property_income` when countable property capital is
present, `savings_interest_income` when savings are present, and
`dividend_income` when corporate wealth is present. Those linked income
streams are removed from `uc_unearned_income` once `uc_tariff_income`
applies to avoid double counting the same capital. The model does not add
`dividend_income` when corporate wealth is present. `corporate_wealth` is
no longer itself a countable capital source (`capital/sources.yaml`
excludes the mixed pension bundle as an interim approximation), but it
still gates the dividend fold because it is the only available signal that
the benunit holds shares, which remain countable capital in law; once
tariff income arises from savings or property, regulation 72(3) treats
dividends from those shares as capital rather than income. Those linked
income streams are removed from `uc_unearned_income` once `uc_tariff_income`
applies, which for savings and property avoids double counting the same
capital and for dividends approximates the legal treatment of income from
shares the dataset cannot separate from pensions. The model does not add
annual income flows back into `uc_assessable_capital`, because the dataset
does not identify whether those receipts are retained as capital within the
assessment year. When benunit-level `uc_reported_capital` is provided, the
Expand Down
5 changes: 4 additions & 1 deletion policyengine_uk/tests/microsimulation/reforms_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ reforms:
expected_impact: -1.2
parameters:
gov.hmrc.child_benefit.amount.additional: 25
# Re-baselined when corporate_wealth left the UC countable capital sources
# (interim pension-bundle exclusion): the taper reform now operates on the
# larger eligible caseload.
- name: Reduce Universal Credit taper rate to 20%
expected_impact: -18.1
expected_impact: -30.3
tolerance: 3.0
parameters:
gov.dwp.universal_credit.means_test.reduction_rate: 0.2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@
uc_assessable_capital: 50_000
is_uc_eligible: false

- name: Countable corporate wealth can push capital over the upper threshold
- name: Corporate wealth is excluded from Universal Credit capital pending a pension split
period: 2025
absolute_error_margin: 0
input:
Expand All @@ -295,8 +295,8 @@
members: person
corporate_wealth: 50_000
output:
uc_assessable_capital: 50_000
is_uc_eligible: false
uc_assessable_capital: 0
is_uc_eligible: true

- name: Main residence value is excluded from Universal Credit capital
period: 2025
Expand Down
4 changes: 4 additions & 0 deletions policyengine_uk/tests/test_stock_capital_periods.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def test_uc_capital_stocks_are_not_prorated_in_monthly_calculations():
"members": ["person"],
"savings": {"2026": 12_000},
"other_residential_property_value": {"2026": 12_000},
"non_residential_property_value": {"2026": 12_000},
"corporate_wealth": {"2026": 12_000},
}
},
Expand All @@ -23,6 +24,9 @@ def test_uc_capital_stocks_are_not_prorated_in_monthly_calculations():

assert simulation.calculate("savings", "2026-01")[0] == 12_000
assert simulation.calculate("corporate_wealth", "2026-01")[0] == 12_000
# corporate_wealth is not a countable UC capital source (interim
# approximation pending the dataset-side pension split), so assessable
# capital is the other three stocks, unprorated.
assert simulation.calculate("uc_assessable_capital", "2026-01")[0] == 36_000
assert not simulation.calculate("is_uc_eligible", "2026-01")[0]

Expand Down
89 changes: 89 additions & 0 deletions policyengine_uk/tests/test_uc_capital_sources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"""Interim Universal Credit capital scope: corporate_wealth is not counted.

The dataset builds ``corporate_wealth`` as total private pension wealth less
current defined benefit pension wealth plus employee shares and options, UK
shares, investment ISAs and collective investments. The Universal Credit
Regulations 2013 disregard pension rights (Schedule 10 paragraph 10) but count
shares, investment ISAs and collective investments. Until the dataset build
splits pensions from the share-like components (policyengine-uk-data#452), the
UC capital test excludes the whole bundle. These tests pin that scope: the UC
list itself, the UC assessment, and the deliberate decision that nothing
outside the UC chain changes.
"""

from policyengine_uk import Simulation

YEAR = 2025


def _single_adult_household(**household_inputs):
return {
"people": {"person": {"age": {YEAR: 30}}},
"benunits": {
"benunit": {
"members": ["person"],
"would_claim_uc": {YEAR: True},
}
},
"households": {
"household": {
"members": ["person"],
**{k: {YEAR: v} for k, v in household_inputs.items()},
}
},
}


def test_corporate_wealth_is_not_a_uc_capital_source():
simulation = Simulation(situation=_single_adult_household(savings=1_000))
parameters = simulation.tax_benefit_system.parameters
for instant in ("2013-04-29", "2020-01-01", f"{YEAR}-01-01", "2030-01-01"):
listed = list(
parameters(instant).gov.dwp.universal_credit.means_test.capital.sources
)
assert listed == [
"savings",
"other_residential_property_value",
"non_residential_property_value",
]


def test_uc_assessable_capital_excludes_corporate_wealth():
simulation = Simulation(
situation=_single_adult_household(savings=5_000, corporate_wealth=250_000)
)
assert simulation.calculate("uc_assessable_capital", YEAR)[0] == 5_000
assert simulation.calculate("is_uc_eligible", YEAR)[0]
# Below the 6,000 GBP tariff income threshold, so the pension bundle
# neither disqualifies the claimant nor deems a tariff income.
assert simulation.calculate("uc_tariff_income", YEAR)[0] == 0


def test_uc_still_counts_savings_and_property():
simulation = Simulation(
situation=_single_adult_household(
savings=7_000,
other_residential_property_value=6_000,
non_residential_property_value=4_000,
corporate_wealth=1_000_000,
)
)
assert simulation.calculate("uc_assessable_capital", YEAR)[0] == 17_000
assert not simulation.calculate("is_uc_eligible", YEAR)[0]


def test_corporate_wealth_still_feeds_wealth_and_legacy_capital_tests():
"""Scope guard: only the UC list changed in the interim fix.

Housing Benefit keeps counting the mixed bundle (its parameter description
documents that as an unresolved data limitation), and total_wealth includes
corporate_wealth regardless of any means test. The follow-up that splits
pension wealth in the dataset build should revisit the legacy lists and
update this test.
"""
simulation = Simulation(
situation=_single_adult_household(savings=1_000, corporate_wealth=50_000)
)
assert simulation.calculate("uc_assessable_capital", YEAR)[0] == 1_000
assert simulation.calculate("housing_benefit_assessable_capital", YEAR)[0] == 51_000
assert simulation.calculate("total_wealth", YEAR)[0] == 51_000