Conversation
The SPI income model predicts from age, gender and region only; applying it to the FRS half gave Universal Credit claimants six-figure dividends unrelated to anything they reported (policyengine-uk#1948). Only the SPI-donor copy is now imputed, as in the microcosm UK build. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frs.py summed investment-account dividends to person.index while account.person_id holds household_id * 1e3 + person, so only 8 people got any (about £40m against £8bn). Extract the calculation into a tested helper and annualise with WEEKS_IN_YEAR like the savings lines beside it. Correct the income.py comments that relied on the near-zero FRS dividends. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 30, 2026
Draft
25 tasks
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #495. Fixes the data cause of PolicyEngine/policyengine-uk#1948.
Summary
The enhanced FRS gives Universal Credit claimants six-figure dividends that no respondent reported (PolicyEngine/policyengine-uk#1948). Two defects in the FRS half cause it:
frs.pykeys the survey's dividends on row position. It sums investment-account dividends toperson.index, whileaccount.person_idholdshousehold_id * 1000 + person. Almost nothing matches: in the 2024-25 FRS about £40m of dividends survives, against £8.0bn across 1,717 people when keyed onperson_id(gross4weights). The interest lines a few rows above key onperson_idcorrectly.impute_incomeoverwrites them anyway. It replaces every FRS respondent'sdividend_incomewith a draw from the SPI income model, whose only predictors are age, gender and region. So dividends land on people without regard to their investments, earnings or benefits. In the published data, UC claimants receive them about as often as anyone else.This PR fixes both.
frs_dividend_income, keyed onperson_idand annualised withWEEKS_IN_YEARlike the savings lines beside it. It was* 52, a 0.35% difference.impute_incomeno longer imputes the FRS half. The SPI-donor half still carries the SPI income distribution, and calibration to the HMRC dividend band targets reweights between the two halves.Evidence the dividends were not the survey's
Linking the published-style artifact (
enhanced_frs_2024_25.h5, sha256e433e532…, run with policyengine-uk main44240bd8, 2026) to the raw FRS 2024-25 gives these aggregates:dividend_incomeThose households report no dividends from a company they direct, so these are not owner-managers either. PolicyEngine/policyengine-uk#1965 adds UC's owner-manager rule (reg 77) separately.
Measured: rebuilds of main and this branch
Builds. Both are real local builds with production settings (512 epochs,
PE_UK_DATA_OA_CLONES=1). They used the same trained imputation models, the same raw inputs and the same environment (uk-datauv.lock, policyengine-uk 2.93.0 for the build).b45c373, dataset sha2566baf2953….c9b203e, dataset sha256c9878b7f….Both logs show the same failure to download HMRC's salary sacrifice CSV (410 Gone), so that target source is missing from both builds equally (below).
Simulation. Each dataset was simulated with policyengine-uk main (
44240bd8) and with PolicyEngine/policyengine-uk#1950 (473adff9). #1950 applies UC Regs reg 66(1): dividends, interest and rent are never UC unearned income. Stored weights; aggregates only.Dividends (policyengine-uk main, 2026)
The #1948 cohort (2026)
On this branch, no FRS-half record has UC and dividends above £20k, under either rule set. What remains is SPI-donor rows only: 25 records over £20k and 13 over £50k under #1950. Those rows take their household wealth and
would_claim_ucfrom the FRS donor household they were copied from.impute_frs_only_variablesre-imputes benefit reports and pension contributions, but not wealth or take-up. Making those consistent with the SPI incomes is the remaining capital-coherence work in #495; the microcosm build hasuc_capital_coherenceanduc_reporter_redrawstages for this.The data fix also halves what #1950 costs: +£1.93bn of UC on the main build against +£0.90bn on this branch (2026). Much of #1950's measured gain on the published data went to imputed dividends.
Everything else (policyengine-uk main)
These moves come from calibration. With FRS respondents no longer carrying SPI-scale dividends, the optimiser puts more weight on the SPI-donor half to meet the HMRC dividend targets (+0.58m benefit units), and that half has higher incomes. I have not decomposed them further.
Calibration fit (final epoch, 637 national targets)
HMRC dividend targets, estimate ÷ target, by total-income band:
The fit worsens in the £300k–£500k amount band and improves in the £500k–£1m band. The £1m-and-over band has an estimate of zero in both builds, a pre-existing gap I have not investigated.
Other targets that moved most, from main to branch, are listed in
national_fit.txtamong the evidence below. Examples: property income count at £500k–£1m, 2.25 → 1.11; self-employment count at £500k–£1m, 2.01 → 1.04; private pension count at £300k–£500k, 1.02 → 1.28; ONS savings interest, 0.80 → 0.77.Tests
test_frs_dividend_income.py(new): accounts land on the person whoseperson_idholds them, not on whoever sits at that row. It also checks the account-type and gross-up rules and that dividends are never negative.test_imputation_source_flags.py::test_impute_income_keeps_reported_dividends_on_the_frs_half(new): only the SPI-donor copy is imputed, and FRS-half dividends and earnings pass through unchanged. It fails on main.Not in this PR
DIVIDENDis not read. Despite its label, the stored value is a weekly net amount: it equals the adult table'sDIVIDNETfor 176 of 177 people. The FRS puts directors' dividends at £12-20bn a year net (DIVIDNETandDIVIDEND× 52,gross4). Adding them needs a net-to-gross conversion and a check that they are not already in other income fields or in the investment accounts.owned_company_*inputs from Model Universal Credit reg 77 for owner-managers of a company policyengine-uk#1965 (fromDIRECTOR,DIRPERCandDIVIDEND) needs that release first.would_claim_uc(Capital stocks disagree with income from capital, so UC capital tests miss rent, share and savings holders #495).hmrc_spi_income_spinestage redraws base-FRS adults' dividends from the same age/gender/region forest (spi_income.py, required byuk/spec/sources.yaml). It needs the matching change; that is tracked separately.This changes a published dataset, so the merge is a data-release decision.
Evidence (local, aggregates only):
~/reviews/uc-dividends-1948-2026-09-30/(impact/rebuild/,data-build/*.log,diag/).🤖 Generated with Claude Code