Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

UK: model landlord property income on the taxable (net of expenses) concept, not rent received

Open
#1,106 2 comments 0 reactions 1 assignee View on GitHub

Maintainers usually reply within 1 day

@juaristi22 is already working on this.

Since Oct 5, 2026.

Assessment

This issue has not been assessed yet.

Description

The question

HMRC has confirmed to an external user of its statistics that the Property Rental Income Statistics report property income as declared, before allowable expenses (repairs, insurance, legal and letting fees and so on). Net of those expenses, the totals line up with HMRC's other property-income figures and with the SPI public-use tape. The published PRIS amounts are therefore a gross concept, while income tax is charged on landlords' profit after allowable expenses.

This raises two questions:

  1. What property-income concept does Microcosm UK hold, and what does it calibrate against?
  2. How should property income be modelled, so that property-tax and income-tax analyses use the taxable concept at the right level?

Where Microcosm UK stands today (main)

  • The spine's property_income is FRS rent received, not landlord profit. frs_spine.py:585-599 builds it from the household head's sub-let rent (subrent, tenure types 5–6), plus cvpay (lodgers) and royyr1 (royalties), annualised. Landlords' income from letting other properties isn't in it as a profit concept.
  • No property-income target is bound. All 26 HMRC SPI property-income rows (13 amounts, 13 counts, by total-income band) are excluded. docs/uk-income-anchors-280.md ("Not done here") explains why: the SPI concept is landlords' net income after expenses, and the spine doesn't hold that. hmrc_income_replay_report.json fences the rows (full_frs_tei_band_unavailable, blocked on hmrc_spi_assessable_income).
  • There is no PRIS target anywhere.
  • WAS round 8's DVNetRentAmtAnnualR8 (net rent) is read only as a predictor in was_wealth, never as a target.

So the release neither overstates property income through a gross target nor corrects the FRS's known under-reporting of it. It is close to raw FRS, which is likely too low for landlords.

Related, outside this repo: the Enhanced FRS pipeline (policyengine-uk-data) calibrates property income to the PRIS amount (about £55.5bn for 2023-24). Given HMRC's clarification, that is a gross target, and it overstates taxable property income in analyses built on the Enhanced FRS. That should get its own issue in policyengine-uk-data; this one covers Microcosm UK.

What we should do

The target concept is taxable property profit after allowable expenses, the SPI concept. PRIS should be used for counts of landlords, not for amounts.

1. Put a landlord-profit variable on the spine
  • Keep sub-lets, lodgers and royalties as they are, under a name that says what they are, and add landlord net property profit as its own person-level input. Then the engine's property_income can be the taxable concept without mixing in other things.
  • Check what the engine treats as taxable property income before choosing names:
    • the £1,000 property allowance;
    • Rent-a-Room;
    • the s.24 finance-cost restriction. Residential finance costs aren't deducted from profit and get a 20% tax reduction instead, so "profit after expenses" in the SPI may or may not be before finance costs. Pin down which before binding anything.
2. Impute landlord profit from the SPI
  • Draw it through the existing SPI channel. Use a QRF on the SPI property-profit field, conditioned on:
    • age;
    • region;
    • total income and the other income components;
    • tenure;
    • WAS other-property wealth (the buy-to-let value that #1089 now places).
  • Keep it coherent with wealth. A household with landlord profit should hold other residential property, and the reverse should mostly hold too. The WAS coherence gate added in #1089 is the natural place for that check.
  • Use identity-keyed draws, as the newer stages do, so later stages don't reshuffle it.
3. Bind the right targets
  • Amounts: un-exclude the 26 SPI property-income rows once the spine holds the matching concept. They are net of expenses, which is the taxable concept.
  • Counts: the PRIS landlord count is fine to bind (or keep as a diagnostic beside the SPI counts), because expenses change the amount per landlord, not the number of landlords.
  • PRIS amounts: only as a diagnostic, reported against the modelled gross equivalent (profit plus imputed expenses) if we model expenses. Never as a bound amount.
4. Acceptance checks (receipts)
  • The aggregate net property profit against the SPI total, and against HMRC's net figures.
  • The landlord count against PRIS.
  • The distribution across the SPI income bands, inside the usual fit.
  • Income tax on property income against HMRC's estimate, if one is available.
  • A short note quantifying the gross-to-net gap (PRIS against SPI), so the size of the concept difference is on record.
5. Downstream
  • Flag in the release notes that property-tax analyses (LVT, high-value property charges, property CGT) should read the new landlord-profit variable once it lands.
  • Once a fix is decided, open the matching issue in policyengine-uk-data.

Suggested order

  1. Confirm the engine's taxable property-income definition (allowance, Rent-a-Room, finance costs).
  2. Pin down the SPI field and its concept (before or after finance costs).
  3. Add the spine variable and the SPI-channel imputation behind a stage, with the coherence check.
  4. Un-exclude the SPI amount rows, add the PRIS count, and run a licensed arm with receipts.

This can go on the #1095 tracker if that's simpler.

Dominant language
Python
Stars
0
Forks
4
Avg merge
1d 13h
Merged PRs (30d)
105

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from PolicyEngine/microcosm

All issues in PolicyEngine/microcosm

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.