Skip to content

Two-universe calibration (ONS frame + HMRC registered subset), full regeneration, and paper revision - #46

Merged
vahid-ahmadi merged 13 commits into
mainfrom
fix/calibration-universes-37
Sep 6, 2026
Merged

Two-universe calibration (ONS frame + HMRC registered subset), full regeneration, and paper revision#46
vahid-ahmadi merged 13 commits into
mainfrom
fix/calibration-universes-37

Conversation

@vahid-ahmadi

Copy link
Copy Markdown
Contributor

Summary

Resolves the universe incompatibility in #37 by calibrating on two declared universes, regenerates every checked artifact, and revises the manuscript to match. This is the single regeneration María asked for; PRs #42#45 are cherry-picked onto this branch so their text lands once.

Two decisions taken (both flagged in #39 as mine to make):

  1. Population universe. The ONS VAT/PAYE enterprise frame is the modelled population; HMRC margins describe its VAT-registered subset and enter through a per-band registration propensity p_b = HMRC count / frame mass (0.89 below the threshold; 0.51–1.00 above, the remainder being PAYE-only/exempt enterprises). HMRC negative/zero traders are appended before calibration as an out-of-frame stratum. The 2023-24 employment CSV is rebuilt from Table 3 (enterprises) via scripts/etl_ons_tables.py, which also sources both vintages' turnover tables from Table 8; a test asserts the two tables agree to disclosure rounding and that the CSVs match the extract.
  2. Ageing convention. Turnover and liability are aged together against nominal thresholds (the paper's own fiscal-drag logic), growth indexed to each vintage's data year; anchor_reform and threshold_sweep now use one rule.

What changed in the results

before after
Calibration overall (2023-24 / 2024-25) 92.9% / 93.5% 98.0% / 98.0%
ONS population / employment (2023-24) 88.3% / 92.1% 99.7% / 99.8%
Kish ESS (2023-24 / 2024-25) 93,887 / 275,186 137,054 / 192,917
Anchor 2025-26 (full dereg. / 43% retention) vs HMRC −£185m −£364m / −£207m −£234m / −£134m
Sweep at £70k / £120k (2025-26) +£1,456m / −£1,553m +£187m / −£1,190m
Raise to £100k / linear taper / 10% / 15% −£876m / −£1,525m / −£515m / −£258m −£742m / −£1,406m / −£446m / −£223m
Constant-50% marginal taper (new) −£968m
Bunching E, 2023-24 (frame) 10,418 5,498
In-scope firms in dominated band 156,747 (all rows) 142,860

Why the big moves:

  • Anchor now lands between the two conventions because a rise releases only in-scope firms below the £88k deregistration threshold (statutory gap £2,000). A fixed cross-section has no entry margin, so 2027-28 undershoots HMRC's −£50m; the fixed-preference reading of the frame's 89% below-threshold registration share (−£38m) is reported as a lower bound and explained as frame selection.
  • Cut rows collapse because ~89% of below-threshold frame enterprises are already registered; the paper now calls them frame-confined lower bounds and points at Extend the population frame to all businesses (BPE) so threshold cuts and the administrative cliff are in-frame #25.
  • Reform menu is priced on in-scope firms only.
  • Taper: the £141,667 band top is a property of the linear design; a constant-50% marginal taper of the same width removes the notch at a third less cost with half the peak marginal rate (taper_band_top, make_schedule_taper_flat, tests).
  • Marginal buncher depends on δ under formulation A (£127k → £149k at δ=0.6, e=0.17); reported for δ ∈ {0, 0.4, 0.6}.

Fresh-reviewer items folded in

Deregistration threshold (1), taper band-top frontier (2), 2024-25 coarse-band bunching readback in Section 6 (3), stale "smooth-counterfactual sweep" text (4), literature additions Keen–Mintz, Kanbur–Keen, Dharmapala–Slemrod–Wilson, Mirrlees Review, BizTax (5), δ-dependent n_H (6), retention as a bound given HMRC's ~zero below-threshold liability (7), sector targets vs negative/zero traders (8), by-construction band accuracy stated (9), dominated-band claim rewritten (10), ageing factors and the [£70k,£130k] re-optimisation window stated (11), population statement (12), pass-through and FRS conventions stated (15).

Reproduction

make reproduce runs ETL → both vintages → every artifact → figure sync (reproduce-heavy adds seed sensitivity and placebo B). All 70 tests pass; ruff clean. No quarto locally: the Paper workflow renders the PDF and now uploads it as an artifact (paper-pdf); paper/main.pdf should be replaced from that artifact before merge.

Not done here (tracked)

Closes #37. Refs #32, #38, #39, #40, #41.

🤖 Generated with Claude Code

vahid-ahmadi and others added 7 commits September 4, 2026 09:38
…gistered subset (#37)

Data: rebuild the ONS tables from the raw workbooks with scripts/etl_ons_tables.py
(Table 8 turnover, Table 3 employment, both enterprises). The 2023-24 employment
CSV had reproduced Table 18 (local units, 3,173,660 sites); the 2024-25 tables
came from a different sheet with rounding differences. A test asserts the two
tables agree to disclosure rounding and that the CSVs match the ETL.

Calibration: every target row is declared on one universe. Frame rows
(population, employment, near-threshold shape) contribute 1 for ONS-frame
enterprises and 0 for the HMRC negative/zero traders, which are now appended
BEFORE calibration; HMRC rows (all 8 turnover bands, sectors, liability by
band) contribute a per-band registration propensity p_b = HMRC count / frame
mass (0.89 below the threshold, 0.51-1.00 above). The population target is
the full ONS total; the employment targets partition it.

Flags: assign_vat_flags returns (vat_scope, vat_registered) by seeded weighted
selection per band to the HMRC counts; below the threshold every frame firm is
registrable and the HMRC count is registered. Output gains vat_scope and
in_frame columns.

Consumers: validate/report score HMRC dimensions on registered rows and ONS
dimensions on frame rows; static, dynamic, reform-menu and dominated-region
code price in-scope firms only. Static model: turnover and liability are aged
together against nominal thresholds, growth is indexed to each vintage's data
year, baseline voluntary registrants stay registered, and a threshold rise
releases firms only below the deregistration threshold (gap £2,000).

Also: flat-50% marginal taper variant sharing the linear design's £141,667
band top; marginal buncher reported for delta in {0, 0.4, 0.6}; dynamic
results note derived from the table; make reproduce target; README updates.

Refs #37, #39, #40, #32.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…with the checked artifact

The width a = T*·tau/(1-tau) is proportional to the threshold, so raising
T* to £100,000 widens the dominated region to £25,000 (£100k–£125k); the
text said the £21,250 band was "transported" unchanged. Cascade the
correction to the intro, static section and conclusion.

Replace the reduced-rate primary/secondary band counts, which came from an
older run, with the values in results/dominated_region_mass.txt, and use
one baseline count (156,700) instead of 157,000 and 147,700 in the same
section. Round £21,562.50 to £21,563.

Refs #32 (finding 2, minor 1), #40.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…he code defines them

Section 6 described a mass-conservation restriction that locates y_R
endogenously and wrote excess bunching as the integral of the counterfactual
density. The code sums the positive part of f_obs - f_cf below the threshold
for E, searches for y_R only up to the window edge (censored at £100k in the
headline run, where Delta_R = 0), and normalises b_LLAT over both sides of
the threshold. Say so, and drop the b ≈ Δy*/y* relation that matches
neither statistic.

Refs #38, #32 (finding 5).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…to the region-confined convention

With deductible share delta the untaxed optimum is n(1-delta)^e, not n, so
say so and align the ability-recovery statement with the flat-optimum
equation (and note the code sets delta = 0 in the anchor). Recast the
"every potential crosser optimally stops at the new threshold" paragraph:
the region-confined solve clips released firms at the new notch by
construction, so the invariance is a property of the maintained status
convention, not a computed global-optimum result.

Refs #39, #32 (finding 4).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… table, drop orphan bib entry

Replace "the honest reading" and "spectacular" with neutral wording; point
Figure 1(a) at the ONS 2024 bulletin and name the table (the input is the
2024 workbook, not 2023); add the reduced-rate costs and the bunching
readback to the conclusion's summary table; remove the uncited
keenmintz2004 entry.

Refs #41, #32 (minor 7, 9, 11), #8.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…paper

Static model: explicit registration rule (in-scope firms registered at/above the
threshold; data-year registrants retained down to the £2,000 deregistration gap;
voluntary registrants unchanged where never required; fixed-preference and
LLAT-retention conventions as labelled sensitivities); anchor differences the two
regimes each year. Dynamic layer drops out-of-scope rows; marginal buncher solved
under formulation A with the unregistered choice capped at the threshold. Sector
targets allocate HMRC's 'Unknown' turnover band proportionally. The dominated-
region artifact labels its in-scope E separately from the Section 6 frame E.

Regenerated: synthetic populations (not committed), calibration report, static
sweep and anchor, bunching inference and figures, notch, dynamic results and
figures, reform menu, dominated-region masses, verification reports, recovery
test, seed sensitivity; figures synced to paper/figures.

Paper: data section rewritten around the two universes (Table 3/8 enterprise
units, registration propensity, scope and registration flags, calibration table
in Appendix A); static section restated with the registration rule, ageing
convention, deregistration threshold, anchor sensitivities, frame-confined cut
rows, and the constant-50% taper alongside the linear design; dominated-region
section gives the U(m) band-top frontier; behavioural section reports n_H(e, delta);
bunching section adds the 2024-25 coarse-band readback; intro/conclusion/summary
table renumbered; literature on size-based thresholds and firm microsimulation
added. Paper workflow now uploads the rendered PDF as an artifact.

Closes #37. Refs #32, #38, #39, #40, #41.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
vahid-ahmadi and others added 6 commits September 4, 2026 10:02
…al run, resync static figures

The displayed R(T) now matches StaticVATModel._registered (voluntary
registrants unchanged only where never required; data-year registrants
gap-protected). The anchor prose and Figure 4 caption describe the headline
that overshoots HMRC in every release year rather than the superseded
fixed-preference run. Static figures re-synced after the last rule change;
README anchor bullet and seed-dispersion numbers updated; 2024-25 y_R marked
censored; Figure 3 caption says in-scope counts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… scope artefact

Review of PR #46 found that from 2025-26 on no data-year mandatory registrant
is released by the £85k->£90k anchor (all have aged past the £88k
deregistration point); 84% of released liability belongs to data-year voluntary
registrants aged across £85k. The paper now says so, presents the
fixed-preference convention as the co-equal lower bound, and the static dump
prints the decomposition. The 64% in-scope share above £10m is described as a
uniform-draw artefact of the open ONS 5000+ band, not a PAYE/exempt share.
Sweep caption and README describe the voluntary rule as implemented; README row
count corrected; two unreferenced figures referenced; CUDA device bug in the
weighted selector fixed; propensity test pins per-band values; ageing test
covers a voluntary registrant crossing T0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix ONS employment source unit and calibration universes

1 participant