From a53f69d8772da05b35f144683633985b596c7634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Juaristi?= <127882282+juaristi22@users.noreply.github.com> Date: Fri, 23 Oct 2026 01:44:05 +0100 Subject: [PATCH 1/8] Add input-driven gambling duty calculations Refs #2586 --- changelog.d/2586.added.md | 1 + docs/engineering/excise-duties.md | 5 + docs/engineering/gambling-duties.md | 230 +++++++++++ policyengine_uk/data/fiscal_partition.yaml | 34 +- .../gov/hmrc/gambling_duty/bingo/rate.yaml | 16 + .../gaming/normal_six_month_bands.yaml | 35 ++ .../rates/exchange_commission.yaml | 15 + .../rates/financial_spread.yaml | 15 + .../general_betting/rates/fixed_odds.yaml | 15 + .../general_betting/rates/other_spread.yaml | 15 + .../general_betting/rates/remote.yaml | 16 + .../gov/hmrc/gambling_duty/lottery/rate.yaml | 14 + .../machine_games/rates/higher.yaml | 15 + .../machine_games/rates/lower.yaml | 15 + .../machine_games/rates/standard.yaml | 15 + .../hmrc/gambling_duty/pool_betting/rate.yaml | 13 + .../gambling_duty/remote_gaming/rate.yaml | 16 + .../gambling/casino_effective_rate.yaml | 14 + policyengine_uk/programs.yaml | 13 + .../gov/hmrc/gambling_duty/bingo_duty.yaml | 26 ++ .../gov/hmrc/gambling_duty/gaming_duty.yaml | 29 ++ .../gambling_duty/general_betting_duty.yaml | 70 ++++ .../gov/hmrc/gambling_duty/integration.yaml | 364 ++++++++++++++++++ .../gov/hmrc/gambling_duty/lottery_duty.yaml | 19 + .../gambling_duty/machine_games_duty.yaml | 29 ++ .../hmrc/gambling_duty/pool_betting_duty.yaml | 17 + .../gambling_duty/remote_gaming_duty.yaml | 21 + .../test_gambling_additional_schedules.py | 70 ++++ .../tests/test_gambling_schedules.py | 91 +++++ policyengine_uk/variables/gov/gov_tax.py | 1 + .../gov/hmrc/gambling_duty/bingo_duty.py | 23 ++ .../gov/hmrc/gambling_duty/gambling_duty.py | 18 + .../gov/hmrc/gambling_duty/gaming_duty.py | 17 + .../gambling_duty/general_betting_duty.py | 41 ++ .../gov/hmrc/gambling_duty/lottery_duty.py | 14 + .../hmrc/gambling_duty/machine_games_duty.py | 24 ++ .../hmrc/gambling_duty/pool_betting_duty.py | 20 + .../hmrc/gambling_duty/remote_gaming_duty.py | 23 ++ .../variables/gov/hmrc/household_tax.py | 1 + .../gambling/betting_exchange_commission.py | 11 + .../gambling/bingo_net_receipts.py | 11 + .../financial_spread_betting_net_losses.py | 11 + .../horse_and_dog_pool_betting_net_losses.py | 11 + .../gambling/land_based_casino_net_losses.py | 11 + ...and_based_fixed_odds_betting_net_losses.py | 11 + .../machine_games_higher_rate_net_takings.py | 11 + .../machine_games_lower_rate_net_takings.py | 11 + ...machine_games_standard_rate_net_takings.py | 11 + .../national_lottery_ticket_spending.py | 11 + .../other_spread_betting_net_losses.py | 11 + .../gambling/pool_betting_net_receipts.py | 11 + .../gambling/remote_gaming_net_losses.py | 11 + .../remote_other_betting_net_losses.py | 11 + ...emote_uk_horseracing_betting_net_losses.py | 11 + 54 files changed, 1594 insertions(+), 1 deletion(-) create mode 100644 changelog.d/2586.added.md create mode 100644 docs/engineering/gambling-duties.md create mode 100644 policyengine_uk/parameters/gov/hmrc/gambling_duty/bingo/rate.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/gambling_duty/gaming/normal_six_month_bands.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/exchange_commission.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/financial_spread.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/fixed_odds.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/other_spread.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/remote.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/gambling_duty/lottery/rate.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/gambling_duty/machine_games/rates/higher.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/gambling_duty/machine_games/rates/lower.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/gambling_duty/machine_games/rates/standard.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/gambling_duty/pool_betting/rate.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/gambling_duty/remote_gaming/rate.yaml create mode 100644 policyengine_uk/parameters/household/consumption/gambling/casino_effective_rate.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/bingo_duty.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/gaming_duty.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/general_betting_duty.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/integration.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/lottery_duty.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/machine_games_duty.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/pool_betting_duty.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/remote_gaming_duty.yaml create mode 100644 policyengine_uk/tests/test_gambling_additional_schedules.py create mode 100644 policyengine_uk/tests/test_gambling_schedules.py create mode 100644 policyengine_uk/variables/gov/hmrc/gambling_duty/bingo_duty.py create mode 100644 policyengine_uk/variables/gov/hmrc/gambling_duty/gambling_duty.py create mode 100644 policyengine_uk/variables/gov/hmrc/gambling_duty/gaming_duty.py create mode 100644 policyengine_uk/variables/gov/hmrc/gambling_duty/general_betting_duty.py create mode 100644 policyengine_uk/variables/gov/hmrc/gambling_duty/lottery_duty.py create mode 100644 policyengine_uk/variables/gov/hmrc/gambling_duty/machine_games_duty.py create mode 100644 policyengine_uk/variables/gov/hmrc/gambling_duty/pool_betting_duty.py create mode 100644 policyengine_uk/variables/gov/hmrc/gambling_duty/remote_gaming_duty.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/betting_exchange_commission.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/bingo_net_receipts.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/financial_spread_betting_net_losses.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/horse_and_dog_pool_betting_net_losses.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/land_based_casino_net_losses.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/land_based_fixed_odds_betting_net_losses.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/machine_games_higher_rate_net_takings.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/machine_games_lower_rate_net_takings.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/machine_games_standard_rate_net_takings.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/national_lottery_ticket_spending.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/other_spread_betting_net_losses.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/pool_betting_net_receipts.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/remote_gaming_net_losses.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/remote_other_betting_net_losses.py create mode 100644 policyengine_uk/variables/input/consumption/gambling/remote_uk_horseracing_betting_net_losses.py diff --git a/changelog.d/2586.added.md b/changelog.d/2586.added.md new file mode 100644 index 0000000000..65e7cb0796 --- /dev/null +++ b/changelog.d/2586.added.md @@ -0,0 +1 @@ +- Added seven input-driven gambling duty families from 2024/25 to household and government taxes, including a documented casino incidence proxy. diff --git a/docs/engineering/excise-duties.md b/docs/engineering/excise-duties.md index 2151b427ae..2e353c04a9 100644 --- a/docs/engineering/excise-duties.md +++ b/docs/engineering/excise-duties.md @@ -6,6 +6,11 @@ and duty on LPG and natural road fuel gas from supplied household inputs. diesel duty. These totals combine the different annual bases described below. The incidence assumption is full pass-through to consumers. +The model also includes seven gambling duty families from supplied attributed +household bases. [Household gambling duties](gambling-duties.md) documents the +rates, casino incidence proxy, period conventions and outstanding population +data, machine-premises and VAT work. + ## Coverage and periods Alcohol, tobacco and car duty calculations cover 2024/25 to 2026/27. The alcohol diff --git a/docs/engineering/gambling-duties.md b/docs/engineering/gambling-duties.md new file mode 100644 index 0000000000..3c93cff0a2 --- /dev/null +++ b/docs/engineering/gambling-duties.md @@ -0,0 +1,230 @@ +# Household gambling duties + +The model calculates General Betting Duty, Pool Betting Duty, Remote Gaming +Duty, Machine Games Duty, casino Gaming Duty incidence, Bingo Duty and Lottery +Duty from supplied annual household bases. The `gambling_duty` output adds +these seven components and enters `household_tax` and `gov_tax` once. The +incidence assumption passes all duty to consumers. These inputs support +household calculations; they do not establish population revenue estimates or +repair existing survey data. + +## Periods and attribution + +Coverage starts in 2024/25. Model period `2024` covers 6 April 2024 to 5 April +2025. The formulas apply dated rates within each interval of that fiscal year +and weight each interval by its share of the year, using +`utils.excise.fiscal_year_segments`. They assume each supplied annual base +accrues uniformly. For example, Finance Act 2026 raises Remote Gaming Duty on +1 April 2026: FY2025 contains 360 days at 21% and five at 40%. The remote +betting change on 1 April 2027 similarly affects five days of FY2026. + +Each input supplies a nonnegative, already-attributed dutiable base in pounds. +All inputs default to zero. A data provider must allocate operator liabilities +to households, preserving statutory netting, adjustments and exemptions before +supplying these bases. A household's raw signed stakes minus winnings do not +satisfy this contract. The formulas do not reproduce operator accounting +periods or loss carry-forward and do not validate negative input values. +Positive inputs before 2024 produce default zero because those model years +remain outside the implemented coverage. + +The full pass-through assumption follows the issue's household-incidence +proposal. The OBR's November 2025 forecast assumed around 90% pass-through of +the Budget 2025 increases; that behavioural assumption about changes is not a +baseline incidence fraction in this implementation. This implementation does +not reproduce the OBR's demand or substitution costing. Government receipts +and household incidence use the same attributed base here. + +Source: [OBR November 2025 Economic and Fiscal Outlook, paragraph 3.40](https://obr.uk/docs/dlm_uploads/OBR_Economic_and_fiscal_outlook_November_2025.pdf#page=85). + +## General Betting Duty + +Supply these separate, non-overlapping inputs: + +| Annual household input | Dutiable base | +| --- | --- | +| `remote_uk_horseracing_betting_net_losses` | Remote fixed-odds bets wholly related to horse racing in the United Kingdom. | +| `remote_other_betting_net_losses` | Other qualifying remote fixed-odds bets, excluding all pool and spread bets, exchange commission, on-course bets and qualifying licensed-premises self-service betting terminals. | +| `land_based_fixed_odds_betting_net_losses` | Dutiable off-course land-based fixed-odds bets, including qualifying self-service betting terminals in licensed premises. Exclude on-course bets, pools, spread bets and exchange commission. | +| `horse_and_dog_pool_betting_net_losses` | Dutiable pool or totalisator bets wholly related to horse or dog racing. | +| `financial_spread_betting_net_losses` | Dutiable financial spread betting net receipts of UK bookmakers. | +| `other_spread_betting_net_losses` | Dutiable nonfinancial spread betting net receipts of UK bookmakers. | +| `betting_exchange_commission` | Chargeable exchange commission charged to UK persons. Exchange stakes must not enter another input. | + +The rate is 15% for fixed odds, racing pools and exchange commission, 3% for +financial spreads and 10% for other spreads. Qualifying other remote +fixed-odds bets use 25% from 1 April 2027. Bets wholly on UK horse racing, +qualifying licensed-premises self-service terminals and pool bets retain their +existing treatment. On-course bets are exempt and must not enter a dutiable +input. A terminal used to bet on future real events differs from a gaming +machine subject to Machine Games Duty. + +Sources: [Finance Act 2014, Chapter 1](https://www.legislation.gov.uk/ukpga/2014/26/part/3/chapter/1), +[Finance Act 2026, section 87](https://www.legislation.gov.uk/ukpga/2026/11/section/87) +and [HMRC gambling duty guidance](https://www.gov.uk/guidance/general-betting-duty-pool-betting-duty-and-remote-gaming-duty). + +## Pool Betting Duty + +Supply `pool_betting_net_receipts`, the household's attributed dutiable pool +betting base, including statutory additions and excluding qualifying +community-benefit bets. The rate is 15%. Exclude fixed-odds bets and pools +wholly related to horse or dog racing; those racing pools enter General Betting +Duty. A mixed pool does not become exempt from Pool Betting Duty merely +because one leg concerns horse or dog racing. + +Source: [Finance Act 2014, Chapter 2](https://www.legislation.gov.uk/ukpga/2014/26/part/3/chapter/2). + +## Remote Gaming Duty + +Supply `remote_gaming_net_losses`, the household's attributed chargeable, +non-exempt remote gaming profits base. The rate is 21% in the starting +coverage, rising to 40% on 1 April 2026. The input excludes gaming already +charged with another gambling tax and other exemptions in section 161. Remote +bingo enters this base only where chargeable: section 161 also exempts UK +licensed bingo and specified unlicensed bingo with mixed remote and in-person +participation. Do not allocate the same gaming to another duty base. + +Sources: [Finance Act 2014, sections 154 to 162](https://www.legislation.gov.uk/ukpga/2014/26/part/3/chapter/3), +[Finance Act 2026, section 86](https://www.legislation.gov.uk/ukpga/2026/11/section/86) +and [HMRC historical rates](https://www.gov.uk/government/statistics/uk-betting-and-gaming-statistics/historical-uk-betting-and-gaming-duty-rates). + +## Machine Games Duty + +Supply attributed dutiable net takings separately: + +| Input | Rate | Statutory machine class | +| --- | --- | --- | +| `machine_games_lower_rate_net_takings` | 5% | Every dutiable game costs at most 20p and has a maximum cash prize of at most £10. | +| `machine_games_standard_rate_net_takings` | 20% | The machine does not qualify for the lower rate, and every dutiable game costs at most £5. | +| `machine_games_higher_rate_net_takings` | 25% | Other dutiable machines. | + +Classify the entire machine using all dutiable games it offers. Exactly £5 per +play remains within the standard-rate limit. A gambling category, one selected +game or a premises type does not identify the tax class. The model introduces +no average rate, machine-count weighting or premises allocation. A reform +targeting betting shops needs a separate evidenced allocation of each tax +class's net takings by premises. + +Sources: [Finance Act 2012, Schedule 24](https://www.legislation.gov.uk/ukpga/2012/14/schedule/24) +and [HMRC Notice 452](https://www.gov.uk/guidance/machine-games-duty-excise-notice-452). + +## Bingo Duty + +Supply `bingo_net_receipts`, the household's nonnegative share of dutiable +bingo promotion profits after statutory adjustments and exemptions. Bingo Duty +charged 10% before abolition on 1 April 2026. FY2025 therefore charges +£3,600 on a uniform £36,500 attributed base: £100 per day × 360 days × 10%. +FY2026 charges zero. The operator's exemption where duty would be less than +£1 does not create a household threshold. The supplied base must already +reflect the operator's treatment. + +Exclude bingo exempt under the pre-repeal law, including wholly remote +unlicensed bingo under section 17(2A). Chargeable remote gaming uses the +separate Remote Gaming Duty contract, including its section 161 exemptions. + +Sources: [Betting and Gaming Duties Act 1981, section 17, as at 6 April 2024](https://www.legislation.gov.uk/ukpga/1981/63/section/17/2024-04-06) +and [Finance Act 2026, section 88](https://www.legislation.gov.uk/ukpga/2026/11/section/88). + +## Lottery Duty + +Supply `national_lottery_ticket_spending`, the chargeable consideration for +National Lottery tickets and scratchcards. The rate is 12%, with no deduction +for winnings or expenses. Section 25 includes amounts paid, given or debited +in connection with the ticket. Where consideration falls below a shown ticket +price, or no consideration is given, use the deemed shown price. Exempt +lotteries and bingo are outside this input. The model's initial parameter +entry marks verified 2024/25 coverage and makes no earlier commencement claim. + +Sources: [Finance Act 1993, section 24](https://www.legislation.gov.uk/ukpga/1993/34/section/24/2024-04-06), +[section 25](https://www.legislation.gov.uk/ukpga/1993/34/section/25/2024-04-06) +and [HMRC Notice 458](https://www.gov.uk/guidance/lottery-duty-excise-notice-458). + +## Casino Gaming Duty incidence + +Supply `land_based_casino_net_losses`, the household's nonnegative attributed +dutiable casino gross gaming yield. The attribution includes applicable gaming +charges, such as participation charges for equal-chance games, alongside +stakes less prizes where the operator acts as banker. Exclude admission-only +charges, machine games and gaming taxed or exempt under the other duties. + +The household formula multiplies this base by the empirical assumption +`household.consumption.gambling.casino_effective_rate`, initially **0.235487** +(23.5487%). It holds that fraction constant from model year 2024 onwards. +The approved proxy divides these official observations: + +| Observation, April 2024 to March 2025 | Amount | Source location | +| --- | ---: | --- | +| HMRC UK Gaming Duty cash receipts | £165.397 million | July 2026 Betting and Gaming tables, `Table_1_BG_receipts!E45`, reported in £000. | +| Gambling Commission GB casino **game** gross gaming yield | £702.36095883367 million | 2024/25 industry statistics, `8a!B94`, also `8!H49`; provisional. | + +The exact division gives 0.2354871778104747736771859341. Rounding to six +fractional decimals changes the arithmetic on that denominator by £124.89, +below HMRC's £1,000 source reporting unit. Casino **machine** yield of +£231.47255869591993 million is excluded; machines use Machine Games Duty. +The original HMRC ODS SHA-256 is +`3979df37c0611cce371f80950bacac6bc5e9dad499c726f6138f838c882914e8`; +the original Gambling Commission workbook SHA-256 is +`ea5335233cc728f9c500d4ed3233bd996b60acb2cff11f271e0fcf002c8e1b6b`. + +The observations have different coverage. HMRC reports UK cash receipts with +payment and accounting lags; the Gambling Commission reports GB licensed +operator yield and marks the 2024/25 row provisional. Both source periods run +1 April to 31 March, while the model uses 6 April to 5 April. The division +therefore supplies a documented incidence approximation, without establishing +matched geography, taxpayers or accrual periods. Its numerical precision does +not resolve those differences. [Issue 2587](https://github.com/PolicyEngine/policyengine-uk/issues/2587) +tracks review and refresh of the proxy. + +Statutory Gaming Duty applies progressive bands to casino premises for an +operator accounting period. The separate +`gov.hmrc.gambling_duty.gaming.normal_six_month_bands` catalogue records the +registered-premises, normal six-month schedule: 15% on the first £2,686,000, +20% on the next £1,852,000, 30% on the next £3,243,000, 40% on the next +£6,845,000 and 50% thereafter. It excludes unregistered-gaming treatment and +alternative accounting-period adjustments. Household annual losses never +enter those bands. + +Changing the operator catalogue alone leaves the household proxy unchanged. +Scoring a casino band reform requires operator yield distributions and an +incidence mapping, or an explicitly supplied alternative effective fraction. +The fixed proxy does not model behaviour, predict later effective rates or +certify national receipts. + +Sources: [Finance Act 1997, sections 10 and 11](https://www.legislation.gov.uk/ukpga/1997/16/section/11/2024-04-06), +[HMRC Notice 453](https://www.gov.uk/guidance/gaming-duty-excise-notice-453), +[HMRC July 2026 tables](https://assets.publishing.service.gov.uk/media/6ab54fd9fe72ed1e2b02f06b/2026_Jul_Bet_and_Gam_Tab.ods) +and [Gambling Commission 2024/25 industry statistics](https://www.gamblingcommission.gov.uk/statistics-and-research/publication/industry-statistics-annual-report-financial-year-april-2024-to-march-2025). + +## Tax totals and fiscal classification + +The seven component duties enter `gambling_duty`, which enters household and +government taxes once. The fiscal partition assigns that head to **Other HMRC +taxes**. OBR's November 2025 Economic and Fiscal Outlook, Table A.5 footnote 5, +places betting and gaming duties in that row. The model measures only the +gambling part from supplied bases; residual Other HMRC taxes remain +unmodelled. No Table 3.1 allocation is asserted. Removing the row from the +partition's list of rows with no household measure does not claim complete +coverage of the row. + +Source: [OBR November 2025 Economic and Fiscal Outlook, Table A.5](https://obr.uk/docs/dlm_uploads/OBR_Economic_and_fiscal_outlook_November_2025.pdf#page=195). + +## Data and coverage follow-ups + +Released data need a separate mapping from gambling stakes and winnings to +these dutiable bases. The mapping must distinguish remote and land-based +products, avoid overlap, address survey under-recording and reconcile weighted +totals with HMRC receipts using compatible periods and population coverage. +The formulas make no allocation from `recreation_consumption`. Default-zero +inputs do not demonstrate upstream data adoption or population coverage. + +The three machine tax-class bases cannot score a betting-shop-only reform +without joint tax-class-by-premises yield data. Premises shares alone do not +identify that allocation, and machine counts do not provide yield weights. +No assumption of independence between premises and tax class enters the +model. [Issue 2586](https://github.com/PolicyEngine/policyengine-uk/issues/2586) +remains open for that contract and data work, alongside Microcosm 1359 and +Chronos 381. The separate casino review remains under issue 2587. + +Gambling removal from the VAT base also requires a comparable expenditure +contract: subtracting net gambling losses from a gross-stakes consumption +aggregate would mix bases. Data adoption, calibrated population receipts and +VAT-base repair remain separate from this seven-family engine foundation. diff --git a/policyengine_uk/data/fiscal_partition.yaml b/policyengine_uk/data/fiscal_partition.yaml index 3ae9f9fef0..c5f169450f 100644 --- a/policyengine_uk/data/fiscal_partition.yaml +++ b/policyengine_uk/data/fiscal_partition.yaml @@ -100,6 +100,12 @@ sources: the employer NI response incidence and the two unallocated tax changes) as exclusions from a total of levels; here they are heads of the totals that use them, zero at current law. + gambling_duties: >- + OBR, Economic and fiscal outlook, November 2025, Table A.5, footnote 5, + includes betting and gaming duties within Other HMRC taxes. This head + measures only supplied household gambling bases; the other components + of that receipts row remain unmodelled. No Table 3.1 allocation is + claimed. https://obr.uk/docs/dlm_uploads/OBR_Economic_and_fiscal_outlook_November_2025.pdf#page=195 totals: gov_tax: @@ -338,6 +344,26 @@ tax_heads: Zero for every household on policyengine-uk-data 1.58.0 (enhanced_frs_2024_25) in every year from 2025 to 2030, measured 2026-10-11. Issue 1874. + - id: gambling_duty + label: Betting and gaming duties + variable: gambling_duty + entity: household + obr_line: Other HMRC taxes + obr_line_note: >- + Betting and gaming duties belong to this row under November 2025 Table + A.5 footnote 5. This head provides partial row coverage only; other + Other HMRC taxes remain unmodelled. + obr_table_3_1: null + report_group: excise_duties + payer: household + totals: [gov_tax, household_tax] + sign: nonnegative + data_note: >- + Calculated from supplied nonnegative attributed dutiable bases only. + Missing inputs default to zero; this does not establish population + data adoption or calibrated receipts. Casino incidence uses a fixed + UK cash receipts/GB game yield proxy reviewed under issue 2587. + This head does not measure the residual Other HMRC taxes components. - id: tv_licence label: TV licence fee variable: tv_licence @@ -604,6 +630,13 @@ exclusions: - {variable: wine_duty, kind: component, head: alcohol_duty} - {variable: spirits_duty, kind: component, head: alcohol_duty} - {variable: other_fermented_duty, kind: component, head: alcohol_duty} + - {variable: general_betting_duty, kind: component, head: gambling_duty} + - {variable: pool_betting_duty, kind: component, head: gambling_duty} + - {variable: remote_gaming_duty, kind: component, head: gambling_duty} + - {variable: machine_games_duty, kind: component, head: gambling_duty} + - {variable: gaming_duty, kind: component, head: gambling_duty} + - {variable: bingo_duty, kind: component, head: gambling_duty} + - {variable: lottery_duty, kind: component, head: gambling_duty} - {variable: car_vehicle_excise_duty, kind: component, head: vehicle_excise_duty} # Council tax net of a benefit - {variable: council_tax_less_benefit, kind: composite, spans: [council_tax], note: "Council tax net of Council Tax Reduction (the OBR's basis). The totals count gross council tax and credit the reduction as spending, so adding this would count council tax twice."} @@ -695,7 +728,6 @@ uncovered_receipts: - {obr_line: Bank surcharge} - {obr_line: Apprenticeship levy} - {obr_line: Digital services tax} - - {obr_line: Other HMRC taxes} - {obr_line: Environmental levies} - {obr_line: Emissions Trading Scheme} - {obr_line: Energy profits levy} diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/bingo/rate.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/bingo/rate.yaml new file mode 100644 index 0000000000..2fd52c9e06 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/bingo/rate.yaml @@ -0,0 +1,16 @@ +description: The UK levies Bingo Duty at this rate on dutiable bingo promotion profits, until its abolition on 1 April 2026. +values: + '2014-07-01': 0.1 + '2026-04-01': 0 +metadata: + unit: /1 + period: year + label: bingo duty rate + preserve_calendar_dates: true + reference: + - title: Betting and Gaming Duties Act 1981, section 17, as at 6 April 2024 + href: https://www.legislation.gov.uk/ukpga/1981/63/section/17/2024-04-06 + - title: Finance Act 2026, section 88 + href: https://www.legislation.gov.uk/ukpga/2026/11/section/88 + - title: HMRC historical gambling duty rates + href: https://www.gov.uk/government/statistics/uk-betting-and-gaming-statistics/historical-uk-betting-and-gaming-duty-rates diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/gaming/normal_six_month_bands.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/gaming/normal_six_month_bands.yaml new file mode 100644 index 0000000000..86b12183bd --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/gaming/normal_six_month_bands.yaml @@ -0,0 +1,35 @@ +description: The UK applies these marginal Gaming Duty rates to gross gaming yield from registered casino premises in a normal six-month accounting period. This operator catalogue excludes unregistered gaming and alternative-period adjustments; household incidence uses a separate empirical assumption. +brackets: +- threshold: + '2022-04-01': 0 + rate: + '2022-04-01': 0.15 +- threshold: + '2022-04-01': 2_686_000 + rate: + '2022-04-01': 0.2 +- threshold: + '2022-04-01': 4_538_000 + rate: + '2022-04-01': 0.3 +- threshold: + '2022-04-01': 7_781_000 + rate: + '2022-04-01': 0.4 +- threshold: + '2022-04-01': 14_626_000 + rate: + '2022-04-01': 0.5 +metadata: + type: marginal_rate + unit: /1 + threshold_unit: currency-GBP + period: year + label: normal six-month casino gaming duty bands + reference: + - title: Finance Act 1997, section 11(2), as at 6 April 2024 + href: https://www.legislation.gov.uk/ukpga/1997/16/section/11/2024-04-06 + - title: HMRC Gaming Duty Notice 453 + href: https://www.gov.uk/guidance/gaming-duty-excise-notice-453 + - title: HMRC gambling duty rates + href: https://www.gov.uk/government/publications/rates-and-allowance-excise-duty-gambling-duty/excise-duty-gambling-duty-rates diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/exchange_commission.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/exchange_commission.yaml new file mode 100644 index 0000000000..35d721dcf6 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/exchange_commission.yaml @@ -0,0 +1,15 @@ +description: The UK levies General Betting Duty at this rate on chargeable betting exchange commission. +values: + '2014-12-01': 0.15 +metadata: + unit: /1 + period: year + label: general betting duty exchange commission rate + reference: + - title: Finance Act 2014, section 140 + href: https://www.legislation.gov.uk/ukpga/2014/26/section/140 + - title: HMRC gambling duty rates + href: https://www.gov.uk/government/publications/rates-and-allowance-excise-duty-gambling-duty/excise-duty-gambling-duty-rates + - title: HMRC General Betting Duty guidance + href: https://www.gov.uk/guidance/general-betting-duty-pool-betting-duty-and-remote-gaming-duty + preserve_calendar_dates: true diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/financial_spread.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/financial_spread.yaml new file mode 100644 index 0000000000..9bfdd79982 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/financial_spread.yaml @@ -0,0 +1,15 @@ +description: The UK levies General Betting Duty at this rate on financial spread betting. +values: + '2014-12-01': 0.03 +metadata: + unit: /1 + period: year + label: general betting duty financial spread rate + reference: + - title: Finance Act 2014, section 128 + href: https://www.legislation.gov.uk/ukpga/2014/26/section/128 + - title: HMRC gambling duty rates + href: https://www.gov.uk/government/publications/rates-and-allowance-excise-duty-gambling-duty/excise-duty-gambling-duty-rates + - title: HMRC General Betting Duty guidance + href: https://www.gov.uk/guidance/general-betting-duty-pool-betting-duty-and-remote-gaming-duty + preserve_calendar_dates: true diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/fixed_odds.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/fixed_odds.yaml new file mode 100644 index 0000000000..a4b1b72720 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/fixed_odds.yaml @@ -0,0 +1,15 @@ +description: The UK levies General Betting Duty at this rate on fixed-odds and wholly horse or dog racing pool betting. +values: + '2014-12-01': 0.15 +metadata: + unit: /1 + period: year + label: general betting duty fixed odds rate + reference: + - title: Finance Act 2014, section 127 + href: https://www.legislation.gov.uk/ukpga/2014/26/section/127 + - title: HMRC gambling duty rates + href: https://www.gov.uk/government/publications/rates-and-allowance-excise-duty-gambling-duty/excise-duty-gambling-duty-rates + - title: HMRC General Betting Duty guidance + href: https://www.gov.uk/guidance/general-betting-duty-pool-betting-duty-and-remote-gaming-duty + preserve_calendar_dates: true diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/other_spread.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/other_spread.yaml new file mode 100644 index 0000000000..a0eeec4be4 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/other_spread.yaml @@ -0,0 +1,15 @@ +description: The UK levies General Betting Duty at this rate on other spread betting. +values: + '2014-12-01': 0.1 +metadata: + unit: /1 + period: year + label: general betting duty other spread rate + reference: + - title: Finance Act 2014, section 129 + href: https://www.legislation.gov.uk/ukpga/2014/26/section/129 + - title: HMRC gambling duty rates + href: https://www.gov.uk/government/publications/rates-and-allowance-excise-duty-gambling-duty/excise-duty-gambling-duty-rates + - title: HMRC General Betting Duty guidance + href: https://www.gov.uk/guidance/general-betting-duty-pool-betting-duty-and-remote-gaming-duty + preserve_calendar_dates: true diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/remote.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/remote.yaml new file mode 100644 index 0000000000..e2039ab95e --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/remote.yaml @@ -0,0 +1,16 @@ +description: The UK levies General Betting Duty at this rate on qualifying remote fixed-odds bets other than bets wholly related to UK horse racing. The new remote rate excludes pool, spread and on-course bets and qualifying licensed-premises self-service betting terminals. +values: + '2014-12-01': 0.15 + '2027-04-01': 0.25 +metadata: + unit: /1 + period: year + label: general betting duty qualifying remote rate + reference: + - title: Finance Act 2014, section 127 + href: https://www.legislation.gov.uk/ukpga/2014/26/section/127 + - title: Finance Act 2026, section 87 + href: https://www.legislation.gov.uk/ukpga/2026/11/section/87 + - title: HMRC gambling duty rates + href: https://www.gov.uk/government/publications/rates-and-allowance-excise-duty-gambling-duty/excise-duty-gambling-duty-rates + preserve_calendar_dates: true diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/lottery/rate.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/lottery/rate.yaml new file mode 100644 index 0000000000..3a0caad32f --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/lottery/rate.yaml @@ -0,0 +1,14 @@ +description: The UK levies Lottery Duty at this rate on chargeable ticket consideration, including deemed ticket price. The initial entry marks this model's 2024/25 coverage, rather than legislative commencement. +values: + '2024-04-06': 0.12 +metadata: + unit: /1 + period: year + label: lottery duty rate + reference: + - title: Finance Act 1993, section 25, as at 6 April 2024 + href: https://www.legislation.gov.uk/ukpga/1993/34/section/25/2024-04-06 + - title: Finance Act 1993, section 24, as at 6 April 2024 + href: https://www.legislation.gov.uk/ukpga/1993/34/section/24/2024-04-06 + - title: HMRC Lottery Duty Notice 458 + href: https://www.gov.uk/guidance/lottery-duty-excise-notice-458 diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/machine_games/rates/higher.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/machine_games/rates/higher.yaml new file mode 100644 index 0000000000..dab76291c3 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/machine_games/rates/higher.yaml @@ -0,0 +1,15 @@ +description: The UK levies Machine Games Duty at this rate on dutiable net takings from machines outside the lower- and standard-rate classes. +values: + '2015-03-01': 0.25 +metadata: + unit: /1 + period: year + label: machine games duty higher rate + reference: + - title: Finance Act 2012, Schedule 24, paragraph 9 + href: https://www.legislation.gov.uk/ukpga/2012/14/schedule/24/paragraph/9 + - title: HMRC Machine Games Duty Notice 452 + href: https://www.gov.uk/guidance/machine-games-duty-excise-notice-452 + - title: HMRC historical gambling duty rates + href: https://www.gov.uk/government/statistics/uk-betting-and-gaming-statistics/historical-uk-betting-and-gaming-duty-rates + preserve_calendar_dates: true diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/machine_games/rates/lower.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/machine_games/rates/lower.yaml new file mode 100644 index 0000000000..f20b5a0465 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/machine_games/rates/lower.yaml @@ -0,0 +1,15 @@ +description: The UK levies Machine Games Duty at this rate on dutiable net takings from machines on which every dutiable game costs at most 20p and has a maximum cash prize of at most GBP 10. +values: + '2013-02-01': 0.05 +metadata: + unit: /1 + period: year + label: machine games duty lower rate + reference: + - title: Finance Act 2012, Schedule 24, paragraph 9 + href: https://www.legislation.gov.uk/ukpga/2012/14/schedule/24/paragraph/9 + - title: HMRC Machine Games Duty Notice 452 + href: https://www.gov.uk/guidance/machine-games-duty-excise-notice-452 + - title: HMRC historical gambling duty rates + href: https://www.gov.uk/government/statistics/uk-betting-and-gaming-statistics/historical-uk-betting-and-gaming-duty-rates + preserve_calendar_dates: true diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/machine_games/rates/standard.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/machine_games/rates/standard.yaml new file mode 100644 index 0000000000..71d5b1d668 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/machine_games/rates/standard.yaml @@ -0,0 +1,15 @@ +description: The UK levies Machine Games Duty at this rate on dutiable net takings from machines outside the lower-rate class on which every dutiable game costs at most GBP 5. +values: + '2013-02-01': 0.2 +metadata: + unit: /1 + period: year + label: machine games duty standard rate + reference: + - title: Finance Act 2012, Schedule 24, paragraph 9 + href: https://www.legislation.gov.uk/ukpga/2012/14/schedule/24/paragraph/9 + - title: HMRC Machine Games Duty Notice 452 + href: https://www.gov.uk/guidance/machine-games-duty-excise-notice-452 + - title: HMRC historical gambling duty rates + href: https://www.gov.uk/government/statistics/uk-betting-and-gaming-statistics/historical-uk-betting-and-gaming-duty-rates + preserve_calendar_dates: true diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/pool_betting/rate.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/pool_betting/rate.yaml new file mode 100644 index 0000000000..d7ab82002c --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/pool_betting/rate.yaml @@ -0,0 +1,13 @@ +description: The UK levies Pool Betting Duty at this rate on chargeable pool betting profits, excluding pools wholly related to horse or dog racing and qualifying community-benefit bets. +values: + '2014-12-01': 0.15 +metadata: + unit: /1 + period: year + label: pool betting duty rate + reference: + - title: Finance Act 2014, sections 143 and 144 + href: https://www.legislation.gov.uk/ukpga/2014/26/section/144 + - title: HMRC General Betting Duty and Pool Betting Duty guidance + href: https://www.gov.uk/guidance/general-betting-duty-pool-betting-duty-and-remote-gaming-duty + preserve_calendar_dates: true diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/remote_gaming/rate.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/remote_gaming/rate.yaml new file mode 100644 index 0000000000..d59ed3e27c --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/remote_gaming/rate.yaml @@ -0,0 +1,16 @@ +description: The UK levies Remote Gaming Duty at this rate on chargeable non-exempt remote gaming profits. +values: + '2019-04-01': 0.21 + '2026-04-01': 0.4 +metadata: + unit: /1 + period: year + label: remote gaming duty rate + reference: + - title: Finance Act 2014, section 155 + href: https://www.legislation.gov.uk/ukpga/2014/26/section/155 + - title: Finance Act 2026, section 86 + href: https://www.legislation.gov.uk/ukpga/2026/11/section/86 + - title: HMRC historical gambling duty rates + href: https://www.gov.uk/government/statistics/uk-betting-and-gaming-statistics/historical-uk-betting-and-gaming-duty-rates + preserve_calendar_dates: true diff --git a/policyengine_uk/parameters/household/consumption/gambling/casino_effective_rate.yaml b/policyengine_uk/parameters/household/consumption/gambling/casino_effective_rate.yaml new file mode 100644 index 0000000000..f1668ba6cd --- /dev/null +++ b/policyengine_uk/parameters/household/consumption/gambling/casino_effective_rate.yaml @@ -0,0 +1,14 @@ +description: The model attributes household casino Gaming Duty at this empirical fraction of supplied dutiable casino yield. It divides HMRC UK cash Gaming Duty receipts of GBP 165.397 million by provisional Gambling Commission GB casino game yield of GBP 702.36095883367 million for April 2024 to March 2025, rounded to six fractional decimals and held constant after model year 2024. Geography, receipt timing and model-year differences remain unresolved; review and refresh are tracked at https://github.com/PolicyEngine/policyengine-uk/issues/2587. +values: + '2024-01-01': 0.235487 +metadata: + unit: /1 + period: year + label: casino gaming duty household incidence fraction + reference: + - title: HMRC July 2026 Betting and Gaming tables, Table_1_BG_receipts E45, GBP 000 + href: https://assets.publishing.service.gov.uk/media/6ab54fd9fe72ed1e2b02f06b/2026_Jul_Bet_and_Gam_Tab.ods + - title: Gambling Commission April 2024 to March 2025 industry statistics, casino game yield, sheet 8a B94 + href: https://www.gamblingcommission.gov.uk/statistics-and-research/publication/industry-statistics-annual-report-financial-year-april-2024-to-march-2025 + - title: Review and refresh the household casino-duty incidence proxy + href: https://github.com/PolicyEngine/policyengine-uk/issues/2587 diff --git a/policyengine_uk/programs.yaml b/policyengine_uk/programs.yaml index 11657e674c..307736433f 100644 --- a/policyengine_uk/programs.yaml +++ b/policyengine_uk/programs.yaml @@ -125,6 +125,19 @@ programs: verified_end_year: 2026 notes: One car per person with supplied vehicle characteristics. Excludes vans, motorcycles, eVED and population imputation. + - id: gambling_duty + name: Gambling duties + full_name: Betting and gaming duties + category: Taxes + agency: HMRC + status: partial + coverage: UK + variable: gambling_duty + parameter_prefix: gov.hmrc.gambling_duty + verified_start_year: 2024 + verified_end_year: 2027 + notes: Seven duty families on supplied attributed bases. Casino incidence uses a fixed FY2024/25 UK receipts/GB game yield proxy under household.consumption.gambling; review issue 2587. Excludes population imputation, verified machine-class-by-premises allocation and VAT-base repair. + - id: student_loans name: Student loan repayments full_name: Student Loan Repayments diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/bingo_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/bingo_duty.yaml new file mode 100644 index 0000000000..642e9cf5ae --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/bingo_duty.yaml @@ -0,0 +1,26 @@ +- name: Case 1, attributed dutiable bingo profits use ten percent in FY2024. + period: 2024 + input: + bingo_net_receipts: 1_000 + output: + bingo_duty: 100 # BGDA 1981 section 17(1)(b). + +- name: Case 2, bingo abolition affects the final five days of FY2025. + period: 2025 + input: + bingo_net_receipts: 36_500 + output: + bingo_duty: 3_600 # FA 2026 section 88: 100 * 360 * .10. + +- name: Case 3, bingo duty is abolished throughout FY2026. + period: 2026 + input: + bingo_net_receipts: 36_500 + output: + bingo_duty: 0 + +- name: Case 4, missing bingo input defaults to zero. + period: 2024 + input: {} + output: + bingo_duty: 0 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/gaming_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/gaming_duty.yaml new file mode 100644 index 0000000000..56ec7a5879 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/gaming_duty.yaml @@ -0,0 +1,29 @@ +- name: Case 1, household casino incidence uses the approved empirical fraction. + period: 2024 + absolute_error_margin: 0.01 + input: + land_based_casino_net_losses: 1_000 + output: + gaming_duty: 235.487 # 1,000 * .235487; HMRC/GC FY2024/25 ratio, issue 2587. + +- name: Case 2, household casino incidence scales linearly with the supplied base. + period: 2024 + absolute_error_margin: 0.01 + input: + land_based_casino_net_losses: 10_000 + output: + gaming_duty: 2_354.87 # Annual household bases never use operator brackets. + +- name: Case 3, the empirical casino fraction stays constant after its source year. + period: 2027 + absolute_error_margin: 0.01 + input: + land_based_casino_net_losses: 1_000 + output: + gaming_duty: 235.487 + +- name: Case 4, missing casino input defaults to zero. + period: 2024 + input: {} + output: + gaming_duty: 0 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/general_betting_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/general_betting_duty.yaml new file mode 100644 index 0000000000..dc1b083c49 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/general_betting_duty.yaml @@ -0,0 +1,70 @@ +- name: Case 1, UK horseracing remote fixed odds use the general rate. + period: 2024 + input: + remote_uk_horseracing_betting_net_losses: 1_000 + output: + general_betting_duty: 150 # FA 2014 section 127: 1,000 * 15%. + +- name: Case 2, other remote fixed odds use the general rate before April 2027. + period: 2024 + input: + remote_other_betting_net_losses: 1_000 + output: + general_betting_duty: 150 + +- name: Case 3, licensed-premises self-service terminals use the land-based rate. + period: 2027 + input: + land_based_fixed_odds_betting_net_losses: 1_000 + output: + general_betting_duty: 150 # Excluded from FA 2014 section 127A remote bets. + +- name: Case 4, pools wholly related to horse or dog racing enter general betting duty. + period: 2027 + input: + horse_and_dog_pool_betting_net_losses: 1_000 + output: + general_betting_duty: 150 + pool_betting_duty: 0 + +- name: Case 5, financial spread betting has a separate three percent base. + period: 2024 + input: + financial_spread_betting_net_losses: 1_000 + output: + general_betting_duty: 30 # FA 2014 section 128. + +- name: Case 6, other spread betting has a separate ten percent base. + period: 2024 + input: + other_spread_betting_net_losses: 1_000 + output: + general_betting_duty: 100 # FA 2014 section 129. + +- name: Case 7, betting exchanges pay duty on commission. + period: 2024 + input: + betting_exchange_commission: 1_000 + output: + general_betting_duty: 150 # FA 2014 section 140, commission rather than stakes. + +- name: Case 8, remote fixed odds change during FY2026 with exclusions retained. + period: 2026 + input: + remote_other_betting_net_losses: 36_500 + remote_uk_horseracing_betting_net_losses: 36_500 + land_based_fixed_odds_betting_net_losses: 36_500 + horse_and_dog_pool_betting_net_losses: 36_500 + output: + # FA 2026 section 87: 360 days at 15%, five at 25%; three excluded bases at 15%. + general_betting_duty: 21_950 # 5,525 + 3 * 5,475. + +- name: Case 9, remote fixed odds use twenty-five percent throughout FY2027. + period: 2027 + input: + remote_other_betting_net_losses: 36_500 + remote_uk_horseracing_betting_net_losses: 36_500 + land_based_fixed_odds_betting_net_losses: 36_500 + horse_and_dog_pool_betting_net_losses: 36_500 + output: + general_betting_duty: 25_550 # 9,125 + 3 * 5,475. diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/integration.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/integration.yaml new file mode 100644 index 0000000000..b037692f88 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/integration.yaml @@ -0,0 +1,364 @@ +# All other tax heads are held at zero to expose an exactly-once integration. +# Oracles: statutory component rates; casino .235487 approved proxy (issue 2587). +- name: Case 1, missing gambling bases preserve zero isolated tax totals. + period: 2024 + absolute_error_margin: 0.01 + input: + LVT: 0 + alcohol_duty: 0 + business_rates: 0 + capital_gains_tax: 0 + carbon_tax: 0 + consumer_incident_tax_revenue_change: 0 + corporate_incident_tax_revenue_change: 0 + corporate_sdlt: 0 + council_tax: 0 + domestic_rates: 0 + employer_ni_response_capital_incidence: 0 + employer_ni_response_consumer_incidence: 0 + expected_lbtt: 0 + expected_ltt: 0 + expected_sdlt: 0 + fuel_duty: 0 + high_value_council_tax_surcharge: 0 + income_tax: 0 + national_insurance: 0 + ni_employer: 0 + non_primary_residence_wealth_tax: 0 + private_school_vat: 0 + student_loan_repayments: 0 + tobacco_duty: 0 + tv_licence: 0 + vat: 0 + vat_change: 0 + vehicle_excise_duty: 0 + wealth_tax: 0 + output: + general_betting_duty: 0 + pool_betting_duty: 0 + remote_gaming_duty: 0 + machine_games_duty: 0 + bingo_duty: 0 + lottery_duty: 0 + gaming_duty: 0 + gambling_duty: 0 + household_tax: 0 + gov_tax: 0 +- name: Case 2, all fifteen non-overlapping bases enter both tax totals once. + period: 2024 + absolute_error_margin: 0.01 + input: + LVT: 0 + alcohol_duty: 0 + business_rates: 0 + capital_gains_tax: 0 + carbon_tax: 0 + consumer_incident_tax_revenue_change: 0 + corporate_incident_tax_revenue_change: 0 + corporate_sdlt: 0 + council_tax: 0 + domestic_rates: 0 + employer_ni_response_capital_incidence: 0 + employer_ni_response_consumer_incidence: 0 + expected_lbtt: 0 + expected_ltt: 0 + expected_sdlt: 0 + fuel_duty: 0 + high_value_council_tax_surcharge: 0 + income_tax: 0 + national_insurance: 0 + ni_employer: 0 + non_primary_residence_wealth_tax: 0 + private_school_vat: 0 + student_loan_repayments: 0 + tobacco_duty: 0 + tv_licence: 0 + vat: 0 + vat_change: 0 + vehicle_excise_duty: 0 + wealth_tax: 0 + remote_uk_horseracing_betting_net_losses: 1_000 + remote_other_betting_net_losses: 1_000 + land_based_fixed_odds_betting_net_losses: 1_000 + horse_and_dog_pool_betting_net_losses: 1_000 + financial_spread_betting_net_losses: 1_000 + other_spread_betting_net_losses: 1_000 + betting_exchange_commission: 1_000 + pool_betting_net_receipts: 1_000 + remote_gaming_net_losses: 1_000 + machine_games_lower_rate_net_takings: 1_000 + machine_games_standard_rate_net_takings: 1_000 + machine_games_higher_rate_net_takings: 1_000 + bingo_net_receipts: 1_000 + national_lottery_ticket_spending: 1_000 + land_based_casino_net_losses: 1_000 + output: + general_betting_duty: 880 + pool_betting_duty: 150 + remote_gaming_duty: 210 + machine_games_duty: 500 + bingo_duty: 100 + lottery_duty: 120 + gaming_duty: 235.487 + gambling_duty: 2_195.487 + household_tax: 2_195.487 + gov_tax: 2_195.487 +- name: Case 3, coverage before FY2024 remains unmodelled. + period: 2023 + absolute_error_margin: 0.01 + input: + LVT: 0 + alcohol_duty: 0 + business_rates: 0 + capital_gains_tax: 0 + carbon_tax: 0 + consumer_incident_tax_revenue_change: 0 + corporate_incident_tax_revenue_change: 0 + corporate_sdlt: 0 + council_tax: 0 + domestic_rates: 0 + employer_ni_response_capital_incidence: 0 + employer_ni_response_consumer_incidence: 0 + expected_lbtt: 0 + expected_ltt: 0 + expected_sdlt: 0 + fuel_duty: 0 + high_value_council_tax_surcharge: 0 + income_tax: 0 + national_insurance: 0 + ni_employer: 0 + non_primary_residence_wealth_tax: 0 + private_school_vat: 0 + student_loan_repayments: 0 + tobacco_duty: 0 + tv_licence: 0 + vat: 0 + vat_change: 0 + vehicle_excise_duty: 0 + wealth_tax: 0 + remote_uk_horseracing_betting_net_losses: 1_000 + remote_other_betting_net_losses: 1_000 + land_based_fixed_odds_betting_net_losses: 1_000 + horse_and_dog_pool_betting_net_losses: 1_000 + financial_spread_betting_net_losses: 1_000 + other_spread_betting_net_losses: 1_000 + betting_exchange_commission: 1_000 + pool_betting_net_receipts: 1_000 + remote_gaming_net_losses: 1_000 + machine_games_lower_rate_net_takings: 1_000 + machine_games_standard_rate_net_takings: 1_000 + machine_games_higher_rate_net_takings: 1_000 + bingo_net_receipts: 1_000 + national_lottery_ticket_spending: 1_000 + land_based_casino_net_losses: 1_000 + output: + general_betting_duty: 0 + pool_betting_duty: 0 + remote_gaming_duty: 0 + machine_games_duty: 0 + bingo_duty: 0 + lottery_duty: 0 + gaming_duty: 0 + gambling_duty: 0 + household_tax: 0 + gov_tax: 0 +- name: Case 4, explicit zero bases preserve zero isolated tax totals. + period: 2027 + absolute_error_margin: 0.01 + input: + LVT: 0 + alcohol_duty: 0 + business_rates: 0 + capital_gains_tax: 0 + carbon_tax: 0 + consumer_incident_tax_revenue_change: 0 + corporate_incident_tax_revenue_change: 0 + corporate_sdlt: 0 + council_tax: 0 + domestic_rates: 0 + employer_ni_response_capital_incidence: 0 + employer_ni_response_consumer_incidence: 0 + expected_lbtt: 0 + expected_ltt: 0 + expected_sdlt: 0 + fuel_duty: 0 + high_value_council_tax_surcharge: 0 + income_tax: 0 + national_insurance: 0 + ni_employer: 0 + non_primary_residence_wealth_tax: 0 + private_school_vat: 0 + student_loan_repayments: 0 + tobacco_duty: 0 + tv_licence: 0 + vat: 0 + vat_change: 0 + vehicle_excise_duty: 0 + wealth_tax: 0 + remote_uk_horseracing_betting_net_losses: 0 + remote_other_betting_net_losses: 0 + land_based_fixed_odds_betting_net_losses: 0 + horse_and_dog_pool_betting_net_losses: 0 + financial_spread_betting_net_losses: 0 + other_spread_betting_net_losses: 0 + betting_exchange_commission: 0 + pool_betting_net_receipts: 0 + remote_gaming_net_losses: 0 + machine_games_lower_rate_net_takings: 0 + machine_games_standard_rate_net_takings: 0 + machine_games_higher_rate_net_takings: 0 + bingo_net_receipts: 0 + national_lottery_ticket_spending: 0 + land_based_casino_net_losses: 0 + output: + general_betting_duty: 0 + pool_betting_duty: 0 + remote_gaming_duty: 0 + machine_games_duty: 0 + bingo_duty: 0 + lottery_duty: 0 + gaming_duty: 0 + gambling_duty: 0 + household_tax: 0 + gov_tax: 0 +- name: Case 5, remote gaming and betting keep separate April transitions. + period: 2026 + absolute_error_margin: 0.01 + input: + LVT: 0 + alcohol_duty: 0 + business_rates: 0 + capital_gains_tax: 0 + carbon_tax: 0 + consumer_incident_tax_revenue_change: 0 + corporate_incident_tax_revenue_change: 0 + corporate_sdlt: 0 + council_tax: 0 + domestic_rates: 0 + employer_ni_response_capital_incidence: 0 + employer_ni_response_consumer_incidence: 0 + expected_lbtt: 0 + expected_ltt: 0 + expected_sdlt: 0 + fuel_duty: 0 + high_value_council_tax_surcharge: 0 + income_tax: 0 + national_insurance: 0 + ni_employer: 0 + non_primary_residence_wealth_tax: 0 + private_school_vat: 0 + student_loan_repayments: 0 + tobacco_duty: 0 + tv_licence: 0 + vat: 0 + vat_change: 0 + vehicle_excise_duty: 0 + wealth_tax: 0 + remote_other_betting_net_losses: 36_500 + remote_gaming_net_losses: 36_500 + output: + general_betting_duty: 5_525 + pool_betting_duty: 0 + remote_gaming_duty: 14_600 + machine_games_duty: 0 + bingo_duty: 0 + lottery_duty: 0 + gaming_duty: 0 + gambling_duty: 20_125 + household_tax: 20_125 + gov_tax: 20_125 +- name: Case 6, the FY2025 remote gaming increase and bingo abolition are time weighted. + period: 2025 + absolute_error_margin: 0.01 + input: + LVT: 0 + alcohol_duty: 0 + business_rates: 0 + capital_gains_tax: 0 + carbon_tax: 0 + consumer_incident_tax_revenue_change: 0 + corporate_incident_tax_revenue_change: 0 + corporate_sdlt: 0 + council_tax: 0 + domestic_rates: 0 + employer_ni_response_capital_incidence: 0 + employer_ni_response_consumer_incidence: 0 + expected_lbtt: 0 + expected_ltt: 0 + expected_sdlt: 0 + fuel_duty: 0 + high_value_council_tax_surcharge: 0 + income_tax: 0 + national_insurance: 0 + ni_employer: 0 + non_primary_residence_wealth_tax: 0 + private_school_vat: 0 + student_loan_repayments: 0 + tobacco_duty: 0 + tv_licence: 0 + vat: 0 + vat_change: 0 + vehicle_excise_duty: 0 + wealth_tax: 0 + remote_gaming_net_losses: 36_500 + bingo_net_receipts: 36_500 + output: + general_betting_duty: 0 + pool_betting_duty: 0 + remote_gaming_duty: 7_760 + machine_games_duty: 0 + bingo_duty: 3_600 + lottery_duty: 0 + gaming_duty: 0 + gambling_duty: 11_360 + household_tax: 11_360 + gov_tax: 11_360 +- name: Case 7, casino gaming machine takings and other products remain distinct. + period: 2024 + absolute_error_margin: 0.01 + input: + LVT: 0 + alcohol_duty: 0 + business_rates: 0 + capital_gains_tax: 0 + carbon_tax: 0 + consumer_incident_tax_revenue_change: 0 + corporate_incident_tax_revenue_change: 0 + corporate_sdlt: 0 + council_tax: 0 + domestic_rates: 0 + employer_ni_response_capital_incidence: 0 + employer_ni_response_consumer_incidence: 0 + expected_lbtt: 0 + expected_ltt: 0 + expected_sdlt: 0 + fuel_duty: 0 + high_value_council_tax_surcharge: 0 + income_tax: 0 + national_insurance: 0 + ni_employer: 0 + non_primary_residence_wealth_tax: 0 + private_school_vat: 0 + student_loan_repayments: 0 + tobacco_duty: 0 + tv_licence: 0 + vat: 0 + vat_change: 0 + vehicle_excise_duty: 0 + wealth_tax: 0 + remote_uk_horseracing_betting_net_losses: 1_000 + machine_games_standard_rate_net_takings: 1_000 + bingo_net_receipts: 1_000 + national_lottery_ticket_spending: 1_000 + land_based_casino_net_losses: 1_000 + output: + general_betting_duty: 150 + pool_betting_duty: 0 + remote_gaming_duty: 0 + machine_games_duty: 200 + bingo_duty: 100 + lottery_duty: 120 + gaming_duty: 235.487 + gambling_duty: 805.487 + household_tax: 805.487 + gov_tax: 805.487 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/lottery_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/lottery_duty.yaml new file mode 100644 index 0000000000..c55010b730 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/lottery_duty.yaml @@ -0,0 +1,19 @@ +- name: Case 1, National Lottery duty uses gross chargeable ticket consideration. + period: 2024 + input: + national_lottery_ticket_spending: 1_000 + output: + lottery_duty: 120 # FA 1993 section 25(1): 12% without winnings deductions. + +- name: Case 2, the same lottery rate applies in FY2027. + period: 2027 + input: + national_lottery_ticket_spending: 1_000 + output: + lottery_duty: 120 + +- name: Case 3, missing National Lottery spending defaults to zero. + period: 2024 + input: {} + output: + lottery_duty: 0 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/machine_games_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/machine_games_duty.yaml new file mode 100644 index 0000000000..9b8cba8bd7 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/machine_games_duty.yaml @@ -0,0 +1,29 @@ +- name: Case 1, lower-rate machine net takings use five percent. + period: 2024 + input: + machine_games_lower_rate_net_takings: 1_000 + output: + machine_games_duty: 50 # FA 2012 Schedule 24 paragraph 9. + +- name: Case 2, standard-rate machine net takings use twenty percent. + period: 2024 + input: + machine_games_standard_rate_net_takings: 1_000 + output: + machine_games_duty: 200 + +- name: Case 3, higher-rate machine net takings use twenty-five percent. + period: 2024 + input: + machine_games_higher_rate_net_takings: 1_000 + output: + machine_games_duty: 250 + +- name: Case 4, machine classes supply their own bases without a mix assumption. + period: 2024 + input: + machine_games_lower_rate_net_takings: 1_000 + machine_games_standard_rate_net_takings: 2_000 + machine_games_higher_rate_net_takings: 3_000 + output: + machine_games_duty: 1_200 # 50 + 400 + 750. diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/pool_betting_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/pool_betting_duty.yaml new file mode 100644 index 0000000000..87c132f0ac --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/pool_betting_duty.yaml @@ -0,0 +1,17 @@ +- name: Case 1, dutiable non-racing pool receipts use fifteen percent. + period: 2024 + input: + pool_betting_net_receipts: 1_000 + output: + pool_betting_duty: 150 # FA 2014 section 144. + general_betting_duty: 0 + +- name: Case 2, general and pool betting bases remain distinct. + period: 2027 + input: + pool_betting_net_receipts: 1_000 + horse_and_dog_pool_betting_net_losses: 2_000 + output: + general_betting_duty: 300 + pool_betting_duty: 150 + gambling_duty: 450 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/remote_gaming_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/remote_gaming_duty.yaml new file mode 100644 index 0000000000..009a6df2a3 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/remote_gaming_duty.yaml @@ -0,0 +1,21 @@ +- name: Case 1, remote gaming profits use twenty-one percent in FY2024. + period: 2024 + input: + remote_gaming_net_losses: 36_500 + output: + remote_gaming_duty: 7_665 # HMRC historic rates: 36,500 * 21%. + +- name: Case 2, remote gaming changes on 1 April within FY2025. + period: 2025 + input: + remote_gaming_net_losses: 36_500 + output: + # FA 2026 section 86: 360 days at 21% and five at 40%. + remote_gaming_duty: 7_760 # 100 * (360 * .21 + 5 * .40). + +- name: Case 3, remote gaming uses forty percent throughout FY2026. + period: 2026 + input: + remote_gaming_net_losses: 36_500 + output: + remote_gaming_duty: 14_600 diff --git a/policyengine_uk/tests/test_gambling_additional_schedules.py b/policyengine_uk/tests/test_gambling_additional_schedules.py new file mode 100644 index 0000000000..ddfbb06f2a --- /dev/null +++ b/policyengine_uk/tests/test_gambling_additional_schedules.py @@ -0,0 +1,70 @@ +"""New duty dates, operator catalogue and the household casino approximation.""" + +import numpy as np +import pytest + +from policyengine_uk import CountryTaxBenefitSystem +from policyengine_uk.tests.test_gambling_schedules import gambling_simulation + + +def test_bingo_rate_preserves_abolition_date(): + """Finance Act 2026 section 88 abolishes bingo duty on 1 April 2026.""" + system = CountryTaxBenefitSystem() + rate = system.parameters.gov.hmrc.gambling_duty.bingo.rate + assert rate("2026-03-31") == 0.1 + assert rate("2026-04-01") == 0 + + +def test_registered_casino_normal_six_month_operator_catalogue(): + """Finance Act 1997 section 11(2): cumulative six-month premises bands.""" + system = CountryTaxBenefitSystem() + scale = system.parameters( + "2024" + ).gov.hmrc.gambling_duty.gaming.normal_six_month_bands + yield_amounts = np.array( + [ + 0, + 2_686_000, + 2_686_001, + 4_538_000, + 4_538_001, + 7_781_000, + 7_781_001, + 14_626_000, + 14_626_001, + ], + dtype=float, + ) + # Successive statutory widths: 2,686,000; 1,852,000; 3,243,000; 6,845,000. + expected = [ + 0, + 402_900, + 402_900.2, + 773_300, + 773_300.3, + 1_746_200, + 1_746_200.4, + 4_484_200, + 4_484_200.5, + ] + assert scale.calc(yield_amounts) == pytest.approx(expected) + + +def test_explicit_casino_incidence_reform_changes_household_duty(): + """An explicit 30% incidence assumption charges GBP 300 on GBP 1,000.""" + sim = gambling_simulation( + 2024, + {"household.consumption.gambling.casino_effective_rate": {"2024": 0.3}}, + {"land_based_casino_net_losses": 1_000}, + ) + assert sim.calculate("gaming_duty", 2024).iloc[0] == pytest.approx(300) + + +def test_operator_band_reform_does_not_automatically_change_household_proxy(): + """The empirical household proxy does not model casino band responses.""" + sim = gambling_simulation( + 2024, + {"gov.hmrc.gambling_duty.gaming.normal_six_month_bands[0].rate": {"2024": 0.3}}, + {"land_based_casino_net_losses": 1_000}, + ) + assert sim.calculate("gaming_duty", 2024).iloc[0] == pytest.approx(235.487) diff --git a/policyengine_uk/tests/test_gambling_schedules.py b/policyengine_uk/tests/test_gambling_schedules.py new file mode 100644 index 0000000000..f203051530 --- /dev/null +++ b/policyengine_uk/tests/test_gambling_schedules.py @@ -0,0 +1,91 @@ +"""Statutory gambling rate dates and fiscal-year reform behaviour.""" + +import pytest + +from policyengine_uk import CountryTaxBenefitSystem, Microsimulation +from policyengine_uk.model_api import Scenario + + +@pytest.mark.parametrize( + "path,before,after,old,new", + [ + ("remote_gaming.rate", "2026-03-31", "2026-04-01", 0.21, 0.4), + ("general_betting.rates.remote", "2027-03-31", "2027-04-01", 0.15, 0.25), + ], +) +def test_gambling_rates_preserve_exact_statutory_dates(path, before, after, old, new): + """Finance Act 2026 sections 86 and 87 change rates on 1 April.""" + system = CountryTaxBenefitSystem() + rate = system.parameters.get_child(f"gov.hmrc.gambling_duty.{path}") + assert rate(before) == old + assert rate(after) == new + + +def gambling_simulation(year, changes, inputs): + return Microsimulation( + situation={ + "people": {"person1": {"age": {year: 40}}}, + "benunits": {"benunit": {"members": ["person1"]}}, + "households": { + "household": { + "members": ["person1"], + **{name: {year: value} for name, value in inputs.items()}, + } + }, + }, + scenario=Scenario(parameter_changes=changes), + ) + + +@pytest.mark.parametrize( + "year,path,inputs,variable,expected", + [ + ( + 2025, + "remote_gaming.rate", + {"remote_gaming_net_losses": 36_500}, + "remote_gaming_duty", + 18_250, + ), + ( + 2026, + "general_betting.rates.remote", + {"remote_other_betting_net_losses": 36_500}, + "general_betting_duty", + 18_250, + ), + ( + 2026, + "machine_games.rates.standard", + {"machine_games_standard_rate_net_takings": 36_500}, + "machine_games_duty", + 18_250, + ), + ], +) +def test_annual_gambling_reform_replaces_the_whole_fiscal_year( + year, path, inputs, variable, expected +): + """A 50% annual reform applies to all GBP 36,500 of the year's base.""" + sim = gambling_simulation( + year, {f"gov.hmrc.gambling_duty.{path}": {str(year): 0.5}}, inputs + ) + assert sim.calculate(variable, year).values[0] == pytest.approx(expected) + + +def test_explicit_date_reform_keeps_its_dates_and_adjacent_fiscal_year(): + """Five days at 50%, 360 at 15%, then the FY2026 statutory transition.""" + sim = gambling_simulation( + 2025, + { + "gov.hmrc.gambling_duty.general_betting.rates.remote": { + "2026-04-01.2026-04-05": 0.5 + } + }, + {"remote_other_betting_net_losses": 36_500}, + ) + sim.set_input("remote_other_betting_net_losses", 2026, [36_500]) + # Explicit five-day reform: 100 * (360 * .15 + 5 * .50). + assert sim.calculate("general_betting_duty", 2025).values[0] == pytest.approx(5_650) + # Finance Act 2026 section 87: 100 * (360 * .15 + 5 * .25). + assert sim.calculate("general_betting_duty", 2026).values[0] == pytest.approx(5_525) diff --git a/policyengine_uk/variables/gov/gov_tax.py b/policyengine_uk/variables/gov/gov_tax.py index ffbe95bae9..ea1742a789 100644 --- a/policyengine_uk/variables/gov/gov_tax.py +++ b/policyengine_uk/variables/gov/gov_tax.py @@ -13,6 +13,7 @@ "fuel_duty", "alcohol_duty", "tobacco_duty", + "gambling_duty", "vehicle_excise_duty", "tv_licence", "wealth_tax", diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/bingo_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/bingo_duty.py new file mode 100644 index 0000000000..319bac292d --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/bingo_duty.py @@ -0,0 +1,23 @@ +from policyengine_uk.model_api import * +from policyengine_uk.utils.excise import fiscal_year_segments + + +class bingo_duty(Variable): + value_type = float + entity = Household + definition_period = YEAR + unit = GBP + label = "Bingo Duty" + reference = ( + "https://www.legislation.gov.uk/ukpga/1981/63/section/17/2024-04-06", + "https://www.legislation.gov.uk/ukpga/2026/11/section/88", + ) + + def formula_2024(household, period, parameters): + receipts = household("bingo_net_receipts", period) + total = 0 + for rates, share in fiscal_year_segments( + parameters.gov.hmrc.gambling_duty.bingo, period.start.year + ): + total += receipts * rates.rate * share + return total diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/gambling_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/gambling_duty.py new file mode 100644 index 0000000000..a5fc8b5225 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/gambling_duty.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class gambling_duty(Variable): + value_type = float + entity = Household + definition_period = YEAR + unit = GBP + label = "Gambling duty" + adds = [ + "general_betting_duty", + "pool_betting_duty", + "remote_gaming_duty", + "machine_games_duty", + "gaming_duty", + "bingo_duty", + "lottery_duty", + ] diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/gaming_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/gaming_duty.py new file mode 100644 index 0000000000..2219feaecc --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/gaming_duty.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class gaming_duty(Variable): + value_type = float + entity = Household + definition_period = YEAR + unit = GBP + label = "Casino Gaming Duty incidence" + reference = ( + "https://www.legislation.gov.uk/ukpga/1997/16/section/11", + "https://github.com/PolicyEngine/policyengine-uk/issues/2587", + ) + + def formula_2024(household, period, parameters): + rate = parameters(period).household.consumption.gambling.casino_effective_rate + return household("land_based_casino_net_losses", period) * rate diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/general_betting_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/general_betting_duty.py new file mode 100644 index 0000000000..fe6ad16b9e --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/general_betting_duty.py @@ -0,0 +1,41 @@ +from policyengine_uk.model_api import * +from policyengine_uk.utils.excise import fiscal_year_segments + + +class general_betting_duty(Variable): + value_type = float + entity = Household + definition_period = YEAR + unit = GBP + label = "General Betting Duty" + reference = ( + "https://www.legislation.gov.uk/ukpga/2014/26/part/3/chapter/1", + "https://www.legislation.gov.uk/ukpga/2026/11/section/87", + ) + + def formula_2024(household, period, parameters): + fixed_odds = add( + household, + period, + [ + "remote_uk_horseracing_betting_net_losses", + "land_based_fixed_odds_betting_net_losses", + "horse_and_dog_pool_betting_net_losses", + ], + ) + remote = household("remote_other_betting_net_losses", period) + financial_spread = household("financial_spread_betting_net_losses", period) + other_spread = household("other_spread_betting_net_losses", period) + exchange = household("betting_exchange_commission", period) + total = 0 + for rates, share in fiscal_year_segments( + parameters.gov.hmrc.gambling_duty.general_betting.rates, period.start.year + ): + total += share * ( + fixed_odds * rates.fixed_odds + + remote * rates.remote + + financial_spread * rates.financial_spread + + other_spread * rates.other_spread + + exchange * rates.exchange_commission + ) + return total diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/lottery_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/lottery_duty.py new file mode 100644 index 0000000000..a7b0ca1c59 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/lottery_duty.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + + +class lottery_duty(Variable): + value_type = float + entity = Household + definition_period = YEAR + unit = GBP + label = "Lottery Duty" + reference = "https://www.legislation.gov.uk/ukpga/1993/34/section/25" + + def formula_2024(household, period, parameters): + rate = parameters(period).gov.hmrc.gambling_duty.lottery.rate + return household("national_lottery_ticket_spending", period) * rate diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/machine_games_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/machine_games_duty.py new file mode 100644 index 0000000000..f96b8d6382 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/machine_games_duty.py @@ -0,0 +1,24 @@ +from policyengine_uk.model_api import * +from policyengine_uk.utils.excise import fiscal_year_segments + + +class machine_games_duty(Variable): + value_type = float + entity = Household + definition_period = YEAR + unit = GBP + label = "Machine Games Duty" + reference = "https://www.legislation.gov.uk/ukpga/2012/14/schedule/24" + + def formula_2024(household, period, parameters): + lower = household("machine_games_lower_rate_net_takings", period) + standard = household("machine_games_standard_rate_net_takings", period) + higher = household("machine_games_higher_rate_net_takings", period) + total = 0 + for rates, share in fiscal_year_segments( + parameters.gov.hmrc.gambling_duty.machine_games.rates, period.start.year + ): + total += share * ( + lower * rates.lower + standard * rates.standard + higher * rates.higher + ) + return total diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/pool_betting_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/pool_betting_duty.py new file mode 100644 index 0000000000..f18efdc335 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/pool_betting_duty.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * +from policyengine_uk.utils.excise import fiscal_year_segments + + +class pool_betting_duty(Variable): + value_type = float + entity = Household + definition_period = YEAR + unit = GBP + label = "Pool Betting Duty" + reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/144" + + def formula_2024(household, period, parameters): + receipts = household("pool_betting_net_receipts", period) + total = 0 + for rates, share in fiscal_year_segments( + parameters.gov.hmrc.gambling_duty.pool_betting, period.start.year + ): + total += receipts * rates.rate * share + return total diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/remote_gaming_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/remote_gaming_duty.py new file mode 100644 index 0000000000..bcb8d82ced --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/remote_gaming_duty.py @@ -0,0 +1,23 @@ +from policyengine_uk.model_api import * +from policyengine_uk.utils.excise import fiscal_year_segments + + +class remote_gaming_duty(Variable): + value_type = float + entity = Household + definition_period = YEAR + unit = GBP + label = "Remote Gaming Duty" + reference = ( + "https://www.legislation.gov.uk/ukpga/2014/26/section/155", + "https://www.legislation.gov.uk/ukpga/2026/11/section/86", + ) + + def formula_2024(household, period, parameters): + losses = household("remote_gaming_net_losses", period) + total = 0 + for rates, share in fiscal_year_segments( + parameters.gov.hmrc.gambling_duty.remote_gaming, period.start.year + ): + total += losses * rates.rate * share + return total diff --git a/policyengine_uk/variables/gov/hmrc/household_tax.py b/policyengine_uk/variables/gov/hmrc/household_tax.py index 3404b8ade9..3794ac08b5 100644 --- a/policyengine_uk/variables/gov/hmrc/household_tax.py +++ b/policyengine_uk/variables/gov/hmrc/household_tax.py @@ -13,6 +13,7 @@ "fuel_duty", "alcohol_duty", "tobacco_duty", + "gambling_duty", "vehicle_excise_duty", "tv_licence", "wealth_tax", diff --git a/policyengine_uk/variables/input/consumption/gambling/betting_exchange_commission.py b/policyengine_uk/variables/input/consumption/gambling/betting_exchange_commission.py new file mode 100644 index 0000000000..765e908a7e --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/betting_exchange_commission.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class betting_exchange_commission(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Chargeable betting exchange commission attributed to the household" + reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/140" diff --git a/policyengine_uk/variables/input/consumption/gambling/bingo_net_receipts.py b/policyengine_uk/variables/input/consumption/gambling/bingo_net_receipts.py new file mode 100644 index 0000000000..ad67462e6d --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/bingo_net_receipts.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class bingo_net_receipts(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Attributed dutiable bingo promotion profits" + reference = "https://www.legislation.gov.uk/ukpga/1981/63/section/17/2024-04-06" diff --git a/policyengine_uk/variables/input/consumption/gambling/financial_spread_betting_net_losses.py b/policyengine_uk/variables/input/consumption/gambling/financial_spread_betting_net_losses.py new file mode 100644 index 0000000000..8aefb4c920 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/financial_spread_betting_net_losses.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class financial_spread_betting_net_losses(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Attributed dutiable financial spread betting losses" + reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/128" diff --git a/policyengine_uk/variables/input/consumption/gambling/horse_and_dog_pool_betting_net_losses.py b/policyengine_uk/variables/input/consumption/gambling/horse_and_dog_pool_betting_net_losses.py new file mode 100644 index 0000000000..8e0dcaa607 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/horse_and_dog_pool_betting_net_losses.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class horse_and_dog_pool_betting_net_losses(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Attributed dutiable horse and dog racing pool losses" + reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/126" diff --git a/policyengine_uk/variables/input/consumption/gambling/land_based_casino_net_losses.py b/policyengine_uk/variables/input/consumption/gambling/land_based_casino_net_losses.py new file mode 100644 index 0000000000..bb19e35e74 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/land_based_casino_net_losses.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class land_based_casino_net_losses(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Attributed dutiable land-based casino gaming yield" + reference = "https://www.legislation.gov.uk/ukpga/1997/16/section/11" diff --git a/policyengine_uk/variables/input/consumption/gambling/land_based_fixed_odds_betting_net_losses.py b/policyengine_uk/variables/input/consumption/gambling/land_based_fixed_odds_betting_net_losses.py new file mode 100644 index 0000000000..0156162579 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/land_based_fixed_odds_betting_net_losses.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class land_based_fixed_odds_betting_net_losses(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Attributed dutiable land-based fixed-odds losses" + reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/126" diff --git a/policyengine_uk/variables/input/consumption/gambling/machine_games_higher_rate_net_takings.py b/policyengine_uk/variables/input/consumption/gambling/machine_games_higher_rate_net_takings.py new file mode 100644 index 0000000000..5a80acaa9c --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/machine_games_higher_rate_net_takings.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class machine_games_higher_rate_net_takings(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Attributed dutiable higher-rate machine games net takings" + reference = "https://www.gov.uk/guidance/machine-games-duty-excise-notice-452" diff --git a/policyengine_uk/variables/input/consumption/gambling/machine_games_lower_rate_net_takings.py b/policyengine_uk/variables/input/consumption/gambling/machine_games_lower_rate_net_takings.py new file mode 100644 index 0000000000..f746d877d6 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/machine_games_lower_rate_net_takings.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class machine_games_lower_rate_net_takings(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Attributed dutiable lower-rate machine games net takings" + reference = "https://www.gov.uk/guidance/machine-games-duty-excise-notice-452" diff --git a/policyengine_uk/variables/input/consumption/gambling/machine_games_standard_rate_net_takings.py b/policyengine_uk/variables/input/consumption/gambling/machine_games_standard_rate_net_takings.py new file mode 100644 index 0000000000..c4acb3d525 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/machine_games_standard_rate_net_takings.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class machine_games_standard_rate_net_takings(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Attributed dutiable standard-rate machine games net takings" + reference = "https://www.gov.uk/guidance/machine-games-duty-excise-notice-452" diff --git a/policyengine_uk/variables/input/consumption/gambling/national_lottery_ticket_spending.py b/policyengine_uk/variables/input/consumption/gambling/national_lottery_ticket_spending.py new file mode 100644 index 0000000000..e493e2bbbf --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/national_lottery_ticket_spending.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class national_lottery_ticket_spending(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Chargeable National Lottery ticket consideration" + reference = "https://www.legislation.gov.uk/ukpga/1993/34/section/25" diff --git a/policyengine_uk/variables/input/consumption/gambling/other_spread_betting_net_losses.py b/policyengine_uk/variables/input/consumption/gambling/other_spread_betting_net_losses.py new file mode 100644 index 0000000000..6e2875b4b6 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/other_spread_betting_net_losses.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class other_spread_betting_net_losses(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Attributed dutiable other spread betting losses" + reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/129" diff --git a/policyengine_uk/variables/input/consumption/gambling/pool_betting_net_receipts.py b/policyengine_uk/variables/input/consumption/gambling/pool_betting_net_receipts.py new file mode 100644 index 0000000000..d3f88029c1 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/pool_betting_net_receipts.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class pool_betting_net_receipts(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Attributed dutiable non-racing pool betting net receipts" + reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/143" diff --git a/policyengine_uk/variables/input/consumption/gambling/remote_gaming_net_losses.py b/policyengine_uk/variables/input/consumption/gambling/remote_gaming_net_losses.py new file mode 100644 index 0000000000..ecaae21921 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/remote_gaming_net_losses.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class remote_gaming_net_losses(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Attributed chargeable non-exempt remote gaming losses" + reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/161" diff --git a/policyengine_uk/variables/input/consumption/gambling/remote_other_betting_net_losses.py b/policyengine_uk/variables/input/consumption/gambling/remote_other_betting_net_losses.py new file mode 100644 index 0000000000..c2ee45223c --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/remote_other_betting_net_losses.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class remote_other_betting_net_losses(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Attributed dutiable other remote fixed-odds losses" + reference = "https://www.legislation.gov.uk/ukpga/2026/11/section/87" diff --git a/policyengine_uk/variables/input/consumption/gambling/remote_uk_horseracing_betting_net_losses.py b/policyengine_uk/variables/input/consumption/gambling/remote_uk_horseracing_betting_net_losses.py new file mode 100644 index 0000000000..367b3e80e3 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/gambling/remote_uk_horseracing_betting_net_losses.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class remote_uk_horseracing_betting_net_losses(Variable): + value_type = float + entity = Household + definition_period = YEAR + default_value = 0 + unit = GBP + label = "Attributed dutiable remote UK horseracing fixed-odds losses" + reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/127" From a623908b314d8c5b898da0d1663c6c735216daf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Juaristi?= <127882282+juaristi22@users.noreply.github.com> Date: Fri, 23 Oct 2026 01:56:53 +0100 Subject: [PATCH 2/8] Correct gambling duty statutory references --- .../general_betting/rates/exchange_commission.yaml | 4 ++-- .../general_betting/rates/financial_spread.yaml | 4 ++-- .../gambling_duty/general_betting/rates/fixed_odds.yaml | 2 ++ .../gambling_duty/general_betting/rates/other_spread.yaml | 4 ++-- .../gov/hmrc/gambling_duty/general_betting_duty.yaml | 6 +++--- .../consumption/gambling/betting_exchange_commission.py | 2 +- .../gambling/horse_and_dog_pool_betting_net_losses.py | 2 +- .../consumption/gambling/other_spread_betting_net_losses.py | 2 +- 8 files changed, 14 insertions(+), 12 deletions(-) diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/exchange_commission.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/exchange_commission.yaml index 35d721dcf6..abd5b9a0de 100644 --- a/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/exchange_commission.yaml +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/exchange_commission.yaml @@ -6,8 +6,8 @@ metadata: period: year label: general betting duty exchange commission rate reference: - - title: Finance Act 2014, section 140 - href: https://www.legislation.gov.uk/ukpga/2014/26/section/140 + - title: Finance Act 2014, section 141(2) to (4) + href: https://www.legislation.gov.uk/ukpga/2014/26/section/141 - title: HMRC gambling duty rates href: https://www.gov.uk/government/publications/rates-and-allowance-excise-duty-gambling-duty/excise-duty-gambling-duty-rates - title: HMRC General Betting Duty guidance diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/financial_spread.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/financial_spread.yaml index 9bfdd79982..e905872ac1 100644 --- a/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/financial_spread.yaml +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/financial_spread.yaml @@ -6,8 +6,8 @@ metadata: period: year label: general betting duty financial spread rate reference: - - title: Finance Act 2014, section 128 - href: https://www.legislation.gov.uk/ukpga/2014/26/section/128 + - title: Finance Act 2014, section 129(2) + href: https://www.legislation.gov.uk/ukpga/2014/26/section/129 - title: HMRC gambling duty rates href: https://www.gov.uk/government/publications/rates-and-allowance-excise-duty-gambling-duty/excise-duty-gambling-duty-rates - title: HMRC General Betting Duty guidance diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/fixed_odds.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/fixed_odds.yaml index a4b1b72720..8242a0f702 100644 --- a/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/fixed_odds.yaml +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/fixed_odds.yaml @@ -8,6 +8,8 @@ metadata: reference: - title: Finance Act 2014, section 127 href: https://www.legislation.gov.uk/ukpga/2014/26/section/127 + - title: Finance Act 2014, section 135(2) + href: https://www.legislation.gov.uk/ukpga/2014/26/section/135 - title: HMRC gambling duty rates href: https://www.gov.uk/government/publications/rates-and-allowance-excise-duty-gambling-duty/excise-duty-gambling-duty-rates - title: HMRC General Betting Duty guidance diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/other_spread.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/other_spread.yaml index a0eeec4be4..f8738f79bd 100644 --- a/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/other_spread.yaml +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/general_betting/rates/other_spread.yaml @@ -6,8 +6,8 @@ metadata: period: year label: general betting duty other spread rate reference: - - title: Finance Act 2014, section 129 - href: https://www.legislation.gov.uk/ukpga/2014/26/section/129 + - title: Finance Act 2014, section 130(2) + href: https://www.legislation.gov.uk/ukpga/2014/26/section/130 - title: HMRC gambling duty rates href: https://www.gov.uk/government/publications/rates-and-allowance-excise-duty-gambling-duty/excise-duty-gambling-duty-rates - title: HMRC General Betting Duty guidance diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/general_betting_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/general_betting_duty.yaml index dc1b083c49..3afdd88a7a 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/general_betting_duty.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/general_betting_duty.yaml @@ -32,21 +32,21 @@ input: financial_spread_betting_net_losses: 1_000 output: - general_betting_duty: 30 # FA 2014 section 128. + general_betting_duty: 30 # FA 2014 section 129(2). - name: Case 6, other spread betting has a separate ten percent base. period: 2024 input: other_spread_betting_net_losses: 1_000 output: - general_betting_duty: 100 # FA 2014 section 129. + general_betting_duty: 100 # FA 2014 section 130(2). - name: Case 7, betting exchanges pay duty on commission. period: 2024 input: betting_exchange_commission: 1_000 output: - general_betting_duty: 150 # FA 2014 section 140, commission rather than stakes. + general_betting_duty: 150 # FA 2014 section 141(2) to (4), commission rather than stakes. - name: Case 8, remote fixed odds change during FY2026 with exclusions retained. period: 2026 diff --git a/policyengine_uk/variables/input/consumption/gambling/betting_exchange_commission.py b/policyengine_uk/variables/input/consumption/gambling/betting_exchange_commission.py index 765e908a7e..453f5c85b9 100644 --- a/policyengine_uk/variables/input/consumption/gambling/betting_exchange_commission.py +++ b/policyengine_uk/variables/input/consumption/gambling/betting_exchange_commission.py @@ -8,4 +8,4 @@ class betting_exchange_commission(Variable): default_value = 0 unit = GBP label = "Chargeable betting exchange commission attributed to the household" - reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/140" + reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/141" diff --git a/policyengine_uk/variables/input/consumption/gambling/horse_and_dog_pool_betting_net_losses.py b/policyengine_uk/variables/input/consumption/gambling/horse_and_dog_pool_betting_net_losses.py index 8e0dcaa607..f628b80280 100644 --- a/policyengine_uk/variables/input/consumption/gambling/horse_and_dog_pool_betting_net_losses.py +++ b/policyengine_uk/variables/input/consumption/gambling/horse_and_dog_pool_betting_net_losses.py @@ -8,4 +8,4 @@ class horse_and_dog_pool_betting_net_losses(Variable): default_value = 0 unit = GBP label = "Attributed dutiable horse and dog racing pool losses" - reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/126" + reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/134" diff --git a/policyengine_uk/variables/input/consumption/gambling/other_spread_betting_net_losses.py b/policyengine_uk/variables/input/consumption/gambling/other_spread_betting_net_losses.py index 6e2875b4b6..075809c3b2 100644 --- a/policyengine_uk/variables/input/consumption/gambling/other_spread_betting_net_losses.py +++ b/policyengine_uk/variables/input/consumption/gambling/other_spread_betting_net_losses.py @@ -8,4 +8,4 @@ class other_spread_betting_net_losses(Variable): default_value = 0 unit = GBP label = "Attributed dutiable other spread betting losses" - reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/129" + reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/130" From 07e1ba81497036f65bcac06382d402ab9129b34a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Juaristi?= <127882282+juaristi22@users.noreply.github.com> Date: Fri, 23 Oct 2026 10:59:13 +0100 Subject: [PATCH 3/8] Split dated Lottery Duty reforms by day Lottery Duty read the rate once per model year, so a reform dated within a fiscal year had no effect. It now goes through fiscal_year_segments like the other statutory families, and the rate keeps its calendar dates. A 20% rate from 1 January to 5 April 2027 on GBP 1,000 of FY2026 ticket spending now gives GBP 140.82 rather than GBP 120. Refs #2586 Co-Authored-By: Claude Opus 5.5 --- .../gov/hmrc/gambling_duty/lottery/rate.yaml | 1 + .../tests/test_gambling_schedules.py | 38 +++++++++++++++++++ .../gov/hmrc/gambling_duty/lottery_duty.py | 10 ++++- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/lottery/rate.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/lottery/rate.yaml index 3a0caad32f..1c7b65606d 100644 --- a/policyengine_uk/parameters/gov/hmrc/gambling_duty/lottery/rate.yaml +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/lottery/rate.yaml @@ -12,3 +12,4 @@ metadata: href: https://www.legislation.gov.uk/ukpga/1993/34/section/24/2024-04-06 - title: HMRC Lottery Duty Notice 458 href: https://www.gov.uk/guidance/lottery-duty-excise-notice-458 + preserve_calendar_dates: true diff --git a/policyengine_uk/tests/test_gambling_schedules.py b/policyengine_uk/tests/test_gambling_schedules.py index f203051530..904834e087 100644 --- a/policyengine_uk/tests/test_gambling_schedules.py +++ b/policyengine_uk/tests/test_gambling_schedules.py @@ -61,6 +61,13 @@ def gambling_simulation(year, changes, inputs): "machine_games_duty", 18_250, ), + ( + 2026, + "lottery.rate", + {"national_lottery_ticket_spending": 36_500}, + "lottery_duty", + 18_250, + ), ], ) def test_annual_gambling_reform_replaces_the_whole_fiscal_year( @@ -73,6 +80,37 @@ def test_annual_gambling_reform_replaces_the_whole_fiscal_year( assert sim.calculate(variable, year).values[0] == pytest.approx(expected) +@pytest.mark.parametrize( + "path,value,inputs,variable,expected", + [ + ( + "lottery.rate", + 0.2, + {"national_lottery_ticket_spending": 1_000}, + "lottery_duty", + 1_000 * (270 * 0.12 + 95 * 0.2) / 365, # 140.82, not 120. + ), + ( + "remote_gaming.rate", + 0.5, + {"remote_gaming_net_losses": 10_000}, + "remote_gaming_duty", + 10_000 * (270 * 0.4 + 95 * 0.5) / 365, # 4,260.27. + ), + ], +) +def test_dated_reform_within_the_fiscal_year_splits_by_day( + path, value, inputs, variable, expected +): + """A reform from 1 January to 5 April 2027 covers 95 of FY2026's 365 days.""" + sim = gambling_simulation( + 2026, + {f"gov.hmrc.gambling_duty.{path}": {"2027-01-01.2027-04-05": value}}, + inputs, + ) + assert sim.calculate(variable, 2026).values[0] == pytest.approx(expected, abs=0.01) + + def test_explicit_date_reform_keeps_its_dates_and_adjacent_fiscal_year(): """Five days at 50%, 360 at 15%, then the FY2026 statutory transition.""" sim = gambling_simulation( diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/lottery_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/lottery_duty.py index a7b0ca1c59..66b759da75 100644 --- a/policyengine_uk/variables/gov/hmrc/gambling_duty/lottery_duty.py +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/lottery_duty.py @@ -1,4 +1,5 @@ from policyengine_uk.model_api import * +from policyengine_uk.utils.excise import fiscal_year_segments class lottery_duty(Variable): @@ -10,5 +11,10 @@ class lottery_duty(Variable): reference = "https://www.legislation.gov.uk/ukpga/1993/34/section/25" def formula_2024(household, period, parameters): - rate = parameters(period).gov.hmrc.gambling_duty.lottery.rate - return household("national_lottery_ticket_spending", period) * rate + spending = household("national_lottery_ticket_spending", period) + total = 0 + for rates, share in fiscal_year_segments( + parameters.gov.hmrc.gambling_duty.lottery, period.start.year + ): + total += spending * rates.rate * share + return total From b446a588b1b04de6499ee8ec6ba46121ec4f9fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Juaristi?= <127882282+juaristi22@users.noreply.github.com> Date: Fri, 23 Oct 2026 11:00:46 +0100 Subject: [PATCH 4/8] State that the casino proxy takes one value per model year The casino incidence proxy sits outside gov with the other household consumption assumptions. Formulas read it at 1 January of the model year, and the reform routes read a bare year on it as a calendar year, so a day split would spread a bare-year reform over two model years. Its description and the docs now say it takes one value per model year and is not split by day like the statutory rates, and a test pins that. Refs #2586 Co-Authored-By: Claude Opus 5.5 --- docs/engineering/gambling-duties.md | 16 +++++++++++++--- .../gambling/casino_effective_rate.yaml | 2 +- .../tests/test_gambling_additional_schedules.py | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/docs/engineering/gambling-duties.md b/docs/engineering/gambling-duties.md index 3c93cff0a2..e0ac979476 100644 --- a/docs/engineering/gambling-duties.md +++ b/docs/engineering/gambling-duties.md @@ -11,12 +11,14 @@ repair existing survey data. ## Periods and attribution Coverage starts in 2024/25. Model period `2024` covers 6 April 2024 to 5 April -2025. The formulas apply dated rates within each interval of that fiscal year -and weight each interval by its share of the year, using +2025. The six statutory duty formulas apply dated rates within each interval +of that fiscal year and weight each interval by its share of the year, using `utils.excise.fiscal_year_segments`. They assume each supplied annual base accrues uniformly. For example, Finance Act 2026 raises Remote Gaming Duty on 1 April 2026: FY2025 contains 360 days at 21% and five at 40%. The remote -betting change on 1 April 2027 similarly affects five days of FY2026. +betting change on 1 April 2027 similarly affects five days of FY2026. A reform +dated within a fiscal year is split the same way. The casino incidence proxy +takes one value per model year instead, as described below. Each input supplies a nonnegative, already-attributed dutiable base in pounds. All inputs default to zero. A data provider must allocate operator liabilities @@ -149,6 +151,14 @@ charges, machine games and gaming taxed or exempt under the other duties. The household formula multiplies this base by the empirical assumption `household.consumption.gambling.casino_effective_rate`, initially **0.235487** (23.5487%). It holds that fraction constant from model year 2024 onwards. +Like the other household consumption assumptions, the proxy takes one value +per model year: the value in force on 1 January of the year in which the model +year starts. Unlike a change to a statutory rate, a change to the proxy dated +within a fiscal year is not split by day; it applies to a whole model year +when it is in force on that 1 January. The proxy sits outside `gov`, where the +reform routes read a bare year such as `"2027"` as a calendar year, so a day +split would spread such a reform over two model years. + The approved proxy divides these official observations: | Observation, April 2024 to March 2025 | Amount | Source location | diff --git a/policyengine_uk/parameters/household/consumption/gambling/casino_effective_rate.yaml b/policyengine_uk/parameters/household/consumption/gambling/casino_effective_rate.yaml index f1668ba6cd..f4403879e5 100644 --- a/policyengine_uk/parameters/household/consumption/gambling/casino_effective_rate.yaml +++ b/policyengine_uk/parameters/household/consumption/gambling/casino_effective_rate.yaml @@ -1,4 +1,4 @@ -description: The model attributes household casino Gaming Duty at this empirical fraction of supplied dutiable casino yield. It divides HMRC UK cash Gaming Duty receipts of GBP 165.397 million by provisional Gambling Commission GB casino game yield of GBP 702.36095883367 million for April 2024 to March 2025, rounded to six fractional decimals and held constant after model year 2024. Geography, receipt timing and model-year differences remain unresolved; review and refresh are tracked at https://github.com/PolicyEngine/policyengine-uk/issues/2587. +description: The model attributes household casino Gaming Duty at this empirical fraction of supplied dutiable casino yield. It divides HMRC UK cash Gaming Duty receipts of GBP 165.397 million by provisional Gambling Commission GB casino game yield of GBP 702.36095883367 million for April 2024 to March 2025, rounded to six fractional decimals and held constant after model year 2024. Geography, receipt timing and model-year differences remain unresolved; review and refresh are tracked at https://github.com/PolicyEngine/policyengine-uk/issues/2587. Like the other household consumption assumptions, it takes one value per model year, the value in force on 1 January of the year in which the model year starts; unlike the statutory gambling duty rates, a change dated within a fiscal year is not split by day. values: '2024-01-01': 0.235487 metadata: diff --git a/policyengine_uk/tests/test_gambling_additional_schedules.py b/policyengine_uk/tests/test_gambling_additional_schedules.py index ddfbb06f2a..d4a89b4891 100644 --- a/policyengine_uk/tests/test_gambling_additional_schedules.py +++ b/policyengine_uk/tests/test_gambling_additional_schedules.py @@ -60,6 +60,20 @@ def test_explicit_casino_incidence_reform_changes_household_duty(): assert sim.calculate("gaming_duty", 2024).iloc[0] == pytest.approx(300) +def test_casino_proxy_takes_one_value_per_model_year(): + """FY2026 reads the proxy at 1 January 2026, before a change from 2027.""" + sim = gambling_simulation( + 2026, + { + "household.consumption.gambling.casino_effective_rate": { + "2027-01-01.2027-04-05": 0.3 + } + }, + {"land_based_casino_net_losses": 1_000}, + ) + assert sim.calculate("gaming_duty", 2026).iloc[0] == pytest.approx(235.487) + + def test_operator_band_reform_does_not_automatically_change_household_proxy(): """The empirical household proxy does not model casino band responses.""" sim = gambling_simulation( From d13275d9fa5d174d0fe830feb08f6b784ce583a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Juaristi?= <127882282+juaristi22@users.noreply.github.com> Date: Fri, 23 Oct 2026 11:02:05 +0100 Subject: [PATCH 5/8] Floor gambling duty bases at zero A negative input gave negative duty: -GBP 1,000 of remote gaming losses gave -GBP 400 of Remote Gaming Duty. Each of the fifteen bases is now floored at zero with max_, as the alcohol duty formulas floor their litres, so a negative input gives no duty and does not offset another base. A YAML case per family pins this. Refs #2586 Co-Authored-By: Claude Opus 5.5 --- docs/engineering/gambling-duties.md | 3 ++- .../gov/hmrc/gambling_duty/bingo_duty.yaml | 7 ++++++ .../gov/hmrc/gambling_duty/gaming_duty.yaml | 7 ++++++ .../gambling_duty/general_betting_duty.yaml | 13 +++++++++++ .../gov/hmrc/gambling_duty/lottery_duty.yaml | 7 ++++++ .../gambling_duty/machine_games_duty.yaml | 9 ++++++++ .../hmrc/gambling_duty/pool_betting_duty.yaml | 7 ++++++ .../gambling_duty/remote_gaming_duty.yaml | 7 ++++++ .../gov/hmrc/gambling_duty/bingo_duty.py | 2 +- .../gov/hmrc/gambling_duty/gaming_duty.py | 3 ++- .../gambling_duty/general_betting_duty.py | 22 +++++++++---------- .../gov/hmrc/gambling_duty/lottery_duty.py | 2 +- .../hmrc/gambling_duty/machine_games_duty.py | 6 ++--- .../hmrc/gambling_duty/pool_betting_duty.py | 2 +- .../hmrc/gambling_duty/remote_gaming_duty.py | 2 +- 15 files changed, 78 insertions(+), 21 deletions(-) diff --git a/docs/engineering/gambling-duties.md b/docs/engineering/gambling-duties.md index e0ac979476..58ecbfb7ad 100644 --- a/docs/engineering/gambling-duties.md +++ b/docs/engineering/gambling-duties.md @@ -25,7 +25,8 @@ All inputs default to zero. A data provider must allocate operator liabilities to households, preserving statutory netting, adjustments and exemptions before supplying these bases. A household's raw signed stakes minus winnings do not satisfy this contract. The formulas do not reproduce operator accounting -periods or loss carry-forward and do not validate negative input values. +periods or loss carry-forward. They floor each input at zero, so a negative +input gives no duty and does not offset another base. Positive inputs before 2024 produce default zero because those model years remain outside the implemented coverage. diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/bingo_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/bingo_duty.yaml index 642e9cf5ae..bc5e12bb50 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/bingo_duty.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/bingo_duty.yaml @@ -24,3 +24,10 @@ input: {} output: bingo_duty: 0 + +- name: Case 5, a negative bingo base gives no duty. + period: 2024 + input: + bingo_net_receipts: -1_000 + output: + bingo_duty: 0 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/gaming_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/gaming_duty.yaml index 56ec7a5879..abe16cd9ec 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/gaming_duty.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/gaming_duty.yaml @@ -27,3 +27,10 @@ input: {} output: gaming_duty: 0 + +- name: Case 5, a negative casino base gives no duty. + period: 2024 + input: + land_based_casino_net_losses: -1_000 + output: + gaming_duty: 0 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/general_betting_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/general_betting_duty.yaml index 3afdd88a7a..54b1741ab2 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/general_betting_duty.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/general_betting_duty.yaml @@ -68,3 +68,16 @@ horse_and_dog_pool_betting_net_losses: 36_500 output: general_betting_duty: 25_550 # 9,125 + 3 * 5,475. + +- name: Case 10, negative bases give no duty and do not offset another base. + period: 2027 + input: + remote_uk_horseracing_betting_net_losses: -1_000 + remote_other_betting_net_losses: -1_000 + land_based_fixed_odds_betting_net_losses: 1_000 + horse_and_dog_pool_betting_net_losses: -1_000 + financial_spread_betting_net_losses: -1_000 + other_spread_betting_net_losses: -1_000 + betting_exchange_commission: -1_000 + output: + general_betting_duty: 150 # Only the land-based base: 1,000 * 15%. diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/lottery_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/lottery_duty.yaml index c55010b730..b29dfdfb94 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/lottery_duty.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/lottery_duty.yaml @@ -17,3 +17,10 @@ input: {} output: lottery_duty: 0 + +- name: Case 4, negative National Lottery spending gives no duty. + period: 2024 + input: + national_lottery_ticket_spending: -1_000 + output: + lottery_duty: 0 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/machine_games_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/machine_games_duty.yaml index 9b8cba8bd7..70d2aa8adc 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/machine_games_duty.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/machine_games_duty.yaml @@ -27,3 +27,12 @@ machine_games_higher_rate_net_takings: 3_000 output: machine_games_duty: 1_200 # 50 + 400 + 750. + +- name: Case 5, negative machine bases give no duty and do not offset another class. + period: 2024 + input: + machine_games_lower_rate_net_takings: -1_000 + machine_games_standard_rate_net_takings: -1_000 + machine_games_higher_rate_net_takings: 1_000 + output: + machine_games_duty: 250 # Only the higher-rate base: 1,000 * 25%. diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/pool_betting_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/pool_betting_duty.yaml index 87c132f0ac..0125fedfdc 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/pool_betting_duty.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/pool_betting_duty.yaml @@ -15,3 +15,10 @@ general_betting_duty: 300 pool_betting_duty: 150 gambling_duty: 450 + +- name: Case 3, a negative pool betting base gives no duty. + period: 2024 + input: + pool_betting_net_receipts: -1_000 + output: + pool_betting_duty: 0 diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/remote_gaming_duty.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/remote_gaming_duty.yaml index 009a6df2a3..41319194c6 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/remote_gaming_duty.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/gambling_duty/remote_gaming_duty.yaml @@ -19,3 +19,10 @@ remote_gaming_net_losses: 36_500 output: remote_gaming_duty: 14_600 + +- name: Case 4, a negative remote gaming base gives no duty. + period: 2026 + input: + remote_gaming_net_losses: -1_000 + output: + remote_gaming_duty: 0 # Each base is floored at zero, not charged at -40%. diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/bingo_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/bingo_duty.py index 319bac292d..ae6516ebef 100644 --- a/policyengine_uk/variables/gov/hmrc/gambling_duty/bingo_duty.py +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/bingo_duty.py @@ -14,7 +14,7 @@ class bingo_duty(Variable): ) def formula_2024(household, period, parameters): - receipts = household("bingo_net_receipts", period) + receipts = max_(household("bingo_net_receipts", period), 0) total = 0 for rates, share in fiscal_year_segments( parameters.gov.hmrc.gambling_duty.bingo, period.start.year diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/gaming_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/gaming_duty.py index 2219feaecc..224649e28c 100644 --- a/policyengine_uk/variables/gov/hmrc/gambling_duty/gaming_duty.py +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/gaming_duty.py @@ -14,4 +14,5 @@ class gaming_duty(Variable): def formula_2024(household, period, parameters): rate = parameters(period).household.consumption.gambling.casino_effective_rate - return household("land_based_casino_net_losses", period) * rate + losses = max_(household("land_based_casino_net_losses", period), 0) + return losses * rate diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/general_betting_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/general_betting_duty.py index fe6ad16b9e..a45c5f46a7 100644 --- a/policyengine_uk/variables/gov/hmrc/gambling_duty/general_betting_duty.py +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/general_betting_duty.py @@ -14,19 +14,17 @@ class general_betting_duty(Variable): ) def formula_2024(household, period, parameters): - fixed_odds = add( - household, - period, - [ - "remote_uk_horseracing_betting_net_losses", - "land_based_fixed_odds_betting_net_losses", - "horse_and_dog_pool_betting_net_losses", - ], + fixed_odds = ( + max_(household("remote_uk_horseracing_betting_net_losses", period), 0) + + max_(household("land_based_fixed_odds_betting_net_losses", period), 0) + + max_(household("horse_and_dog_pool_betting_net_losses", period), 0) ) - remote = household("remote_other_betting_net_losses", period) - financial_spread = household("financial_spread_betting_net_losses", period) - other_spread = household("other_spread_betting_net_losses", period) - exchange = household("betting_exchange_commission", period) + remote = max_(household("remote_other_betting_net_losses", period), 0) + financial_spread = max_( + household("financial_spread_betting_net_losses", period), 0 + ) + other_spread = max_(household("other_spread_betting_net_losses", period), 0) + exchange = max_(household("betting_exchange_commission", period), 0) total = 0 for rates, share in fiscal_year_segments( parameters.gov.hmrc.gambling_duty.general_betting.rates, period.start.year diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/lottery_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/lottery_duty.py index 66b759da75..eb442993af 100644 --- a/policyengine_uk/variables/gov/hmrc/gambling_duty/lottery_duty.py +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/lottery_duty.py @@ -11,7 +11,7 @@ class lottery_duty(Variable): reference = "https://www.legislation.gov.uk/ukpga/1993/34/section/25" def formula_2024(household, period, parameters): - spending = household("national_lottery_ticket_spending", period) + spending = max_(household("national_lottery_ticket_spending", period), 0) total = 0 for rates, share in fiscal_year_segments( parameters.gov.hmrc.gambling_duty.lottery, period.start.year diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/machine_games_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/machine_games_duty.py index f96b8d6382..7292d7fc74 100644 --- a/policyengine_uk/variables/gov/hmrc/gambling_duty/machine_games_duty.py +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/machine_games_duty.py @@ -11,9 +11,9 @@ class machine_games_duty(Variable): reference = "https://www.legislation.gov.uk/ukpga/2012/14/schedule/24" def formula_2024(household, period, parameters): - lower = household("machine_games_lower_rate_net_takings", period) - standard = household("machine_games_standard_rate_net_takings", period) - higher = household("machine_games_higher_rate_net_takings", period) + lower = max_(household("machine_games_lower_rate_net_takings", period), 0) + standard = max_(household("machine_games_standard_rate_net_takings", period), 0) + higher = max_(household("machine_games_higher_rate_net_takings", period), 0) total = 0 for rates, share in fiscal_year_segments( parameters.gov.hmrc.gambling_duty.machine_games.rates, period.start.year diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/pool_betting_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/pool_betting_duty.py index f18efdc335..45cca174b1 100644 --- a/policyengine_uk/variables/gov/hmrc/gambling_duty/pool_betting_duty.py +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/pool_betting_duty.py @@ -11,7 +11,7 @@ class pool_betting_duty(Variable): reference = "https://www.legislation.gov.uk/ukpga/2014/26/section/144" def formula_2024(household, period, parameters): - receipts = household("pool_betting_net_receipts", period) + receipts = max_(household("pool_betting_net_receipts", period), 0) total = 0 for rates, share in fiscal_year_segments( parameters.gov.hmrc.gambling_duty.pool_betting, period.start.year diff --git a/policyengine_uk/variables/gov/hmrc/gambling_duty/remote_gaming_duty.py b/policyengine_uk/variables/gov/hmrc/gambling_duty/remote_gaming_duty.py index bcb8d82ced..b1ed51d749 100644 --- a/policyengine_uk/variables/gov/hmrc/gambling_duty/remote_gaming_duty.py +++ b/policyengine_uk/variables/gov/hmrc/gambling_duty/remote_gaming_duty.py @@ -14,7 +14,7 @@ class remote_gaming_duty(Variable): ) def formula_2024(household, period, parameters): - losses = household("remote_gaming_net_losses", period) + losses = max_(household("remote_gaming_net_losses", period), 0) total = 0 for rates, share in fiscal_year_segments( parameters.gov.hmrc.gambling_duty.remote_gaming, period.start.year From 9878ca60398f4067d2e023728f62603d2afbf024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Juaristi?= <127882282+juaristi22@users.noreply.github.com> Date: Fri, 23 Oct 2026 11:02:45 +0100 Subject: [PATCH 6/8] Say household calculations do not use the casino band table The six-month Gaming Duty band table is an operator catalogue that no formula reads, so a band reform in the app leaves household duty unchanged. Its description now says so and points to the household incidence proxy. Its metadata said period: year, but the thresholds apply to a six-month accounting period; the period key is dropped, as for the per-transaction SDLT, LBTT and LTT bands, and the description states the six-month period. Refs #2586 Co-Authored-By: Claude Opus 5.5 --- .../gov/hmrc/gambling_duty/gaming/normal_six_month_bands.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/policyengine_uk/parameters/gov/hmrc/gambling_duty/gaming/normal_six_month_bands.yaml b/policyengine_uk/parameters/gov/hmrc/gambling_duty/gaming/normal_six_month_bands.yaml index 86b12183bd..c97ddaf56e 100644 --- a/policyengine_uk/parameters/gov/hmrc/gambling_duty/gaming/normal_six_month_bands.yaml +++ b/policyengine_uk/parameters/gov/hmrc/gambling_duty/gaming/normal_six_month_bands.yaml @@ -1,4 +1,4 @@ -description: The UK applies these marginal Gaming Duty rates to gross gaming yield from registered casino premises in a normal six-month accounting period. This operator catalogue excludes unregistered gaming and alternative-period adjustments; household incidence uses a separate empirical assumption. +description: The UK applies these marginal Gaming Duty rates to gross gaming yield from registered casino premises in a normal six-month accounting period, with thresholds measured over that six-month period. This operator catalogue excludes unregistered gaming and alternative-period adjustments. Household calculations do not use it, so changing these bands leaves household casino duty unchanged; see household.consumption.gambling.casino_effective_rate. brackets: - threshold: '2022-04-01': 0 @@ -24,7 +24,6 @@ metadata: type: marginal_rate unit: /1 threshold_unit: currency-GBP - period: year label: normal six-month casino gaming duty bands reference: - title: Finance Act 1997, section 11(2), as at 6 April 2024 From 326c0fa262b3fd0a131153dcc569eb905e1ead16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Juaristi?= <127882282+juaristi22@users.noreply.github.com> Date: Fri, 23 Oct 2026 11:03:25 +0100 Subject: [PATCH 7/8] Document the Gaming Duty band freeze for 2026-27 The government froze the casino Gaming Duty bands for 2026-27 with the usual RPI uprating thereafter (OBR November 2025 EFO paragraph 3.39), so bands for accounting periods from 1 April 2027 are expected to change but are not yet law. The docs now say so, and that verified_end_year 2027 in programs.yaml is deliberate because household outputs use the incidence proxy rather than the bands. Refs #2586 Co-Authored-By: Claude Opus 5.5 --- docs/engineering/gambling-duties.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/engineering/gambling-duties.md b/docs/engineering/gambling-duties.md index 58ecbfb7ad..2afa9c5f9b 100644 --- a/docs/engineering/gambling-duties.md +++ b/docs/engineering/gambling-duties.md @@ -194,6 +194,16 @@ registered-premises, normal six-month schedule: 15% on the first £2,686,000, alternative accounting-period adjustments. Household annual losses never enter those bands. +The bands have not changed since 1 April 2022: +[HMRC's rates page](https://www.gov.uk/government/publications/rates-and-allowance-excise-duty-gambling-duty/excise-duty-gambling-duty-rates) +gives the same bands for accounting periods beginning on or after 1 April +2026. The government froze them for 2026-27, "with the usual RPI uprating +thereafter" ([OBR November 2025 Economic and Fiscal Outlook, paragraph 3.39](https://obr.uk/docs/dlm_uploads/OBR_Economic_and_fiscal_outlook_November_2025.pdf#page=85)). +Bands for accounting periods from 1 April 2027 are therefore expected to +change but are not yet law, and the catalogue does not anticipate them. The +programme registry's `verified_end_year: 2027` is deliberate: household +outputs use the proxy, not the bands. + Changing the operator catalogue alone leaves the household proxy unchanged. Scoring a casino band reform requires operator yield distributions and an incidence mapping, or an explicitly supplied alternative effective fraction. From 1bad8f769847b99067e6831f5d13e404f90a978c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mar=C3=ADa=20Juaristi?= <127882282+juaristi22@users.noreply.github.com> Date: Fri, 23 Oct 2026 11:04:09 +0100 Subject: [PATCH 8/8] Record HMRC's 2024-25 gambling receipts as the reconciliation target The docs now give HMRC's 2024-25 UK betting and gaming receipts by duty, GBP 3,616.284 million in total, with their cells in Table_1_BG_receipts of the July 2026 tables the casino proxy already cites. HMRC's commentary gives the same rounded figures for General Betting Duty, Pool Betting Duty and Lottery Duty. Refs #2586 Co-Authored-By: Claude Opus 5.5 --- docs/engineering/gambling-duties.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/engineering/gambling-duties.md b/docs/engineering/gambling-duties.md index 2afa9c5f9b..ba66d26751 100644 --- a/docs/engineering/gambling-duties.md +++ b/docs/engineering/gambling-duties.md @@ -237,6 +237,29 @@ totals with HMRC receipts using compatible periods and population coverage. The formulas make no allocation from `recreation_consumption`. Default-zero inputs do not demonstrate upstream data adoption or population coverage. +The reconciliation target is HMRC's UK receipts for April 2024 to March 2025, +the year of the casino proxy: + +| Duty | 2024-25 receipts | Cell | +| --- | ---: | --- | +| General Betting Duty | £713.858 million | `B45` | +| Pool Betting Duty | £8.125 million | `C45` | +| Remote Gaming Duty | £1,162.539 million | `F45` | +| Machine Games Duty | £609.245 million | `I45` | +| Gaming Duty | £165.397 million | `E45` | +| Bingo Duty | £25.233 million | `H45` | +| Lottery Duty | £931.887 million | `J45` | +| Total | £3,616.284 million | `K45` | + +The cells are in `Table_1_BG_receipts` of the +[HMRC July 2026 tables](https://assets.publishing.service.gov.uk/media/6ab54fd9fe72ed1e2b02f06b/2026_Jul_Bet_and_Gam_Tab.ods), +the file the casino proxy cites, reported in £000. The commentary on HMRC's +[statistics page](https://www.gov.uk/government/statistics/uk-betting-and-gaming-statistics) +gives the same General Betting Duty, Pool Betting Duty and Lottery Duty +figures, rounded. These are cash receipts for a 1 April to 31 March year, so a +reconciliation must handle the timing, period and coverage differences +described for the casino proxy. + The three machine tax-class bases cannot score a betting-shop-only reform without joint tax-class-by-premises yield data. Premises shares alone do not identify that allocation, and machine counts do not provide yield weights.