Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
7a5076c
Value means-tested capital less sale expenses and secured debt
MaxGhenis Sep 30, 2026
0d315ed
Tighten citations and tests from the adversarial review
MaxGhenis Oct 1, 2026
e51391f
Cite R(IS) 21/93 for real property in the HB sale-expense sources
MaxGhenis Oct 1, 2026
ea1e429
Merge origin/main into uc-capital-sale-expenses
Oct 3, 2026
22274ca
Value capital under reg 19 in two Pension Credit tests from main
MaxGhenis Oct 4, 2026
a885743
Merge origin/main into uc-capital-sale-expenses
Oct 12, 2026
f3929c1
Value capital under reg 19 in the corporate_wealth CTR case from main
MaxGhenis Oct 12, 2026
503bebb
Merge remote-tracking branch 'origin/main' into uc-capital-sale-expenses
MaxGhenis Oct 12, 2026
54ed063
Skip passported families in the HB valuation property
MaxGhenis Oct 12, 2026
3abaf16
Assert nil HB capital for passported units and keep non-passported cover
MaxGhenis Oct 13, 2026
b541677
Merge remote-tracking branch 'origin/main' into uc-capital-sale-expenses
MaxGhenis Oct 13, 2026
1efee62
Guarantee both Housing Benefit branches are exercised
MaxGhenis Oct 13, 2026
2eb4e52
Value the savings-credit-only capital test under reg 19
MaxGhenis Oct 19, 2026
a7d8988
Merge origin/main into uc-capital-sale-expenses
Oct 19, 2026
ce8493d
Consume a recorded total's holding at its counted value; round to the…
MaxGhenis Oct 20, 2026
57f9370
Value capital less sale expenses in float64, to the penny
MaxGhenis Oct 20, 2026
b3f6f60
Round capital to the penny as each programme's regulations require
MaxGhenis Oct 21, 2026
9b9ef66
Keep stored whole pennies through float64 paths; fix the CI failures
MaxGhenis Oct 22, 2026
acafa19
Merge origin/main: value share_like_capital less sale expenses
MaxGhenis Oct 24, 2026
8123540
Value each Lifetime ISA to the penny in float64 under each programme'…
MaxGhenis Oct 24, 2026
0531bc9
Add overseas and other property components on capital valuation stack
MaxGhenis Oct 26, 2026
825fe3d
Tighten capital source validation and document UC report routing
MaxGhenis Oct 26, 2026
bc742b9
Reuse a read-only model for property metadata assertions
MaxGhenis Oct 26, 2026
199c534
Exercise formula properties with fresh lightweight simulations
MaxGhenis Oct 26, 2026
442f033
Use the current Towncrier fragment style
MaxGhenis Oct 26, 2026
28164b2
Merge origin/main into property-wealth-overseas-other
MaxGhenis Oct 27, 2026
1193614
Fix the company capital conservation rounding oracle
MaxGhenis Oct 27, 2026
6c5cbc8
Retain property alongside financial-only capital reports
MaxGhenis Oct 27, 2026
798491c
Sum domestic property directly when estimating household land
MaxGhenis Oct 27, 2026
a677cb3
Preserve supplied and reformed Lifetime ISA capital values
MaxGhenis Oct 28, 2026
6467438
Merge origin/main into capital sale-expense valuation
MaxGhenis Oct 28, 2026
d5c2a57
Fix the company capital conservation penny-rounding oracle
MaxGhenis Oct 28, 2026
b168dc5
Give the capital property households a cash ISA
MaxGhenis Oct 28, 2026
696ae34
Keep Core's defined_for mask in the exact Lifetime ISA valuation
MaxGhenis Oct 29, 2026
dbc0c58
Run no Lifetime ISA formula when its defined_for mask applies to nobody
MaxGhenis Oct 29, 2026
5efb62c
Merge main into uc-capital-sale-expenses
MaxGhenis Oct 29, 2026
fc066bd
Expect the sale-expense deduction on a stocks-and-shares ISA in UC ca…
MaxGhenis Oct 29, 2026
30d48c0
Merge main into uc-capital-sale-expenses
MaxGhenis Oct 31, 2026
3a3241c
Say which means tests deduct land debt
MaxGhenis Oct 31, 2026
962771f
Merge origin/main into property-wealth-overseas-other
claude Nov 2, 2026
0e5cd48
Pin zero new property inputs to main and state the Pension Credit sto…
claude Nov 2, 2026
e041345
Value the stocks-and-shares ISA less sale expenses in the stored-tota…
claude Nov 2, 2026
ff0552d
Pin row 8 UC capital, assert C2's pinned allocations exactly and chec…
claude Nov 2, 2026
188f64f
Narrow the zero-input comment, drop the redundant exact household tot…
claude Nov 2, 2026
f40cb89
Scope the engineering page's unmeasured effects to the compatible dat…
claude Nov 2, 2026
ba4de95
Merge main at 0080274d7 into uc-capital-sale-expenses
claude Nov 2, 2026
c11d7c1
Value the stocks-and-shares ISA less sale expenses in the stored-tota…
claude Nov 2, 2026
b4e8722
Merge main at 52b30809f into uc-capital-sale-expenses
MaxGhenis Nov 3, 2026
5b35e02
Merge uc-capital-sale-expenses at b4e872273 into the property-wealth …
claude Nov 3, 2026
35a2971
Shorten the overseas and other real estate changelog fragment
claude Nov 3, 2026
1aacd32
Drop the duplicate HOLDINGS_SOURCE import the merge left in uc_assess…
claude Nov 3, 2026
0d760fc
Describe the Pension Credit fix in the changelog and correct six comm…
MaxGhenis Nov 3, 2026
80e0d34
Correct the Pension Credit override text, test comments and legacy ci…
claude Nov 3, 2026
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/2577.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The model now counts overseas and other real estate in property wealth and in every means-tested capital assessment that includes household property. The Pension Credit formula now adds owned land, non-residential property and residential property other than the main residence to a financial-only capital report instead of letting the report replace them. The household land formula now applies regional land intensity to residential, non-residential and other real estate instead of property_wealth, and adds direct UK land once at full value. On the certified file, whose producer imputed the saved property_wealth total and the components separately, household land at data-year values rises from about £5,113bn to £5,521bn ([measurement](https://github.com/PolicyEngine/policyengine-uk/pull/2688#issuecomment-6449462413)).
1 change: 1 addition & 0 deletions changelog.d/uc-capital-sale-expenses.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Value Universal Credit, Housing Benefit, Income Support, income-based JSA, income-related ESA and Pension Credit capital at market value less 10% where a sale would incur expenses and less any debt secured on the asset, as UC Regs 2013 reg. 49(1) and its legacy equivalents require. The 10% applies to land, property and share-like capital (share_like_capital), not to cash savings; share-like capital also holds unit trusts, which have no costs of sale, and in datasets built before the private pension split it holds pension wealth, so the deduction reaches those too until the data separate them. Adds secured-debt inputs for other residential property, non-residential property and land. Each programme rounds a fraction of a penny in its capital calculations as its regulations require: to the nearest penny for Universal Credit (UC Regs 2013 reg. 6(1)) and Housing Benefit, and down for the legacy benefits and Pension Credit. A Lifetime ISA's surrender value is worked out from its balance and rounded under the same rule. Explicit Lifetime ISA capital inputs and reformed valuation formulas are preserved.
242 changes: 242 additions & 0 deletions docs/engineering/property-wealth-components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
# Property wealth components and UK land

The model derives worldwide gross property wealth from six household stock
inputs. The companion [policyengine-uk-data change #543](https://github.com/PolicyEngine/policyengine-uk-data/pull/543)
preserves the seven Wealth and Assets Survey (WAS) round-8 gross-property
terms in those six inputs. The earlier [data change #501](https://github.com/PolicyEngine/policyengine-uk-data/pull/501)
combines second homes and buy-to-let into one other-residential input and
separates property wealth from financial and private-pension wealth.

## Source and input contract

| WAS round-8 source | Saved household input | Coverage |
| --- | --- | --- |
| `DVhValueR8` | `main_residence_value` | Main residence |
| `DVHseValR8_sum` | `other_residential_property_value` | Second UK homes, including holiday homes and time-shares |
| `DVBltValR8_sum` | `other_residential_property_value` | UK residential property let for profit, added to second homes |
| `DVBlDValR8_sum` | `non_residential_property_value` | Other UK buildings |
| `DVLUKValR8_sum` | `owned_land` | Direct UK land-only holdings |
| `DVLOSValR8_sum` | `overseas_property_value` | Overseas land and property, including time-shares |
| `DVOprValR8_sum` | `other_property_value` | Other real estate with unspecified geography and property type |

The data producer combines second homes and buy-to-let before exporting
`other_residential_property_value`. There is no additional buy-to-let model
input to add to an aggregate, benefit-capital list or purchase calculation.
The associated `other_residential_property_secured_debt` likewise combines
their identified secured debt.

The [WAS round-8 derived-variable specification](https://doc.ukdataservice.ac.uk/doc/7215/mrdoc/excel/7215_property_dv_spec_r8.xlsx),
worksheet `DV_Spec_Property_R8`, defines `DVPropertyR8` as the sum of all
seven source terms. This is gross property value. The separate `HpropWR8`
measure deducts mortgage liabilities. The model's gross wealth aggregates
retain this distinction from net benefit-capital valuations.

The data producer must reconcile the seven source terms before imputation,
with documented missing, not-applicable and refusal values handled
consistently and reported separately for complete-case and operational
zero-filled reconciliation. It must also remove a saved `property_wealth`
column, including one inherited from an earlier dataset. A conditioning-total
draw and independently imputed component draws do not have an accounting
identity: the model derives the released total from the saved components.

## Aggregates and land

The formulas under `policyengine_uk/variables/household/wealth/` implement:

```text
residential_property_value = main_residence_value
+ other_residential_property_value

property_wealth = residential_property_value
+ non_residential_property_value
+ owned_land
+ overseas_property_value
+ other_property_value

total_wealth = property_wealth + corporate_wealth
+ private_pension_wealth + savings

household_land_value = (residential_property_value
+ non_residential_property_value
+ other_property_value) * regional_intensity
+ owned_land
```

Residential property covers the identified domestic residential categories.
Overseas and other-real-estate aggregates do not identify residential
subcomponents. Direct UK land enters worldwide property wealth once and UK
household land once at its full value. Identified overseas land and buildings
contribute to worldwide wealth and contribute zero to UK household land.
`total_wealth` receives all property categories through `property_wealth`;
adding land, overseas or other property separately would duplicate them.

`household_land_value.py` retains the existing regional intensities and the
dwelling-weighted fallback for an unknown region. Summing the domestic base
directly avoids subtracting components from an already rounded aggregate,
so float32 cancellation cannot reduce land below directly owned UK land.
Unclassified other real
estate uses that regional approximation. Its unknown geography means this
can attribute overseas holdings to UK land. Improved source classification,
or excluding unclassified holdings from the domestic land base, requires a
separate modelling decision.

## Uprating and periods

`overseas_property_value` and `other_property_value` are annual household
stocks, defaulting to zero, with GDP-per-capita uprating. Their variable
definitions use `gov.economic_assumptions.indices.obr.per_capita.gdp` and
their dataset registrations use
`gov.economic_assumptions.yoy_growth.obr.per_capita.gdp` in
`policyengine_uk/data/uprating_indices.yaml`. Both registrations are required:
Core carries situation inputs forward using the variable attribute, while
`policyengine_uk/data/economic_assumptions.py` projects stored dataset inputs
using the YAML map. Stored annual inputs take precedence over formula and
fallback calculations.

Current `main_residence_value` and `other_residential_property_value` use the
model's annual OBR house-price index in both routes. Non-residential property
and direct UK land retain GDP-per-capita uprating. The property-component
change preserves these existing choices; the historical specification's
blanket GDP treatment for residential inputs is obsolete. Each projected
year must retain the aggregate identity using the individually uprated
components, rather than grow a saved property total with a single index.
Monthly requests for these stocks retain the full stock amount rather than
one twelfth of the annual amount.

The upstream regional house-price adjustment to main-residence imputation is
separate from the model's projection indexes. Source reconciliation must
precede that adjustment; the other mapped source categories do not acquire
the adjustment through the aggregate.

## Benefit-capital routing

Countable capital reads configured sources, rather than `property_wealth`.
The new overseas and other-property inputs enter each affected programme's
capital calculation once and receive the existing real-property
sale-expense treatment. The shared `policyengine_uk/utils/capital_valuation.py` helper
deducts source-specific sale expenses before mapped secured debt, with the
existing zero floor for secured-property categories. A source appearing only
in a sale-expense list does not become countable capital.

Capital parameters are under these paths relative to
`policyengine_uk/parameters/`:

| Programme | Capital directory |
| --- | --- |
| Universal Credit | `gov/dwp/universal_credit/means_test/capital/` |
| Housing Benefit | `gov/dwp/housing_benefit/means_test/capital/` |
| Income Support | `gov/dwp/income_support/means_test/capital/` |
| Income-based JSA | `gov/dwp/JSA/income/capital/` |
| Income-related ESA | `gov/dwp/ESA/income/capital/` |
| Pension Credit | `gov/dwp/pension_credit/income/capital/` |

Universal Credit keeps financial `sources.yaml` separate from
`property_sources.yaml` and `uc_property_capital`. Its existing domestic
property valuation variables remain in that property pool, independent of
financial `benunit_reported_capital` observations. The new overseas and
unclassified other-property categories follow the same route through
`uc_overseas_property_capital` and `uc_other_property_capital`, which deduct
sale expenses once before `uc_property_capital` allocates their net values.
They are absent from the financial and financial sale-expense source lists.
A financial-only report leaves all separately valued property counted.
A complete `uc_reported_capital` override replaces the ordinary assessment
before the existing company-capital substitution. All countable property
shares remain allocated across all claimant/partner adults, with
complete-override units consuming their shares rather than redistributing
them. Direct `owned_land` remains outside UC countable sources; adding it
requires a separate model decision.

Pension Credit retains its source lists and pension-age-adult allocation.
Its assessment combines a financial-only benefit-unit report with separately
valued land and property before final rounding. Its independent complete
override, `pension_credit_reported_capital`, defaults to -1; an explicitly
supplied nonnegative override still replaces the whole total. Combining the
financial report and property directly in the assessment avoids an intermediate
rounded aggregate that could reduce counted property for large values.
Housing Benefit's savings-credit-only route inherits that corrected total.
If a dataset stores a financial-only survey total in
`pension_credit_reported_capital` rather than in `benunit_reported_capital`,
the formula treats that total as a complete override and counts none of the
household's land or property for that unit. The data producer must move such a
total to `benunit_reported_capital` before the formula can count the property.
Ordinary Housing Benefit, Income Support, income-based JSA and income-related
ESA do not replace their source pools with a financial-only report, so their
existing routing is retained. `share_like_capital` retains the component-aware financial fallback,
so the new property sources do not restore `corporate_wealth` lists or add
the underlying share components twice. Main residence and the separately
identified private-pension input remain excluded from countable sources.

These broad asset categories do not identify every statutory disregard or
individual's beneficial ownership. Their allocation is a model assumption,
not an observation of legal ownership or a determination that every holding
is countable. No new secured-debt inputs are exported for overseas or other
real estate, so the mapping cannot identify or deduct debt on those holdings.
Legacy financial aggregates may also contain pension wealth that cannot be
separately identified. Gross worldwide wealth remains distinct from these
limited net capital proxies.

UC unearned income continues to use its claimant unearned-income source
list. Actual interest, dividends and rent are excluded at every capital
level; countable capital affects tariff income. There is no historical
property-income gate to extend.

## Purchases and downstream consumers

Current main uses explicit transaction inputs for
`additional_residential_property_purchased` and
`non_residential_property_purchased`. Holding other-residential or
non-residential stock does not automatically create a purchase when
`property_purchased` is true. This supersedes the historical stock-based
purchase proxy described in the original companion specification. A domestic
additional-dwelling transaction is supplied once, including a buy-to-let
purchase; overseas and unclassified other-property stocks create no UK SDLT
transaction. `main_residential_property_purchased` retains its existing
main-residence-value calculation.

The current consumer paths below replace historical wheel file-and-line
citations. Paths are relative to `policyengine_uk/variables/`.

| Consumer | Current path and dependency |
| --- | --- |
| Residential and worldwide property | `household/wealth/residential_property_value.py` and `property_wealth.py` use the component identities above |
| Total wealth and wealth deciles | `household/wealth/total_wealth.py` includes property once; `household_wealth_decile.py` reads total wealth with person-weighted household weights |
| Wealth taxes | `contrib/ubi_center/wealth_tax.py` reads `net_wealth`, which deducts mortgage, consumer and student-loan debt from total wealth; `contrib/cec/non_primary_residence_wealth_tax.py` subtracts main residence from gross total wealth |
| UK land and LVT | `household/wealth/household_land_value.py` estimates domestic household land; `land_value.py` adds corporate land; `contrib/ubi_center/land_value_tax.py` applies the configured combined, household and corporate rates |
| Domestic property purchases | `household/consumption/main_residential_property_purchased.py`, `additional_residential_property_purchased.py` and `non_residential_property_purchased.py` retain their separate transaction routing |
| SDLT | `gov/hmrc/sdlt_on_residential_property_transactions.py` and `sdlt_on_non_residential_property_transactions.py` read transactions; `stamp_duty_land_tax.py` aggregates liabilities and `expected_sdlt.py` carries the household liability |
| High Value Council Tax Surcharge | `gov/hmrc/high_value_council_tax_surcharge.py` reads main residence and ownership tenure directly |
| Benefit capital | `gov/dwp/universal_credit/uc_assessable_capital.py` and `uc_property_capital.py`; the assessable-capital variables for Housing Benefit, Pension Credit, Income Support, JSA and ESA use the configured programme sources |
| UC unearned income | `gov/dwp/universal_credit/income/uc_unearned_income.py` reads configured claimant income sources, without a property-income gate |

Changing the aggregate alone does not directly change benefit capital,
purchase transactions or the high-value surcharge at fixed inputs and
weights. Completing the missing components and their routing can change
capital; subsequent recalibration can also change weighted outputs.

## Release boundary and validation

The model and the data release carrying the component contract must land in
lockstep, after the data #501 prerequisite. That data release must depend on
a model release with the new inputs and capital-valuation routing. Its
compatibility guard must reject a model unable to carry the new components.

Existing datasets are not migrated. A stored `property_wealth` input remains
authoritative and overrides the formula, even after the model changes. The
new release must stop storing it and remove inherited copies; updating only
the installed model cannot repair old saved totals. Household land reads the
domestic component inputs directly, including when a saved worldwide total
is present, so that total does not determine the domestic land base.

Synthetic tests cover isolated and mixed components, aggregate identities,
land accounting, household LVT, monthly stocks, situation and dataset
uprating, benefit valuation and transaction routing without licensed data.
They do not establish reconciliation of the licensed WAS household source.
The upstream seven-term reconciliation must run on that licensed source
before release, with its missing-value report and the second-home/buy-to-let
combination checked before imputation.

Population and LVT effects with the compatible data release stay unmeasured
until that release exists and its rebuild and recalibration finish. Existing
land target definitions and target weights stay the same, but calculated land,
losses and fitted geographic weights can change. Production rebuilding, recalibration
and impact measurement are separate release work.
4 changes: 2 additions & 2 deletions policyengine_uk/data/economic_assumptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ def growth_rate(
#
# Past this year policyengine-core serves each stored input from the last
# projected year. An input whose variable has a formula is calculated by it
# instead (pension_credit_reported_capital reads benunit_reported_capital,
# which the Enhanced FRS does not store, so it falls to -1); one whose
# instead (the complete pension_credit_reported_capital override defaults
# to -1, while financial observations are assessed separately); one whose
# variable has an `uprating` attribute is scaled by that index; any other,
# rent and council tax among them, is held at its last projected value.
# Simulation grows the household totals in STORED_HOUSEHOLD_TOTALS itself.
Expand Down
9 changes: 7 additions & 2 deletions policyengine_uk/data/uprating_indices.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,17 @@ gov.economic_assumptions.yoy_growth.obr.per_capita.gdp:
- net_financial_wealth
- non_residential_property_value
- non_residential_property_secured_debt
- other_property_value
- other_residential_property_secured_debt
- overseas_property_value
# Non-residential purchase prices and land retain GDP-per-capita growth.
- non_residential_property_purchased
- owned_land
# A formula of benunit_reported_capital; listed so a dataset that stores it
# directly is uprated like the input.
- owned_land_secured_debt
# The formula returns -1, meaning no complete override;
# pension_credit_assessable_capital assesses a financial-only report through
# benunit_reported_capital instead. This index uprates programme observations
# that a dataset stores directly in the column.
- pension_credit_reported_capital
- pension_income
- private_pension_wealth
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
description: >
Whether a fraction of a penny in an income-related Employment and Support
Allowance capital calculation is disregarded (true) rather than rounded to
the nearest penny (false). A fraction of a penny is treated as a penny
only where that is to the claimant's advantage, and is otherwise
disregarded; for capital that means rounding down.
values:
2008-10-27: true
metadata:
label: Income-related ESA capital rounded down to the penny
period: year
unit: bool
reference:
- title: The Employment and Support Allowance Regulations 2008 reg. 3(a)
href: https://www.legislation.gov.uk/uksi/2008/794/regulation/3
- title: The Employment and Support Allowance Regulations (Northern Ireland) 2008 reg. 3(a)
href: https://www.legislation.gov.uk/nisr/2008/280/regulation/3
Loading
Loading