Skip to content

Stop stacking the trading allowance on expenses already netted from profit - #1881

Merged
MaxGhenis merged 4 commits into
mainfrom
fix/trading-allowance-double-relief
Oct 2, 2026
Merged

MaxGhenis merged 4 commits into
mainfrom
fix/trading-allowance-double-relief

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #1880

What was wrong

trading_allowance_deduction subtracted min(£1,000, self_employment_income) from self_employment_income. That input is profit after expenses, so the allowance was stacked on expenses already deducted. The trading allowance is in ITTOIA 2005 Part 6A Chapter 1, ss. 783A–783AR; /part/7A does not exist on legislation.gov.uk. It is measured against gross receipts ("relevant income", s. 783AC). It replaces actual expenses and capital allowances rather than adding to them:

  • Full relief. When receipts are within £1,000 (s. 783AE), the trade's profits or losses "are treated as nil" (s. 783AF).
  • Partial relief. Above £1,000, the person may elect (s. 783AM) to compute profit as receipts minus £1,000, instead of deducting expenses (s. 783AI). With a single trade, that profit is positive whenever partial relief is available. Section 783AK(4) stops the allowance creating a loss when it is split across several sources.
  • Capital allowances. These only take effect as a trade expense (CAA 2001 s. 247), so they cannot be claimed alongside the allowance (HMRC BIM86050).

Example, 2027: a £25,000 profit gave income_tax £2,286 on adjusted_net_income £24,000. The correct figure is £2,486 on £25,000.

What the data represent

Neither source contains gross receipts. Every self-employment value in the enhanced FRS is a profit net of expenses:

  • FRS. self_employment_income = max(0, SEINCAM2) × 365.25/7 (policyengine-uk-data datasets/frs.py). DWP defines FRS self-employment earnings as "gross of tax and national insurance payments, based on profits ... or on estimated drawings otherwise" (Stat-Xplore FRS metadata).
  • Enhanced FRS imputation. This comes from SPI PROFITS. The SPI Public Use Tape documentation (UKDS SN 9422, 2022-23) defines it as "Gross profits assessable for all sources of self-employment income". Self-employed income there is max(0, PROFITS – CAPALL – LOSSBF).

In the published enhanced FRS 2024-25 (1.56.16), capital_allowances and loss_relief total £0.

The fix

A new optional input, self_employment_gross_receipts, lets callers who know turnover get the exact statutory result. With profit P, receipts R, expenses E = R − P and capital allowances CA:

Case trading_allowance_deduction Result
Receipts supplied max(0, min(P, 1000 − E) − CA) Taxable profit = 0 if R ≤ £1,000, otherwise R − max(E + CA, £1,000). This is exactly the better of the profit method and the allowance.
Receipts unknown or below profit (all microdata today) P − CA if P ≤ £1,000, otherwise 0 A profit within the allowance is treated as coming from receipts within it. A larger profit means receipts exceed £1,000, so full relief is impossible, and partial relief helps only if expenses were under £1,000, which net profit cannot show.

Why keep full relief for small profits when receipts are unknown? In the published dataset, 0.198m people (2026) have profits of £1,000 or less. About 0.16m of them are employees, retired or disabled people with side income, and 0.04m are mainly self-employed. Keeping full relief also leaves the familiar "£1,000 of side income is tax-free" case unchanged. The cost is a known discontinuity at £1,000 when receipts are unknown: tax stays non-decreasing but jumps there. Supplying receipts removes it. The variable documentation states this.

Also in this PR:

  • The parameter now starts in 2017-18: zero before, £1,000 from 2017-04-06 (F(No.2)A 2017 Sch 3 para 13; s. 783AD).
  • References now cite ss. 783AF and 783AI and BIM86050, instead of s. 783AF alone.

Invariants (stated and property-tested)

policyengine_uk/tests/test_trading_allowance_properties.py uses Hypothesis, 40 examples per property with 24 cases each. The draws are weighted towards the £1,000 boundary and the tax thresholds.

  1. Bounds: 0 ≤ relief ≤ min(£1,000, max(profit, 0)).
  2. Differential against an independent statutory reference. With consistent receipts, taxable profit = 0 if R ≤ 1000 else max(0, R − max(E + CA, 1000) − loss relief). With unknown or inconsistent receipts, taxable profit = 0 if P ≤ 1000 else max(0, P − CA − loss relief).
  3. No stacking: receipts − taxable profit ≤ max(E + CA, £1,000) + loss relief whenever receipts exceed £1,000.
  4. Monotonicity: income_tax is non-decreasing in profit at fixed expenses and capital allowances, across employment incomes from £0 to £200k.

Cases include losses (profit down to −£5,000), loss relief, and receipts drawn below profit.

Mutation checks:

  • With main's formula restored, properties 1–3 fail. It returns negative relief on losses and stacks on expenses.
  • Removing the receipts-below-profit guard fails property 2.

Revenue effect (microsimulation, before vs after)

Real Microsimulation runs on 2 October 2026: main 3c48247e against main merged with this head (0cbcc78c), on the published Enhanced FRS 2024-25 (1.57.4, sha256 03fe15e4…). One run per version and year.

Model year Income tax Universal Credit Government balance
2026 +£0.89bn +£0.02bn +£0.87bn
2027 +£0.93bn +£0.02bn +£0.91bn
2028 +£0.94bn +£0.02bn +£0.92bn
2029 +£0.97bn +£0.02bn +£0.94bn
2030 +£0.99bn +£0.02bn +£0.96bn
2031 +£1.01bn +£0.02bn +£1.01bn

About 3.5m households pay more income tax. Class 4 NICs are unchanged, since they already use unreduced profit. This moves baseline income tax by about £0.9–1.0bn a year.

Earlier runs (30 September main, dataset 1.56.16) gave the same picture: the deduction fell from £4.36bn to £0.12bn in 2026, and income tax rose £0.89bn (2026) to £0.98bn (2030).

Known limits (not changed here)

  • Class 4 NICs use raw self_employment_income. They ignore capital allowances and loss relief, and the allowance when receipts are supplied. The statute taxes the same Chapter 2 profit (SSCBA s. 15(1)(b); BIM86052). Follow-up.
  • Property allowance (Property allowance is deducted on top of expenses already netted from property income #1900). property_allowance_deduction has the same stacking bug: INCPROP is "Net income from UK and overseas property (income - expenses)". It also cites a trading section and starts in 2005. Follow-up.
  • Losses under full relief are not set to nil. Section 783AF nils losses as well as profits, but loss_relief is still built from trading_loss and is not limited by the allowance. Taxable profit is floored at zero, so tax is unaffected unless losses carry forward. This was already the case.
  • Pre-2024-25 years follow the current text of s. 783AI. FA 2022 Sch 1 removed the overlap-relief Step 3 from 2024-25, and overlap relief is not modelled.
  • Not modelled:
    • the exclusions for payments from an employer, a partnership or a close company (ss. 783AN–783AQ);
    • miscellaneous income as relevant income (s. 783AB);
    • allocating one allowance across several trades (s. 783AK).

Tests run

  • policyengine-core test policyengine_uk/tests/policy/baseline/gov/hmrc: 443 passed. This folder held all 12 full-suite failures on the first run (1242 passed).
  • Updated expectations that encoded the double relief: pension_contributions_relief.yaml (£30k profit now £3,486 = (30,000 − 12,570) × 20%), child_benefit_hitc.yaml and taxable_self_employment_income.yaml.
  • pytest policyengine_uk/tests/test_trading_allowance_properties.py: 4 passed, 40 examples each.
  • Parameter, metadata and code-health tests: 34 passed, 1 skipped.
  • ruff format --check . and ruff check .: clean.
  • hypothesis is already a dev dependency on main, so this PR no longer touches pyproject.toml or uv.lock.
  • After merging current main (0cbcc78), the full policyengine-core test policyengine_uk/tests/policy run passes (1,277 tests), as do the property tests and ruff.
  • Docs: the trading allowance row in income-tax.ipynb now shows the current value (£1,000 from 2017-04-06). Other rows in that cell still show each parameter's earliest value, which was already the case.

Independent review

An Opus 5.5 reviewer ran on a Subfleet review/standard lane with a read-only sandbox, and returned APPROVE. It hand-checked the statute and every updated expectation. It could not execute code, so the test runs, mutation checks and revenue figures above are this session's own. A delta re-review of 113c6f7 also returned APPROVE; its one actionable minor finding, a YAML case for receipts below profit, is added. The first review's minor findings are all addressed in 113c6f7: inconsistent receipts, the discontinuity doc, wider property tests, the s. 783AK(4) wording, the docstring and the notebook output.

axiom: TheAxiomFoundation/rulespec-uk#348 queued

🤖 Generated with Claude Code

…rofit

self_employment_income is profit after expenses (FRS SEINCAM2; SPI PROFITS
"gross profits assessable"), but trading_allowance_deduction subtracted
min(1000, profit) from it. Under ITTOIA 2005 Part 6A the allowance is
measured against gross receipts (s. 783AC) and replaces actual expenses and
capital allowances (ss. 783AE-783AF full relief, s. 783AI partial relief;
HMRC BIM86050), so the old formula gave up to 1,000 of double relief to
every self-employed person with profits above the allowance.

The deduction is now the allowance's excess over actual expenses and capital
allowances, capped at profit, when the new optional
self_employment_gross_receipts input is supplied. Without receipts, a profit
within the allowance gets full relief and a larger profit gets none. The
allowance parameter now starts in 2017-18 (F(No.2)A 2017 Sch 3 para 13).

Fixes #1880

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits September 26, 2026 23:49
- Treat gross receipts below profit as unknown rather than as zero expenses.
- Document the discontinuity at the allowance when receipts are unknown.
- Property tests now draw losses, loss relief and receipts below profit, and
  fix the Part 6A reference in the docstring.
- Refresh the saved docs notebook output for the trading allowance row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review September 27, 2026 03:51
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Axiom status for this fix. Axiom does not yet encode the trading allowance, so this PR has no Axiom differential check yet. Tracking is in TheAxiomFoundation/rulespec-uk#348.

The statutory reading was checked independently against legislation.gov.uk ITTOIA 2005 Part 6A Chapter 1, and it agrees with this PR:

  • The allowance is £1,000 (s.783AD).
  • "Relevant income" is gross receipts (s.783AC(2)).
  • Full relief applies at or below the allowance absent a s.783AL election (s.783AE), and the trade's profits or losses "are treated as nil" (s.783AF).
  • Partial relief needs a s.783AM election (s.783AH). The profits are then the receipts less the deductible amount, which is the allowance subject to s.783AK (s.783AI(2)–(3)).

So R − max(E + CA, £1,000) above £1,000 of receipts, and nil at or below it, is the statutory result when receipts are known. The small-profit fallback without receipts is a modelling assumption, not statute, as the PR says.

The Axiom encoding needs the Chapter ingested into the Axiom corpus first. Only s.783A is there today, and outside the pinned UK release. Once encoded, Axiom will compare it against the fixed release on a receipts/expenses grid.

@MaxGhenis

MaxGhenis commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

Impact re-measured on current main for the merge train (corrected 2 October, 21:15Z). These are real Microsimulation runs on the published Enhanced FRS 2024-25 (1.57.4, sha256 03fe15e4…): main 3c48247e against main merged with this head (0cbcc78c, no conflicts).

Model year Income tax Universal Credit Household net income Government balance
2026 +£892m +£18m −£874m +£874m
2027 +£929m +£19m −£910m +£910m
2028 +£942m +£20m −£922m +£922m
2029 +£966m +£22m −£944m +£944m
2030 +£988m +£23m −£965m +£965m
2031 +£1,014m +£23m −£1,013m +£1,013m
  • About 3.5m households have lower net income. No household gains in 2026 to 2029 or in 2031; in 2030 fewer than ten survey records gain, so no figure is given for them.
  • NI is unchanged.
  • In 2031 household net income falls about £23m more than income tax and benefits explain. That part comes from items this table does not list; I have not traced it.

The change passes £1bn a year in 2031-32. An earlier version of this comment said that put the merge with Max. He has since removed the size line from the merge-on-gates rule (decision d849 is closed as "merge on gates"), so this PR lands on its gates.

@MaxGhenis
MaxGhenis merged commit 84da286 into main Oct 2, 2026
7 checks passed
@MaxGhenis
MaxGhenis deleted the fix/trading-allowance-double-relief branch October 2, 2026 21:11
@MaxGhenis

Copy link
Copy Markdown
Collaborator Author

Merged at 0cbcc78c under Max's merge-on-gates rule for PolicyEngine-UK fixes (2 October 2026; decisions d452 and d849).

  • CI: gh pr checks passed on 0cbcc78c.
  • Independent review: a Subfleet delta review of 0cbcc78c returned APPROVE (job 20261002-163704-mt-review-1881), on top of the two earlier approvals in the description.
  • Impact: real runs on current main are in the description and the comment above: income tax +£0.89bn in 2026, rising to +£1.01bn in 2031.
  • Composition: on main 3c48247e merged with this head, the full YAML policy suite (1,624 cases) and the trading allowance property tests pass locally.

#1919 (Class 4 NICs on Chapter 2 profits) is stacked on this.

MaxGhenis added a commit that referenced this pull request Oct 3, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	docs/book/programs/gov/dwp/universal-credit.ipynb
#	policyengine_uk/tests/test_uc_state_pension_properties.py
MaxGhenis added a commit that referenced this pull request Oct 3, 2026
- #1950: rent and dividends are not UC unearned income (reg. 66(1) is a
  closed list), so the single parent's unearned income is nil (UC
  11,308.50) and the self-employed claimant's is the pension alone (UC
  3,975.29).
- #1881: without gross receipts, a trade profit above the trading
  allowance gets no allowance (ITTOIA 2005 s. 783AI), so the tax on the
  trade is (18,000 - 12,570) x 20% = 1,086.

The tax and NI this PR deducts are unchanged in method; the cases' gaps
against the pre-fix formula are still 0.55 x the tax on other income.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Trading allowance is deducted on top of expenses already netted from self-employment profit

1 participant