diff --git a/changelog.d/fuel-duty-litre-rate.md b/changelog.d/fuel-duty-litre-rate.md new file mode 100644 index 000000000..bfa616066 --- /dev/null +++ b/changelog.d/fuel-duty-litre-rate.md @@ -0,0 +1 @@ +- Compute fuel duty directly as calibrated petrol and diesel litres multiplied by the statutory duty rate, with fuel-spending proxies uprated by road-fuel litres and pump prices. diff --git a/policyengine_uk/data/uprating_indices.yaml b/policyengine_uk/data/uprating_indices.yaml index a8a0b887b..7686f2946 100644 --- a/policyengine_uk/data/uprating_indices.yaml +++ b/policyengine_uk/data/uprating_indices.yaml @@ -48,8 +48,9 @@ gov.economic_assumptions.yoy_growth.obr.consumer_price_index: - universal_credit_reported - winter_fuel_allowance_reported - working_tax_credit_reported -gov.economic_assumptions.yoy_growth.obr.road_fuel_volume: +gov.economic_assumptions.yoy_growth.obr.diesel_spending_litre_proxy: - diesel_spending +gov.economic_assumptions.yoy_growth.obr.petrol_spending_litre_proxy: - petrol_spending gov.economic_assumptions.yoy_growth.obr.mortgage_interest: - mortgage_interest_repayment diff --git a/policyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml b/policyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml index 459c9ba4a..19284351d 100644 --- a/policyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml +++ b/policyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml @@ -322,6 +322,140 @@ obr: - title: OBR fuel-duty supplementary release, receipts by vehicle type href: https://obr.uk/docs/dlm_uploads/Fuel-duty-supplementary-release_receipts-by-vehicle-type.pdf + petrol_spending_litre_proxy: + description: Unleaded-petrol spending proxy growth that preserves weighted road-fuel litres after dividing by model pump prices. + values: + 2021-01-01: 0.09966723889828133 + 2022-01-01: 0.47125942029399615 + 2023-01-01: 0.09943596417596279 + 2024-01-01: -0.14810324567590538 + 2025-01-01: -0.0269132439679719 + 2026-01-01: -0.018120813771079436 + 2027-01-01: -0.027765858532455168 + 2028-01-01: -0.030878786058031737 + 2029-01-01: -0.04149818363243518 + 2030-01-01: -0.056482738187697334 + 2031-01-01: -0.00428158916658361 + 2032-01-01: -0.004182433778131878 + 2033-01-01: -0.004083258639577703 + 2034-01-01: -0.003984063745019917 + 2035-01-01: -0.003884849088554687 + 2036-01-01: -0.0037856146642757382 + 2037-01-01: -0.003686360466274796 + 2038-01-01: -0.0035870864886409226 + 2039-01-01: -0.0034877927254609586 + 2040-01-01: -0.0034877927254609586 + 2041-01-01: -0.003388479170819303 + 2042-01-01: -0.003388479170819303 + 2043-01-01: -0.0032891458187980227 + 2044-01-01: -0.0031897926634769647 + 2045-01-01: -0.0031897926634769647 + 2046-01-01: -0.003090419698933422 + 2047-01-01: -0.0029910269192421346 + 2048-01-01: -0.0028916143184762877 + 2049-01-01: -0.002792181890705958 + 2050-01-01: -0.002692729629998891 + 2051-01-01: -0.0024937655860348684 + 2052-01-01: -0.002394253790901746 + 2053-01-01: -0.0021951706246258196 + 2054-01-01: -0.0020955992415926383 + 2055-01-01: -0.001996007984031989 + 2056-01-01: -0.0018963968459926317 + 2057-01-01: -0.001796765821521329 + 2058-01-01: -0.001697114904662067 + 2059-01-01: -0.0015974440894569453 + 2060-01-01: -0.0015974440894569453 + 2061-01-01: -0.0015974440894569453 + 2062-01-01: -0.0014977533699451762 + 2063-01-01: -0.0014977533699451762 + 2064-01-01: -0.0014977533699451762 + 2065-01-01: -0.0014977533699451762 + 2066-01-01: -0.0014977533699451762 + 2067-01-01: -0.0014977533699451762 + 2068-01-01: -0.0014977533699451762 + 2069-01-01: -0.0014977533699451762 + 2070-01-01: -0.0014977533699451762 + 2071-01-01: -0.0014977533699451762 + 2072-01-01: -0.0014977533699451762 + 2073-01-01: -0.0013980427401638629 + metadata: + unit: /1 + label: Petrol spending litre-proxy growth + reference: + - title: HMRC Hydrocarbon Oils Bulletin + href: https://www.gov.uk/government/statistics/hydrocarbon-oils-bulletin + - title: OBR EFO March 2026 (fuel-duty receipts forecast) + href: https://obr.uk/efo/economic-and-fiscal-outlook-march-2026/ + - title: RAC latest average petrol price + href: https://www.rac.co.uk/drive/advice/fuel-watch/ + + diesel_spending_litre_proxy: + description: Diesel spending proxy growth that preserves weighted road-fuel litres after dividing by model pump prices. + values: + 2021-01-01: 0.13390420538515913 + 2022-01-01: 0.3702255435452164 + 2023-01-01: -0.01858407316867594 + 2024-01-01: 0.03164670292538285 + 2025-01-01: -0.0269132439679719 + 2026-01-01: -0.018120813771079436 + 2027-01-01: -0.027765858532455168 + 2028-01-01: -0.030878786058031737 + 2029-01-01: -0.04149818363243518 + 2030-01-01: -0.056482738187697334 + 2031-01-01: -0.00428158916658361 + 2032-01-01: -0.004182433778131878 + 2033-01-01: -0.004083258639577703 + 2034-01-01: -0.003984063745019917 + 2035-01-01: -0.003884849088554687 + 2036-01-01: -0.0037856146642757382 + 2037-01-01: -0.003686360466274796 + 2038-01-01: -0.0035870864886409226 + 2039-01-01: -0.0034877927254609586 + 2040-01-01: -0.0034877927254609586 + 2041-01-01: -0.003388479170819303 + 2042-01-01: -0.003388479170819303 + 2043-01-01: -0.0032891458187980227 + 2044-01-01: -0.0031897926634769647 + 2045-01-01: -0.0031897926634769647 + 2046-01-01: -0.003090419698933422 + 2047-01-01: -0.0029910269192421346 + 2048-01-01: -0.0028916143184762877 + 2049-01-01: -0.002792181890705958 + 2050-01-01: -0.002692729629998891 + 2051-01-01: -0.0024937655860348684 + 2052-01-01: -0.002394253790901746 + 2053-01-01: -0.0021951706246258196 + 2054-01-01: -0.0020955992415926383 + 2055-01-01: -0.001996007984031989 + 2056-01-01: -0.0018963968459926317 + 2057-01-01: -0.001796765821521329 + 2058-01-01: -0.001697114904662067 + 2059-01-01: -0.0015974440894569453 + 2060-01-01: -0.0015974440894569453 + 2061-01-01: -0.0015974440894569453 + 2062-01-01: -0.0014977533699451762 + 2063-01-01: -0.0014977533699451762 + 2064-01-01: -0.0014977533699451762 + 2065-01-01: -0.0014977533699451762 + 2066-01-01: -0.0014977533699451762 + 2067-01-01: -0.0014977533699451762 + 2068-01-01: -0.0014977533699451762 + 2069-01-01: -0.0014977533699451762 + 2070-01-01: -0.0014977533699451762 + 2071-01-01: -0.0014977533699451762 + 2072-01-01: -0.0014977533699451762 + 2073-01-01: -0.0013980427401638629 + metadata: + unit: /1 + label: Diesel spending litre-proxy growth + reference: + - title: HMRC Hydrocarbon Oils Bulletin + href: https://www.gov.uk/government/statistics/hydrocarbon-oils-bulletin + - title: OBR EFO March 2026 (fuel-duty receipts forecast) + href: https://obr.uk/efo/economic-and-fiscal-outlook-march-2026/ + - title: RAC latest average diesel price + href: https://www.rac.co.uk/drive/advice/fuel-watch/ + consumer_price_index_ahc: description: Consumer price index year-on-year growth, modified to remove housing costs. values: diff --git a/policyengine_uk/tests/test_fuel_duty_litres.py b/policyengine_uk/tests/test_fuel_duty_litres.py new file mode 100644 index 000000000..3a1f8be8c --- /dev/null +++ b/policyengine_uk/tests/test_fuel_duty_litres.py @@ -0,0 +1,38 @@ +import pytest + +from policyengine_uk import Microsimulation +from policyengine_uk.system import system + + +def test_fuel_duty_is_litres_times_statutory_rate(): + year = 2025 + situation = { + "people": { + "adult": { + "age": {year: 35}, + }, + }, + "benunits": { + "benunit": { + "members": ["adult"], + }, + }, + "households": { + "household": { + "members": ["adult"], + "region": {year: "LONDON"}, + "petrol_spending": {year: 2_000.0}, + "diesel_spending": {year: 1_000.0}, + }, + }, + } + simulation = Microsimulation(situation=situation) + + fuel_duty = simulation.calculate("fuel_duty", year).values[0] + litres = ( + simulation.calculate("petrol_litres", year).values[0] + + simulation.calculate("diesel_litres", year).values[0] + ) + rate = system.parameters.gov.hmrc.fuel_duty.petrol_and_diesel(year) + + assert fuel_duty == pytest.approx(litres * rate) diff --git a/policyengine_uk/tests/test_road_fuel_volume_uprating.py b/policyengine_uk/tests/test_road_fuel_volume_uprating.py index 04d877ccf..9010a0607 100644 --- a/policyengine_uk/tests/test_road_fuel_volume_uprating.py +++ b/policyengine_uk/tests/test_road_fuel_volume_uprating.py @@ -6,15 +6,26 @@ from policyengine_uk.system import system -def test_petrol_and_diesel_spending_use_road_fuel_volume_not_cpi(): +def test_petrol_and_diesel_spending_preserve_road_fuel_litres_not_cpi(): parameters = system.parameters road_fuel_volume = ( parameters.gov.economic_assumptions.yoy_growth.obr.road_fuel_volume ) + petrol_proxy = ( + parameters.gov.economic_assumptions.yoy_growth.obr.petrol_spending_litre_proxy + ) + diesel_proxy = ( + parameters.gov.economic_assumptions.yoy_growth.obr.diesel_spending_litre_proxy + ) + population = parameters.gov.economic_assumptions.yoy_growth.ons.population + petrol_price = parameters.household.consumption.fuel.prices.petrol + diesel_price = parameters.household.consumption.fuel.prices.diesel cpi = parameters.gov.economic_assumptions.yoy_growth.obr.consumer_price_index - assert road_fuel_volume(2027) < 0 - assert cpi(2027) > 0 + assert road_fuel_volume(2024) < 0 + assert cpi(2024) > 0 + assert petrol_proxy(2024) != road_fuel_volume(2024) + assert diesel_proxy(2024) != road_fuel_volume(2024) dataset = UKSingleYearDataset( person=pd.DataFrame( @@ -33,25 +44,66 @@ def test_petrol_and_diesel_spending_use_road_fuel_volume_not_cpi(): "tenure_type": ["OWNED_OUTRIGHT"], "council_tax": [1_500.0], "rent": [0.0], - "petrol_spending": [1_000.0], - "diesel_spending": [2_000.0], + "household_weight": [1.0], + "petrol_spending": [1_000.0 * petrol_price(2023)], + "diesel_spending": [2_000.0 * diesel_price(2023)], } ), - fiscal_year=2026, + fiscal_year=2023, ) extended = extend_single_year_dataset( dataset, tax_benefit_system_parameters=parameters, - end_year=2027, + end_year=2035, + ) + household_2024 = extended[2024].household + + assert household_2024["petrol_spending"].iloc[0] == pytest.approx( + 1_000 * petrol_price(2023) * (1 + petrol_proxy(2024)) ) - household_2027 = extended[2027].household + assert household_2024["diesel_spending"].iloc[0] == pytest.approx( + 2_000 * diesel_price(2023) * (1 + diesel_proxy(2024)) + ) + assert household_2024["household_weight"].iloc[0] == pytest.approx( + 1 + population(2024) + ) + assert ( + household_2024["petrol_spending"].iloc[0] + / petrol_price(2024) + * household_2024["household_weight"].iloc[0] + ) == pytest.approx(1_000 * (1 + road_fuel_volume(2024))) + assert ( + household_2024["diesel_spending"].iloc[0] + / diesel_price(2024) + * household_2024["household_weight"].iloc[0] + ) == pytest.approx(2_000 * (1 + road_fuel_volume(2024))) + + household_2034 = extended[2034].household + household_2035 = extended[2035].household + + def weighted_litres(household, spending_variable, price_parameter, year): + return ( + household[spending_variable].iloc[0] + / price_parameter(year) + * household["household_weight"].iloc[0] + ) - assert household_2027["petrol_spending"].iloc[0] == pytest.approx( - 1_000 * (1 + road_fuel_volume(2027)) + assert weighted_litres( + household_2035, + "petrol_spending", + petrol_price, + 2035, + ) == pytest.approx( + weighted_litres(household_2034, "petrol_spending", petrol_price, 2034) + * (1 + road_fuel_volume(2035)) ) - assert household_2027["diesel_spending"].iloc[0] == pytest.approx( - 2_000 * (1 + road_fuel_volume(2027)) + assert weighted_litres( + household_2035, + "diesel_spending", + diesel_price, + 2035, + ) == pytest.approx( + weighted_litres(household_2034, "diesel_spending", diesel_price, 2034) + * (1 + road_fuel_volume(2035)) ) - assert household_2027["petrol_spending"].iloc[0] < 1_000 - assert household_2027["diesel_spending"].iloc[0] < 2_000 diff --git a/policyengine_uk/variables/gov/hmrc/fuel_duty/fuel_duty.py b/policyengine_uk/variables/gov/hmrc/fuel_duty/fuel_duty.py index 8d07d015e..a34e4b1b5 100644 --- a/policyengine_uk/variables/gov/hmrc/fuel_duty/fuel_duty.py +++ b/policyengine_uk/variables/gov/hmrc/fuel_duty/fuel_duty.py @@ -1,8 +1,5 @@ from policyengine_uk.model_api import * -STATUTORY_CONSUMER_INCIDENCE = 0.5 -ECONOMIC_CONSUMER_INCIDENCE = 1 - class fuel_duty(Variable): label = "Fuel duty (cars only)" @@ -15,9 +12,4 @@ def formula(household, period, parameters): fd = parameters(period).gov.hmrc.fuel_duty petrol_litres = household("petrol_litres", period.this_year) / MONTHS_IN_YEAR diesel_litres = household("diesel_litres", period.this_year) / MONTHS_IN_YEAR - return ( - fd.petrol_and_diesel - * (petrol_litres + diesel_litres) - / STATUTORY_CONSUMER_INCIDENCE - * ECONOMIC_CONSUMER_INCIDENCE - ) + return fd.petrol_and_diesel * (petrol_litres + diesel_litres) diff --git a/policyengine_uk/variables/household/consumption/diesel_litres.py b/policyengine_uk/variables/household/consumption/diesel_litres.py index f4b0efefc..a6a0d71d8 100644 --- a/policyengine_uk/variables/household/consumption/diesel_litres.py +++ b/policyengine_uk/variables/household/consumption/diesel_litres.py @@ -7,7 +7,7 @@ class diesel_litres(Variable): entity = Household definition_period = YEAR value_type = float - unit = GBP + unit = "litre" def formula(household, period, parameters): return household("diesel_spending", period) / household("diesel_price", period) diff --git a/policyengine_uk/variables/household/consumption/petrol_litres.py b/policyengine_uk/variables/household/consumption/petrol_litres.py index 2effd42d3..1b59d9141 100644 --- a/policyengine_uk/variables/household/consumption/petrol_litres.py +++ b/policyengine_uk/variables/household/consumption/petrol_litres.py @@ -7,7 +7,7 @@ class petrol_litres(Variable): entity = Household definition_period = YEAR value_type = float - unit = GBP + unit = "litre" def formula(household, period, parameters): return household("petrol_spending", period) / household("petrol_price", period) diff --git a/policyengine_uk/variables/input/consumption/diesel_spending.py b/policyengine_uk/variables/input/consumption/diesel_spending.py index e4b816840..ac7402f8e 100644 --- a/policyengine_uk/variables/input/consumption/diesel_spending.py +++ b/policyengine_uk/variables/input/consumption/diesel_spending.py @@ -10,4 +10,4 @@ class diesel_spending(Variable): value_type = float unit = GBP quantity_type = FLOW - uprating = "gov.economic_assumptions.indices.obr.road_fuel_volume" + uprating = "gov.economic_assumptions.indices.obr.diesel_spending_litre_proxy" diff --git a/policyengine_uk/variables/input/consumption/petrol_spending.py b/policyengine_uk/variables/input/consumption/petrol_spending.py index bd099183e..1cdb566b9 100644 --- a/policyengine_uk/variables/input/consumption/petrol_spending.py +++ b/policyengine_uk/variables/input/consumption/petrol_spending.py @@ -10,4 +10,4 @@ class petrol_spending(Variable): value_type = float unit = GBP quantity_type = FLOW - uprating = "gov.economic_assumptions.indices.obr.road_fuel_volume" + uprating = "gov.economic_assumptions.indices.obr.petrol_spending_litre_proxy"