Repository navigation
Split VAT grossing into household share and survey coverage - #2187
vahid-ahmadi wants to merge 3 commits into
Conversation
Review of #2187 — Split VAT grossing into household share and survey coverageBase repository: PolicyEngine/policyengine-uk Source DocumentsCriticalNone. Should AddressNone. SuggestionsNone. Evidence Gaps
Notes
Validation Summary8 passed, 1 warning in 3.92s, exit 0, bounded elapsed 8.75s. Existing Python 3.13 with exact snapshot PYTHONPATH and bytecode disabled; run_bounded.py --seconds 540 review-code-test-runner.py current-pr-2187-code-tests-config.json. Exact selected paths and pytest cache in config/runner; output current-pr-2187-code-tests.log. Captured incremental diff read. Three same-worktree HMRC originals/extracts checksum verified; current-pr-2187-review-sources.json and current-pr-2187-review-source-cache-check.json. No repeated blocked searches. Code behavior checked by uk_code. CI at eac21df: Install + smoke-import (py3.11)=SUCCESS, Test documentation builds=SUCCESS, Install + smoke-import (py3.12)=SUCCESS, Install + smoke-import (py3.13)=SUCCESS, Install + smoke-import (py3.14)=SUCCESS, Lint=SUCCESS, Test=SUCCESS. Detailed code, policy, source and reproduction artifacts were retained locally. Timingelapsed seconds: 1151.70s; setup seconds: 270.63s Review SeverityCOMMENT. Open findings: 0 critical, 0 should address, 0 suggestions. |
Replace the undated gov.simulation.microdata_vat_coverage (0.38, added in January 2023 with no derivation) with two documented factors: gov.simulation.vat.household_share_of_receipts (0.7, HMRC's share of VAT liabilities on household consumption) and gov.simulation.vat.survey_consumption_coverage (0.67, derived on the certified Microcosm UK 2024-25 release against OBR's 2024-25 VAT outturn and cross-checked with HMRC's ready reckoner). Document the derivation and rescale the 2pp VAT reform fixture. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Saved reforms that set gov.simulation.microdata_vat_coverage keep working: the parameter is restored with the product of the two new factors (0.469), and vat and baseline_vat use it in place of the two factors whenever it is moved away from that declared value (policyengine_uk/utils/vat.py). The check compares against the declared default rather than the baseline tree, so it also holds for Scenario parameter changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eac21df to
691267f
Compare
Energy-bill VAT sits outside both grossing factors from #2187, since the bills are calibrated to NEED and households bear that VAT in full. Re-derived the survey coverage against receipts net of energy VAT on the certified Microcosm UK 2024-25 release: 79.2 / (0.7 x (171.0 - 2.0)) = 0.670, which rounds to the existing 0.67. Records the derivation in the parameter and in docs/engineering/vat-coverage.md, and loosens a float32 difference check to 1p. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Rebased onto main (
@juaristi22 your 8 Oct review had no blockers. Could you approve when you have a moment? #2189 is stacked on this and rebased too. 🤖 Generated with Claude Code |
juaristi22
left a comment
There was a problem hiding this comment.
Re-reviewed 691267f5cce6dc755fc51649ec6b5c8421ecc35d. The rebase changed only the documentation navigation entry; VAT code, parameters and tests match the prior reviewed patch. Eight focused VAT tests pass, and the documentation build passed. I verified the OBR cash-basis VAT forecast and HMRC household-share/ready-reckoner inputs.
The remaining material evidence gap is the private Microcosm and Enhanced FRS pre-scaling VAT totals behind the 0.67 survey-coverage calibration and its use across datasets. Please document the inputs and calculation sufficiently to reproduce or bound that factor, including which datasets it applies to. I cannot approve that quantitative assumption yet.
Please also update the PR description: it says the legacy combined factor was removed, while this head retains it as deprecated, and it needs the required axiom: line (for example, a specific n/a reason if this is purely statistical calibration). The remaining current-head code workflow jobs were pending at review time.
…unded List the dataset revisions and SHA-256 hashes, the OBR and HMRC inputs, the formula and the per-year results behind 0.67, state that the factor is calibrated to Microcosm UK 2024-25 only, and bound it on that release (product 0.459 to 0.481, coverage 0.655 to 0.688). Label the Enhanced FRS figures by release: the earlier 0.927 was policyengine-uk-data 1.56.16; on 1.58.0 the implied coverage is 0.967 and 0.67 puts 2024-25 VAT 44% above receipts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Energy-bill VAT sits outside both grossing factors from #2187, since the bills are calibrated to NEED and households bear that VAT in full. Re-derived the survey coverage against receipts net of energy VAT on the certified Microcosm UK 2024-25 release: 79.2 / (0.7 x (171.0 - 2.0)) = 0.670, which rounds to the existing 0.67. Records the derivation in the parameter and in docs/engineering/vat-coverage.md, and loosens a float32 difference check to 1p. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@juaristi22 thanks. Pushed
Ruff is clean, and the VAT pytest and |
Fixes #1996.
What was wrong
vatandbaseline_vatdivide household VAT bygov.simulation.microdata_vat_coverage, a single value of 0.38 from 2010 with no derivation. It came in as a hard-coded constant in January 2023 ("Adjust VAT to hit admin targets", 787e6b9), set against the LCFS-based consumption imputation of the time, and moved to a parameter file in #1139.On today's datasets it no longer reproduces receipts. With 0.38, 2024-25 VAT is £211.8bn on Microcosm UK 2024-25 (24% above OBR's £171.0bn outturn) and £292.1bn on Enhanced FRS 2024-25 from policyengine-uk-data 1.56.16 (71% above).
Fix
The issue's option 3: the scalar is split into its two parts, so only one of them moves with the data.
gov.simulation.vat.household_share_of_receipts= 0.7. HMRC's VAT gap estimates put household consumption at around 70% of the VAT total theoretical liability; the rest is government, charities, VAT-exempt businesses and new housing. This is a published quantity and doesn't depend on the dataset.gov.simulation.vat.survey_consumption_coverage= 0.67. This is the share of household VAT liability that the dataset's consumption captures, derived on the certified Microcosm UK 2024-25 release.vat= statutory VAT on recorded consumption ÷ (coverage × household share). The product is 0.469, against 0.38 before.gov.simulation.microdata_vat_coverageis kept as a deprecated parameter, so existing reforms to it still work. Its value is now the product of the two factors (0.469). If a reform sets it to any other value,vatandbaseline_vatuse that value in place of the two factors (policyengine_uk/utils/vat.py); otherwise they use the two factors.test_vat_grossing_legacy.pycovers both cases.The derivation is in
docs/engineering/vat-coverage.mdand the parameter's metadata: dataset revisions and SHA-256 hashes, the OBR and HMRC inputs, the formula, per-year results for each dataset, a bound, and a snippet that reruns it.Derivation
f9d1922c)Pre-scaling VAT = 0.20 × standard-rated consumption + 0.05 × reduced-rated consumption, weighted (on Microcosm in 2024-25, 0.20 × £396.15bn + 0.05 × £25.02bn). Both datasets are in private Hugging Face repositories; the doc gives the revisions and hashes, so anyone with PolicyEngine access can rerun it, and the aggregates let anyone else check the arithmetic.
Independent check: HMRC's ready reckoner (June 2025) gives £8.8bn for one point on the standard rate in 2026-27. On Microcosm that needs a product of 0.481, within 3% of the level-based value.
Bound. Total VAT depends only on the product. On Microcosm it lies between 0.459 (receipts, 2026-27) and 0.481 (ready reckoner), so the coverage lies between 0.655 and 0.688 at a household share of 0.7. The value used, 0.67 (product 0.469), is inside that range.
The implied product moves by under 3% across 2024-25 to 2026-27, so the factors aren't dated by year. A change of default dataset can't be expressed through policy dates, so the doc says to re-derive the coverage when the default dataset changes.
Decision to confirm: which dataset the coverage targets
There's no default dataset in policyengine-uk. policyengine.py 6.2.1's managed UK default is still Enhanced FRS 2024-25, while Microcosm UK 2024-25 is the certified release the data work is moving to. I set the coverage on Microcosm. Compared with 0.38, that improves both datasets:
The factor applies to Microcosm UK 2024-25 only. On Enhanced FRS it still overstates VAT, by 38% to 44% in 2024-25 depending on the release.
Targeting Enhanced FRS instead (coverage 0.93 on 1.56.16, 0.97 on 1.58.0) would fix it there but put Microcosm 28% to 30% below receipts.
Effects
vatfalls by 19% (0.38 / 0.469), on any dataset.baseline_vatfalls the same way, sovat_changescales too.Interaction with #2166
#2166 adds an explicit domestic fuel and power VAT base from
electricity_consumptionandgas_consumption. This PR doesn't touch the energy side ofvat.py. When #2166 lands, an energy base already calibrated to an aggregate shouldn't be divided by the survey coverage again, and the reconciled energy share ofreduced_rate_vat_consumptioncomes out of the base this coverage was derived on. The coverage should be re-derived then.Tests
New
gov/hmrc/vat/vat_grossing.yaml:vatandbaseline_vat);The first fails on main (5,394.74 there).
YAML suite: 2,661 passed.
pytest (code_health, core and top-level tests, excluding the dataset-download tests): 3,187 passed, 2 skipped, 2 xfailed.
ruff formatandruff checkare clean, and the Towncrier draft renders the fragment under "Changed".Sources
axiom: n/a: statistical calibration of the VAT grossing factors to OBR receipts; no VAT rate, base or rule changes. rulespec-uk's
uk/policies/govuk/vat.yamltakes the coverage factor as a supplied simulation input, and the deprecatedmicrodata_vat_coveragepath still works.🤖 Generated with Claude Code