Repository navigation
Conversation
expected_ltt and expected_lbtt multiplied land_transaction_tax and land_and_buildings_transaction_tax by property_sale_rate (0.045). Those liabilities already charge only the year's purchases (on FRS-based data, the seeded annual purchaser draw) and rent increments, so the sale rate discounted them a second time. Follow #2235's SDLT convention: the purchase draw is the expectation, and each expected value equals its liability. property_sale_rate now has no reader, so remove it and the leftover test overrides of it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
YAML cases hand-compute LBTT (SSI 2015/126 Tables A and B, first-time buyer relief) and LTT (WSI 2018/128 Tables 1 to 3) on purchases in Scotland and Wales and check expected = liability = the household's tax totals. The Hypothesis test checks expected equals realised and, independently, that zeroing every LTT and LBTT rate lowers each total by exactly the liabilities. A differential test compares the model with the statutory tables; the schedules it has stale (issues 1795, 1796) are strict expected failures. On the #2235 head (41e970b), 9 of the 17 YAML cases and the property test fail; here all pass. The partition's data notes now give the measured 1.58.0 figures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Fixes #2246
Summary
expected_lttandexpected_lbttmultiplied Land Transaction Tax (Wales) and Land and Buildings Transaction Tax (Scotland) by a 4.5% property sale rate. The liabilities already charge only the year's purchases, so this discounted them a second time. This PR follows the convention #2235 sets for SDLT: the purchase draw is the expectation, so each expected value equals its liability. It also removesproperty_sale_rate, which no longer has any reader.National effect on certified data, 2026-27:
gov_tax,household_tax,pre_budget_change_household_taxandgov_balancerise by £0.435bn if Stop charging property stocks as purchases when a household buys its home #2241 has landed. Without Stop charging property stocks as purchases when a household buys its home #2241 the rise is £2.273bn: £1.838bn of that is LBTT on property stocks that Stop charging property stocks as purchases when a household buys its home #2241 stops charging as purchases.All of this is measured by real runs, below. Stacked on #2235; land after #2235 and after #2241 (see "Merge order").
The bug
expected_ltt = property_sale_rate × land_transaction_taxandexpected_lbtt = property_sale_rate × land_and_buildings_transaction_tax, withgov.hmrc.stamp_duty.property_sale_rate= 0.045. That parameter was a 2021 placeholder ("TODO add proper calculations").main_residential_property_purchased = main_residence_value × property_purchased.property_purchasedat a seeded 3.85% (datasets/frs.py:1874-1878at tag 1.58.0:PROPERTY_PURCHASE_RATE = 0.0385,default_rng(0)).expected_lbttwas £0.104bn. LTT is £0.070bn, andexpected_lttwas £0.003bn.gov_tax,household_taxandpre_budget_change_household_taxat 4.5% of their value, and so did any LTT or LBTT reform.The fix
expected_lttaddsland_transaction_taxandexpected_lbttaddsland_and_buildings_transaction_tax, with no scaling. These are pureadds, the same conventionexpected_sdltfollows in Count corporate SDLT once and let SDLT abolition remove all of it #2235.gov.hmrc.stamp_duty.property_sale_rateis removed.old/instructural-reforms-demo, and policyengine-uk-chat's eval snapshot synced from policyengine-uk 2.90.2, which drops the case on its next resync.ValueError: Could not find the parameter gov.hmrc.stamp_duty.property_sale_rate(checked), where before it silently did nothing.property_sale_rate: 0overrides in the high value council tax surcharge tests are removed. Count corporate SDLT once and let SDLT abolition remove all of it #2235 removed the other five.fiscal_partition.yaml(from Count corporate SDLT once and let SDLT abolition remove all of it #2235):data_notes on headsland_transaction_taxandland_and_buildings_transaction_taxnow give the measured 1.58.0 figures instead of describing the double discount.Invariants
expected_lttis exactlyland_transaction_tax, andexpected_lbttis exactlyland_and_buildings_transaction_tax, for every household. So they also agree in weighted total.land_transaction_tax + land_and_buildings_transaction_tax, independently of 1. The old scaling gave 0.045 × that; counting a head twice would give 2 ×.#2235's partition invariants (heads = lists, traced overlap guard, coefficient one, identity) still hold.
Tests
tests/policy/baseline/gov/revenue_scotland/expected_lbtt.yaml(9 cases)expected_lbtt= liability =household_tax=gov_tax. Cases: £400k, £1m, band tops £145k and £250k, a first-time buyer at £200k, non-residential £300k, plus zero outside Scotland.tests/policy/baseline/gov/wra/land_transaction_tax/expected_ltt.yaml(8 cases)tests/test_ltt_lbtt_expected_properties.pyRate bands cited (read on legislation.gov.uk 2026-10-11):
utils/parameters.py:113).Cross-check against Axiom. The test's statutory tables reproduce every value in rulespec-uk's companion tests, computed:
uk/policies/govuk/lbtt.test.yaml: £200k → 1,100; £300k → 4,600; £500k → 23,350; £800k → 54,350; additional £300k → 4,600 + 24,000.uk/policies/govuk/ltt.test.yaml, current main rates: £300k → 4,500; £500k → 18,000; £800k → 41,750; £2m → 171,750. Higher rates: £300k → 19,950.Red on base, green here. Run against #2235's head (41e970b):
expected_lbtt6,373.53 vs liability 141,633.98.On this branch all pass.
Local runs (Python 3.13, policyengine-core 3.33.0):
expected_lbtt,expected_ltt, the LTT/LBTT liabilities,gov_taxorhousehold_tax: 60 passed.test_ltt_lbtt_expected_properties.py: 8 passed, 3 xfailed.test_fiscal_partition.pyandtest_sdlt_counted_once_properties.py: 40 passed.CI. The PR workflow runs only on PRs into
main, so for this stacked PR I dispatched it on the branch withworkflow_dispatch. It runs again when the PR is retargeted tomain.National impact (real runs)
Setup.
enhanced_frs_2024_25.h5, sha2565ba399ea5ab9a6c7d4e9aaf6c623080beb34d9ad5f330f6244dbad72b8d960b6, the certified release.Microsimulation, with no scaling or interpolation.Current law, £bn: on #2235 alone
expected_ltt, base → headexpected_lbtt, base → headgov_tax,household_tax,pre_budget_change_household_tax,gov_balancehousehold_net_incomeCurrent law, £bn: with #2241 merged into both
expected_ltt, base → headexpected_lbtt, base → headgov_tax,household_tax,pre_budget_change_household_tax,gov_balancehousehold_net_incomeIn every run,
land_transaction_tax,land_and_buildings_transaction_tax,expected_sdltandhbai_household_net_incomeare unchanged by this PR.Where the LBTT comes from, 2026-27.
Reform scoring. An illustrative reform raises the top residential band by 1pp in both countries: LBTT over £750k from 12% to 13%, and LTT main rates over £1.5m from 12% to 13%. The change in
gov_taxfrom current law, £bn:Accounting checks (every year 2025-26 to 2030-31, current law):
expected_ltt−land_transaction_tax|expected_lbtt−land_and_buildings_transaction_tax|gov_tax− sum of its list| (float32)Distribution. Head − base in household net income, weighted mean £ per household, 2026-27:
The payers are few and the deciles are lumpy.
Poverty. BHC and AHC, absolute and relative, are unchanged to four decimal places in every year and every pair.
hbai_household_net_incomedoes not move.Evidence and scripts:
~/reviews/uk-ltt-lbtt-2246/(impact.py,compare.py,impact.md,runs/,shas.txt).Merge order
Land after #2235 (this PR's base) and after #2241.
Without #2241, this PR moves the £1.838bn a year of LBTT that #2241 removes from 4.5% into the totals at full value, until #2241 lands. That LBTT is on Scottish property stocks charged as purchases (#2238). Landing #2241 first means the totals only ever gain the £0.435bn on real main-residence purchases.
If #2241 lands first, the LBTT
data_notestays accurate: it gives both figures. When #2235 merges, GitHub retargets this PR tomainand CI runs on it.Not in this PR
lbtt/rent.yamlcharges 5% above £2m. SSI 2020/24 art. 2 made that band 2%. The model also applies the lease table to residential rent. On 1.58.0 no household has a rent charge, so the impact is zero there. Split into its own task.ltt_on_residential_property_transactionsandlbtt_on_residential_property_transactionsare computed for every UK purchaser: £22.4bn and £29.2bn nationally in 2026-27. Onlyland_transaction_taxandland_and_buildings_transaction_taxmultiply byltt_liable/lbtt_liable. The totals are right, and the partition lists the components as exclusions, but don't sum the components.Against official receipts
These figures were read from the official pages on 2026-10-11. The model figures are 2025-26 from the runs above; the outturn is 2024-25.
expected_lbtt/expected_lttbefore → after this PR (with #2241)Sources:
After this PR, the model's main-residence LBTT is about three quarters of Revenue Scotland's residential (non-ADS) receipts; before it, it was about 3.5%. Welsh LTT stays well below outturn, because the data has few Welsh purchasers. The remaining gaps are the purchase data: ADS, non-residential and lease transactions, and the purchaser count. They are not this PR's convention.
axiom: n/a: microsim-only. This changes the expected-value convention by which the model's tax totals count LTT and LBTT. The statutory liabilities are unchanged; rulespec-uk encodes them in
uk/policies/govuk/ltt.yamlandlbtt.yaml, and this PR's statutory tables reproduce their companion tests.🤖 Generated with Claude Code