PolicyEngine / PolicyEngine/microcosm
Capital gains as primitives: asset-class positions + accrued gains + realization model, with dual-mode provenance
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 0
- Forks
- 4
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 94
Description
Problem
"Realized capital gains" is a legal derivative, not an economic primitive: the US column encodes IRC realization rules, LT/ST classification, and step-up at death. Carrying it as a raw input column caused the dominant defect in the US→UK transfer benchmark (2026-07-05): transferred 1:1 into a UK market-income surface that captures almost no gains (CGT is separately administered, outside FRS income), it inflated UK household market income 36× over native — and 181× after the feed-vintage upgrade, because better source data cannot repair a wrong-concept mapping. ESS analysis confirmed it is not a weight artifact (transferred ESS 39.7% of n vs native 2.4%). See policyengine-uk-data#438 for the sibling feed defect found in the same benchmark.
Columns that encode law don't transfer; primitives do.
Proposal
populace carries law-free primitives; the rules engine computes each country's legal gain concept.
- Primitives layer (the support): asset-class positions with accrued-gain (basis) fractions — SCF-imputable (the SCF measures unrealized gains by asset class), and the ECPS already carries
stock_assets,bond_assets,net_worthas a partial base. NOT per-transaction records in v1: nothing observes them at population scale, and current US/UK law needs only class-level positions plus a long/short share. Per-transaction becomes a spec-driven escalation if some country's rules demand it. - Realization model (the economy slot): converting accrued to realized gains is behavior (timing, lock-in, rate elasticity), not law — v1 is a cross-sectional realization hazard calibrated to admin totals; the model ships as a refereed, versioned occupant.
- Rules (Axiom): each country's gain concept computed from positions × realizations × statute — companion issue in axiom-rules-engine.
- Dual-mode provenance: where a source measures realized gains (PUF), the native file keeps the measured column; the computed path serves transfers and unmeasured countries. The per-column provenance class (measured vs modeled, per geography) plugs into the #159 provenance vocabulary.
- Referee targets: IRS SOI realizations and HMRC CGT statistics (taxpayers, gains, liabilities by band) as ledger facts — calibration and validation for the computed path.
Related: #274 (Schedule-D routing within the existing realized-gains column) remains valid for the measured-mode column; this issue is about adding the primitives/computed mode beside it. Companion issues: axiom-rules-engine (law encodings), populace-dynamics (trajectory state + death-basis events).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reviewing this proposal alongside #159 and #274, then inspect the existing ECPS asset fields and provenance handling referenced in the issue. Map the primitives, realization model, measured-versus-modeled provenance, and referee targets before implementation; done means the design is specified well enough to support both native measured data and computed transfers.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend, data
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100