Skip to content
Open
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/social-rent-target-vintage.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Refresh the social rent calibration target to the English Housing Survey 2024-25 mean (GBP 129/week, from GBP 118/week in EHS 2023-24). The target is set for 2025 but was anchored to a 2023-24 vintage while the private rent target used ONS data from February 2026, understating the social basis by roughly 10% relative to its own target year.
27 changes: 22 additions & 5 deletions policyengine_uk_data/targets/sources/housing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,24 @@

Total mortgage payments, private rent, and social rent.

All three targets are set for 2025 and should be anchored to comparable
vintages. The social rent basis previously used EHS 2023-24 while the
private basis used ONS PRHI from February 2026, understating the social
target by roughly 10% relative to its own year.

Sources:
- ONS PRHI Feb 2026: UK avg private rent £1,374/month
https://www.ons.gov.uk/economy/inflationandpriceindices/bulletins/privaterentandhousepricesuk/march2026
- English Housing Survey 2023-24: avg social rent £118/week
https://www.gov.uk/government/statistics/english-housing-survey-2023-to-2024-rented-sectors
- English Housing Survey 2024-25: avg social rent £129/week (local authority
£120, housing association £134)
https://www.gov.uk/government/statistics/chapters-for-english-housing-survey-2024-to-2025-headline-findings-on-demographics-and-household-resilience/chapter-2-housing-costs-and-affordability
- EHS + devolved stats: ~5.4m private renters, ~5.0m social renters UK-wide

Known limitation: these are totals only, so an error in the household count
and an offsetting error in the mean cannot be detected. At the 1.40.3
dataset the private target is met (1.04x) with 6.99m renters at £256/week
against a basis of 5.4m at £317/week — the total matches by cancellation.
See PolicyEngine/policyengine-uk-data#454.
"""

from policyengine_uk_data.targets.schema import Target, Unit
Expand All @@ -18,12 +30,17 @@
# Mortgage: avg £1,100/month × 7.5m owner-occupiers with mortgage
_MORTGAGE_TOTAL = 1_100 * 12 * 7.5e6 # ~£99bn

# Social rent: EHS 2023-24 mean £118/week × 52 × 5.0m UK social renters
_SOCIAL_RENT_TOTAL = 118 * 52 * 5.0e6 # ~£30.7bn
# Social rent basis, kept as named components so the arithmetic is auditable
# and the vintage is explicit rather than buried in a product.
_SOCIAL_RENT_WEEKLY_MEAN = 129 # EHS 2024-25 (was 118 in EHS 2023-24)
_SOCIAL_RENT_HOUSEHOLDS = 5.0e6 # UK, derived: share x ONS household total
_SOCIAL_RENT_TOTAL = _SOCIAL_RENT_WEEKLY_MEAN * 52 * _SOCIAL_RENT_HOUSEHOLDS

_EHS_REF = (
"https://www.gov.uk/government/statistics/"
"english-housing-survey-2023-to-2024-rented-sectors"
"chapters-for-english-housing-survey-2024-to-2025-headline-findings-on-"
"demographics-and-household-resilience/chapter-2-housing-costs-and-"
"affordability"
)
_ONS_RENT_REF = (
"https://www.ons.gov.uk/economy/inflationandpriceindices/"
Expand Down
17 changes: 16 additions & 1 deletion policyengine_uk_data/tests/test_housing_targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

from policyengine_uk_data.targets.sources.housing import (
_MORTGAGE_TOTAL,
_SOCIAL_RENT_HOUSEHOLDS,
_SOCIAL_RENT_WEEKLY_MEAN,
_PRIVATE_RENT_TOTAL,
_SOCIAL_RENT_TOTAL,
get_targets,
Expand All @@ -20,10 +22,23 @@ def test_private_rent_total_in_range():


def test_social_rent_total_in_range():
"""Social rent total should be £20bn-£40bn (5.0m × ~£118/wk)."""
"""Social rent total should be £20bn-£40bn (5.0m × ~£129/wk)."""
assert 20e9 < _SOCIAL_RENT_TOTAL < 40e9


def test_social_rent_basis_components():
"""Pin the documented EHS basis so a silent edit is visible in review.

This does NOT detect a newer EHS release — it only fails if someone
changes the constant without changing this pin. A real staleness check
would have to fetch EHS, as ons_tenure.py and ons_households.py do for
their sources. Tracked as future work in the module docstring.
"""
assert _SOCIAL_RENT_WEEKLY_MEAN == 129 # EHS 2024-25
assert _SOCIAL_RENT_HOUSEHOLDS == 5.0e6
assert _SOCIAL_RENT_TOTAL == 129 * 52 * 5.0e6


def test_mortgage_total_in_range():
"""Mortgage total should be £80bn-£120bn (7.5m × ~£1,100/mo)."""
assert 80e9 < _MORTGAGE_TOTAL < 120e9
Expand Down