Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/high-value-council-tax-surcharge.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added the England-only High Value Council Tax Surcharge from April 2028, including its 2026-price valuation bands and CPI uprating from 2029-30 onward.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
description: High Value Council Tax Surcharge on owners of residential property in England worth £2 million or more in 2026 prices.

brackets:
- threshold:
2028-01-01: 0
amount:
2028-01-01: 0
- threshold:
2028-01-01: 2_000_000
amount:
2028-01-01: 2_500
- threshold:
2028-01-01: 2_500_000
amount:
2028-01-01: 3_500
- threshold:
2028-01-01: 3_500_000
amount:
2028-01-01: 5_000
- threshold:
2028-01-01: 5_000_000
amount:
2028-01-01: 7_500

metadata:
type: single_amount
threshold_unit: currency-GBP
amount_unit: currency-GBP
uprating: gov.benefit_uprating_cpi
label: High Value Council Tax Surcharge
reference:
- title: High Value Council Tax Surcharge
href: https://www.gov.uk/government/publications/high-value-council-tax-surcharge/high-value-council-tax-surcharge
- title: Budget 2025
href: https://www.gov.uk/government/publications/budget-2025-document/budget-2025-html
70 changes: 70 additions & 0 deletions policyengine_uk/tests/test_high_value_council_tax_surcharge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import pytest

from policyengine_uk import Simulation
from policyengine_uk.variables.gov.gov_tax import gov_tax
from policyengine_uk.variables.gov.hmrc.household_tax import household_tax


def _homeowner(main_residence_value: float, region: str = "LONDON") -> dict:
return {
"people": {
"person": {
"age": {2026: 45, 2028: 47, 2029: 48},
"employment_income": {2028: 0, 2029: 0},
}
},
"benunits": {"benunit": {"members": ["person"]}},
"households": {
"household": {
"members": ["person"],
"region": {2028: region, 2029: region},
"main_residence_value": {2026: main_residence_value},
}
},
}


def test_high_value_council_tax_surcharge_starts_in_2028():
sim = Simulation(situation=_homeowner(2_400_000))

assert sim.calculate("high_value_council_tax_surcharge", 2027)[0] == 0
assert sim.calculate("high_value_council_tax_surcharge", 2028)[0] == 2_500


def test_high_value_council_tax_surcharge_is_uprated_from_2029():
sim = Simulation(situation=_homeowner(2_400_000))
current = sim.tax_benefit_system.parameters("2029")
previous = sim.tax_benefit_system.parameters("2028")
expected_2029 = (
2_500
* float(current.gov.benefit_uprating_cpi)
/ float(previous.gov.benefit_uprating_cpi)
)

assert sim.calculate("high_value_council_tax_surcharge", 2029)[0] == pytest.approx(
expected_2029
)


def test_high_value_council_tax_surcharge_flows_into_tax_aggregates():
sim = Simulation(situation=_homeowner(2_400_000))

other_household_taxes = sum(
sim.calculate(variable, 2028)[0]
for variable in household_tax.adds
if variable != "high_value_council_tax_surcharge"
)
other_government_taxes = sum(
sim.calculate(variable, 2028)[0]
for variable in gov_tax.adds
if variable != "high_value_council_tax_surcharge"
)

assert sim.calculate("household_tax", 2028)[0] - other_household_taxes == 2_500
assert sim.calculate("gov_tax", 2028)[0] - other_government_taxes == 2_500


def test_high_value_council_tax_surcharge_only_applies_in_england():
sim = Simulation(situation=_homeowner(6_000_000, region="SCOTLAND"))

assert sim.calculate("high_value_council_tax_surcharge", 2028)[0] == 0
1 change: 1 addition & 0 deletions policyengine_uk/variables/gov/gov_tax.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class gov_tax(Variable):
"corporate_sdlt",
"business_rates",
"council_tax",
"high_value_council_tax_surcharge",
"domestic_rates",
"fuel_duty",
"tv_licence",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from policyengine_uk.model_api import *


class high_value_council_tax_surcharge(Variable):
value_type = float
entity = Household
label = "High Value Council Tax Surcharge"
documentation = (
"Additional annual surcharge on owners of residential property in England "
"worth at least £2 million in 2026 prices."
)
definition_period = YEAR
unit = GBP

def formula(household, period, parameters):
if period.start.year < 2028:
return 0

country = household("country", period)
in_england = country == country.possible_values.ENGLAND

p = parameters(period)
property_value = household("main_residence_value", period)
current_index = p.gov.economic_assumptions.indices.obr.per_capita.gdp
baseline_index = parameters(
"2026"
).gov.economic_assumptions.indices.obr.per_capita.gdp
value_2026_prices = property_value / (current_index / baseline_index)

surcharge = p.gov.hmrc.council_tax.high_value_surcharge.amount.calc(
value_2026_prices
)
return where(in_england, surcharge, 0)
1 change: 1 addition & 0 deletions policyengine_uk/variables/gov/hmrc/household_tax.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class household_tax(Variable):
"corporate_sdlt",
"business_rates",
"council_tax",
"high_value_council_tax_surcharge",
"domestic_rates",
"fuel_duty",
"tv_licence",
Expand Down
Loading