PolicyEngine / PolicyEngine/policyengine-uk-data

Tax-Free Childcare spending is ~1.9x the repo's own declared calibration target, while caseload is on target

Open
#470 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
1
Forks
2
Avg merge
1d 14h
Merged PRs (30d)
4

Description

Rewritten. This issue was originally filed as a take-up problem and closed by me as invalid, because the take-up claim rested on comparing an annual model aggregate against a point-in-time HMRC figure. Reopening with a different and, I think, sounder framing: the model misses the repo's own declared calibration target, so nothing external needs to be agreed to see the problem.

The repo declares two TFC targets and hits one

policyengine_uk_data/datasets/childcare/takeup_rate.py:

# TFC targets refreshed from HMRC "Tax-Free Childcare statistics: June 2025"
#   - spending: £632.2 m (Table 1, annual government top-up)
#   - caseload: 985 thousand children received TFC in 2024-25 (annual unique)
targets = {
    "spending": {"tfc": 0.63, ...},
    "caseload": {"tfc": 985, ...},
}

enhanced_frs_2024_25 delivers:

year spending vs £0.63bn target children vs 985k target
2024 £1.178bn 1.87× 1,107,711 1.12×
2025 £1.209bn 1.92× 1,108,143 1.13×
2027 £1.237bn 1.96× 1,131,742 1.15×

Caseload is close. Spending is nearly double, in the calibration year itself.

The take-up rate was tuned to caseload and the recalibration never ran

policyengine_uk_data/parameters/take_up/tax_free_childcare.yaml:

values:
  2015-01-01: 0.586
  # Bumped from the 2020-21 FRS-calibrated 0.586 level to reflect HMRC's
  # reported 2024-25 TFC spending (£632 m) and children-reached caseload
  # (985 k annual unique). ... A full recalibration via
  # `datasets/childcare/takeup_rate.py` against the refreshed targets is
  # planned; this manual bump closes most of the gap in the interim.
  2024-04-06: 0.88

The manual bump landed caseload and left spending at ~1.9×. The promised recalibration appears not to have been run.

Take-up is not the lever

Raising or lowering a uniform take-up rate moves spending and caseload together, so it cannot fix one without breaking the other — and caseload is currently the one that is right. No single rate hits both.

Rates required from the present 0.88: 0.836 to hit an 868,095 family count, 0.427 to hit the spending target.

That the caseload lever is already close to where it should be (0.836 against 0.88) while the spending lever would need to halve is the diagnostic: it localises the problem to the average award, not the number of claimants.

Cross-checks on caseload, so it is clear which side is wrong

Against HMRC's annual Table 1 and Table 2 (March 2026 release, tables), FY2025-26 — comparing families to families and children to children:

model 2027 HMRC 2025-26 ratio
families with TFC 914,236 868,095 1.05×
children with TFC 1,131,742 1,151,515 0.98×
average award per family £1,353 £691 1.96×

Caseload is right on both units. The entire overshoot is award size.

Caveat worth stating. HMRC's annual family count is a flow that counts a family active for one month the same as one active for twelve, while the model's award is a full-year entitlement. On a full-year-equivalent basis — March 2026 was 601,000 families and £55m, about £92 per family-month — the split moves: an FYE family count of 543,000–588,000 puts the model at 1.55–1.68× on caseload and 1.23–1.33× on award, rather than 1.05× and 1.96×. So the attribution between caseload and award depends on a comparator choice I am not going to resolve here. The 1.9× on total spending against the repo's own target does not depend on resolving it.

Where the award overshoot comes from

Two contributions, neither of which is take-up:

  1. The childcare fee base. childcare_expenses has no calibration target and sits at the top of the plausible range — see #471, whose ratio I have corrected downward to ~1.2×.
  2. TFC routing, which is the larger part. TFC pays 25% of spend put through a TFC account, not 25% of a family's total childcare spend. The model routes essentially all of it:
model implied by HMRC
childcare spend of claiming families £6,390
average award £1,353 £691
implied spend routed through TFC £5,412 £2,764

Real families route roughly half their childcare spend through the account — the £2,000 per-child cap, part-year claiming, and simply not putting every payment through it. The model has no representation of this.

The cap also limits how much a fee-base correction can achieve. In the model 19.2% of TFC-receiving children are at the per-child cap, and they account for 35.1% of total spend (family-level award p25/median/p75: £516 / £1,431 / £2,000). Spend that is cap-bound does not move at all until a correction is large enough to pull families off the cap, so scaling childcare_expenses down by ~1.2× delivers appreciably less than a 1.2× reduction in TFC spending. A fee-base fix alone cannot close a 1.9× gap; the routing assumption has to carry part of it.

That second part is arguably a policyengine-uk modelling issue rather than a data one, and I am happy to move it if maintainers prefer.

Suggested fix

Run the recalibration the parameter comment promises, but expect it to fail to hit both targets, and treat that failure as the signal: with caseload already right, the residual is the award, and the levers are childcare_expenses (#471) and a routing/utilisation factor on TFC-eligible spend.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with datasets/childcare/takeup_rate.py and parameters/take_up/tax_free_childcare.yaml, then run the childcare calibration against the declared spending and caseload targets. Inspect the childcare_expenses input and TFC routing described in the issue. Done means the calibration outcome explains or addresses the spending gap without losing the caseload target.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
data
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.