Skip to content

Make CenTax’s retention-rate elasticity of 1.0 the default for UK CGT modelling #2396

Closed

Description

@juaristi22

Requested behaviour

We have decided to adopt the central elasticity proposed in uk-cgt-reform#10: 1.0 with respect to the net-of-tax retention rate, with 0.5 and 2.0 available as sensitivities. Please implement this as the shared UK-model default, so the dashboard, direct country-model calculations and policyengine.py can use the same assumption.

The response formula already exists:

G₁ = G₀ × ((1 − t₁) / (1 − t₀)) ** e
e = 1.0 by default

Here G₀ is gains before the response, and t₀/t₁ are the baseline/reform marginal rates measured at the same pre-response gains. Do not replace this with a fixed −0.7 marginal-tax-rate elasticity: the two forms differ across finite rate changes.

Current implementation and ownership

In policyengine-uk 2.126.12, retention elasticity and MTR elasticity both default to zero. The model therefore leaves CGT reform calculations static unless callers provide an elasticity.

The UK model owns the parameters and response formula. This issue covers that implementation and its release. The companion issue PolicyEngine/policyengine.py#602 covers the downstream model pin, bundle compatibility, wrapper integration and run metadata.

Proposed changes

  1. Change the model-owned default. Set gov.simulation.capital_gains_responses.elasticity to 1.0, retaining mtr_elasticity=0 and separate_badr_elasticity=false. Qualifying BADR/Investors’ Relief gains therefore use the same 1.0 as other gains, matching Use black code formatter #10’s central case. Update the parameter documentation and source references. Describe the effective-period treatment as a modelling assumption, rather than a change in tax law.

  2. Keep explicit alternatives usable. Preserve explicit static calculations, retention sensitivities/custom values, and the OBR elasticity comparison (3.6 main and 1.4 qualifying BADR gains with the separate switch on). Define a complete static configuration: both main conventions zero and the separate BADR switch off. Respect dated parameter overrides.

  3. Handle legacy MTR calls before changing the default. The response helper rejects simultaneously nonzero retention and MTR elasticities. A new retention default of 1.0 would make calls supplying only mtr_elasticity fail. Either resolve explicit MTR mode by clearing inherited retention/BADR response settings for its applicable periods, or provide a clear migration requiring callers to clear those settings explicitly. Continue rejecting genuinely conflicting explicit conventions. Do not silently translate −0.7 into retention 1.0.

  4. Validate and release the behaviour change. Update the existing tests that assert a static default, add the checks below, and provide a changelog and migration example. Confirm that the response measurement remains isolated from shared model state. Measure the runtime impact of enabling the response by default, including unreformed calculations. Publish the model release for the companion .py issue to adopt.

Interpretation and related work

CenTax’s 2024 report, pp. 36–37, uses 1.0 for a medium-term package that also changes the tax base, and considers 0.5–2.0. The value incorporates adjustments for removal of uplift at death and cross-base income shifting. Document it as PE’s adopted central assumption, with those qualifications; adopting it does not implement CenTax’s full package or reproduce its revenue estimates.

Income shifting remains separate work in #1982 and #1805. Do not automatically add a separate income-tax recapture to the CenTax central case, which already contains a cross-base adjustment. Likewise, the OBR elasticity comparison alone is not a replication of the OBR’s full costing.

Acceptance criteria

  • A CGT reform with no elasticity override matches an explicit retention-1.0 calculation through both reform= and scenario= country-model routes, including household and microsimulation use.
  • For a 24%→45% rate change, the central gains multiplier matches 0.55/0.76 ≈ 0.723684 within the model’s numerical tolerance. A rate cut produces the corresponding increase in realisations.
  • Unreformed baseline gains/liabilities remain unchanged and the behavioural response is zero without a change in the measured marginal rate.
  • Explicit static, 0.5/2.0 retention cases, custom values and the OBR main/BADR split remain usable; qualifying gains use 1.0 in the default central case.
  • Legacy MTR requests have a tested, documented path; conflicting explicit conventions still raise a clear error. Dated overrides affect only their intended periods.
  • Response measurement does not mutate shared model state; results remain stable across recalculation and calculation order.
  • The released model includes the parameter references, changelog, migration examples and a runtime assessment, and is ready for .py to pin and validate.

Activity

  1. juaristi22 commented on Oct 14, 2026

    @juaristi22
    CollaboratorAuthor

    Implementation: #2419. Merge order and the companion policyengine.py PRs are tracked in the checklist on PolicyEngine/policyengine.py#602. The policyengine.py contract pin (PolicyEngine/policyengine.py#620) must be on main before #2419 merges, because merging publishes the release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions