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/1114-indirect-tax-page-corrections.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Corrected the indirect tax effects planning page (`docs/book/policy/indirect-tax-effects-plan.md`): UKMOD's TCO technical note does not give the 0.8 consumption elasticity reported in #1114, labour supply responses run through two separate routes, fuel duty also reads LPG and natural gas inputs, and a formula on a consumption input would not change values supplied by a dataset.
75 changes: 47 additions & 28 deletions docs/book/policy/indirect-tax-effects-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,38 @@ Right now PolicyEngine UK computes:

1. **Direct effect**: the full structural change to income tax, NI, UC
and so on under the reform.
2. **Behavioural response on labour supply**: modelled with the
2. **Behavioural response on labour supply**: available through two
separate routes. `employment_income_behavioral_response` applies the
substitution and income elasticities under
`gov.simulation.labour_supply_responses` (`policyengine_uk/dynamics/labour_supply.py`,
`employment_income_behavioral_response`).
`gov.simulation.labour_supply_responses` (both 0 by default).
Separately, calling `Simulation.apply_dynamics` on a simulation with a
baseline runs `apply_labour_supply_responses`
(`policyengine_uk/dynamics/labour_supply.py`), which applies the OBR's
elasticities by demographic group when
`gov.dynamic.obr_labour_supply_assumptions` is true, as it is by
default.
3. **Indirect-tax response**: *not modelled*. Households' consumption
bundles are read from the dataset and don't shift in response to
income changes.

#1114 reports that UKMOD handles this in its indirect tax (TCO) module with
an aggregate **consumption elasticity of 0.8**: a 1% rise in net income gives a 0.8% rise in
consumption, which then flows through VAT and excise revenue. That figure
has not yet been checked against UKMOD's own documentation, so it needs a
primary source before it is used as a default.
#1114 suggests that UKMOD handles this in its indirect taxation (TCO)
extension with a consumption elasticity of 0.8, citing only a ChatGPT
conversation. UKMOD's own
[TCO technical note](https://www.microsimulation.ac.uk/wp-content/uploads/2026/01/TCO-extension-v3.pdf)
(revised October 2025) does not give 0.8. It describes three alternative
assumptions: constant income shares (an income elasticity of +1 for every
commodity), constant expenditure shares (nominal savings held fixed, so the
whole change in disposable income goes to spending) and constant quantities
(an income elasticity of 0). Any default therefore needs another primary
source.

## Scope

### Phase 1: household-level consumption response

- New parameter `gov/simulation/indirect_tax_response/consumption_elasticity.yaml`,
defaulting to 0 (no response) until a sourced value such as UKMOD's is
confirmed and cited in its metadata.
defaulting to 0 (no response) until a sourced value is confirmed and
cited in its metadata.
- New variable `consumption_response_factor` (Household, YEAR) =
`1 + elasticity * (net_income_change / baseline_net_income)`.
- Apply the factor to the twelve COICOP categories that `consumption`
Expand All @@ -51,7 +62,8 @@ primary source before it is used as a default.
`reduced_rate_vat_consumption`), so it picks up the change.
- The duties do not read those categories. Fuel duty reads
`petrol_litres` and `diesel_litres`, which divide the `petrol_spending`
and `diesel_spending` inputs by pump prices. Alcohol duty reads the
and `diesel_spending` inputs by pump prices, and the `lpg_kg` and
`natural_gas_kg` inputs. Alcohol duty reads the
`*_litres` inputs under `variables/input/consumption/alcohol/`, and
tobacco duty reads `cigarettes_per_week` and the `*_grams_per_week`
inputs under `variables/input/consumption/tobacco/`. The same factor
Expand All @@ -60,10 +72,11 @@ primary source before it is used as a default.
### Phase 2: heterogeneity by category

- A single economy-wide elasticity hides that some categories (food
staples, domestic energy) are nearly income-inelastic while others
(recreation, restaurants) are not. Replace the single
staples, domestic energy) are less income-elastic than others
(recreation, restaurants). Replace the single
`consumption_elasticity.yaml` with a per-category set, calibrated to
ONS *Living Costs and Food Survey* income-elasticity estimates.
income elasticities estimated from the ONS *Living Costs and Food
Survey*.

### Phase 3: incidence link

Expand Down Expand Up @@ -93,10 +106,11 @@ primary source before it is used as a default.
Each `*_consumption` input variable currently reads straight from the
dataset. Two options for the hook:

1. **Decorate at the input layer**: add `defined_for` /
`default_formula` that multiplies by `consumption_response_factor`.
This is clean but ties the consumption inputs to indirect-tax
modelling.
1. **Decorate at the input layer**: give each input a formula that
multiplies by `consumption_response_factor`. This ties the consumption
inputs to indirect-tax modelling, and policyengine-core does not run a
formula for a period the dataset already supplies, so it would not
change dataset values.
2. **Insert a derived layer**: keep `*_consumption_baseline` (reads from
data) and introduce `*_consumption` formulas that apply the response
factor. This is the safer route: users who do not need the indirect
Expand All @@ -109,28 +123,33 @@ Recommendation: route (2). The dataset-side variable becomes

### Tests

- A reform that drops income tax for low-income households should
produce a *positive* `consumer_incident_tax_revenue_change` aggregate
(more take-home pay -> more consumption -> more VAT).
- Once Phase 3 derives `consumer_incident_tax_revenue_change` from the
response, a reform that drops income tax for low-income households should
produce a *positive* aggregate (more take-home pay -> more consumption ->
more VAT). Today the variable spreads the amount set by the
`consumer_incident_tax_change` parameter (in £bn) over households in
proportion to their consumption, so in a `policyengine_uk` simulation
with positive weighted consumption its weighted aggregate equals that
amount whatever the reform does to income.
- A reform that raises NI should produce a *negative* aggregate.
- The aggregate should track elasticity × (sum of net income change × VAT-equivalent rate)
to within a small tolerance.

## Open questions

- Should the elasticity vary by household income decile? Empirical
evidence suggests low-income households have higher marginal
consumption propensity.
- Should the elasticity vary by household income decile, for example if
lower-income households spend a larger share of extra income?
- Should saving be modelled explicitly (the residual after consumption)?
Phase 1 implicitly assumes any income change above the elasticity
applies to saving.
Phase 1 implicitly assumes that whatever part of an income change is not
consumed (a share of 1 − elasticity × baseline consumption / baseline
net income) is saved.
- How does this interact with the existing labour-supply response so
we don't double-count?

## References

- UKMOD documentation, to confirm the 0.8 consumption elasticity reported in #1114 (not yet checked).
- ONS [Living Costs and Food Survey](https://www.ons.gov.uk/peoplepopulationandcommunity/personalandhouseholdfinances/expenditure/bulletins/familyspendingintheuk/latest): income-elasticity calibration source for Phase 2.
- UKMOD, [Indirect Taxation (TCO) Extension technical note](https://www.microsimulation.ac.uk/wp-content/uploads/2026/01/TCO-extension-v3.pdf) (revised October 2025): its three expenditure assumptions; it does not give the 0.8 reported in #1114.
- ONS, [Family spending in the UK](https://www.ons.gov.uk/peoplepopulationandcommunity/personalandhouseholdfinances/expenditure/bulletins/familyspendingintheuk/latest): the Living Costs and Food Survey's headline publication; Phase 2 income elasticities would be estimated from LCF microdata.
- HMRC, [VAT annual statistics](https://www.gov.uk/government/statistics/value-added-tax-vat-annual-statistics): calibration target for the aggregate.
- Existing infrastructure: [`consumer_incident_tax_revenue_change`](https://github.com/PolicyEngine/policyengine-uk/blob/main/policyengine_uk/variables/contrib/policyengine/consumer_incident_tax_revenue_change.py) and the `gov/simulation/labour_supply_responses/` parameter tree for the parallel labour-supply pattern.
- Related issue: [#1114](https://github.com/PolicyEngine/policyengine-uk/issues/1114).
Loading