Skip to content

Commit ffbf03a

Browse files
Add Universal Credit reforms (2026) (#1287)
* Add the UC rebalancing reforms * Add to docs * Fix import * Update docs * Update test
1 parent ddd26c9 commit ffbf03a

33 files changed

Lines changed: 1408 additions & 689 deletions

docs/book/_toc.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ parts:
1313
- caption: Validation
1414
chapters:
1515
- file: validation/hbai
16-
16+
- caption: Policy
17+
chapters:
18+
- file: policy/uc-rebalancing

docs/book/index.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@
121121
" reformed.calculate(\"gov_balance\", 2025).sum()\n",
122122
" - baseline.calc(\"gov_balance\", 2025).sum()\n",
123123
")\n",
124-
"f\"Revenue: £{round(revenue / 1e+9, 1)}bn\""
124+
"f\"Revenue: £{round(revenue / 1e9, 1)}bn\""
125125
]
126126
}
127127
],

docs/book/policy/uc-rebalancing.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Universal Credit rebalancing reforms
2+
3+
The Universal Credit rebalancing reforms represent changes to Universal Credit provisions introduced through the Universal Credit Bill. These reforms take effect from April 2026 and are designed to adjust benefit levels and eligibility criteria.
4+
5+
## Overview
6+
7+
The reforms consist of two main components:
8+
9+
1. **Health element changes for new claimants**: New Universal Credit claimants from April 2026 onwards receive a fixed health element amount, while existing claimants continue to receive inflation-linked increases.
10+
11+
2. **Standard allowance uplifts**: The standard allowance receives additional uplifts beyond the annual inflationary increase from 2026-2029.
12+
13+
## Health element changes
14+
15+
From April 2026, new Universal Credit claimants who qualify for the Limited Capacity for Work-Related Activity (LCWRA) element receive a fixed monthly amount of £217.26, rather than the inflation-adjusted amount that pre-2026 claimants continue to receive.
16+
17+
The implementation uses transition probabilities based on WPI Economics analysis for the Trussell Trust, derived from administrative Personal Independence Payment data. The probability of being a new claimant varies by year:
18+
19+
- 2026: 11%
20+
- 2027: 13%
21+
- 2028: 16%
22+
- 2029: 22%
23+
24+
## Standard allowance uplifts
25+
26+
The standard allowance receives additional percentage uplifts beyond the normal inflationary increase:
27+
28+
- 2026: 2.3% additional uplift
29+
- 2027: 3.1% additional uplift (cumulative)
30+
- 2028: 4.0% additional uplift (cumulative)
31+
- 2029: 4.8% additional uplift (cumulative)
32+
33+
These uplifts are applied to the previous year's standard allowance amount and compound over time.
34+
35+
## Implementation
36+
37+
The reforms are implemented through:
38+
39+
- **Parameters**: Three YAML files define the reform's activation status, health element amount for new claimants, and standard allowance uplift rates.
40+
- **Scenario modifier**: The `add_universal_credit_reform` function applies the changes to Universal Credit calculations during microsimulation.
41+
- **Scenario**: The `universal_credit_july_2025_reform` scenario enables the reforms in policy analysis.
42+
43+
## Examples
44+
45+
You can use these reforms in your own analysis by creating a `Simulation` with parametric changes to modify the reform parameters.
46+
47+
### Disabling the rebalancing reforms entirely
48+
49+
```python
50+
from policyengine_uk import Simulation, Scenario
51+
52+
# Disable the reforms from 2026 onwards
53+
scenario = Scenario(parameter_changes={
54+
"gov.dwp.universal_credit.rebalancing.active": False,
55+
})
56+
57+
sim = Simulation(scenario=scenario)
58+
```
59+
60+
### Changing the standard allowance uplift parameters
61+
62+
```python
63+
from policyengine_uk import Simulation, Scenario
64+
65+
# Set different uplift rates - e.g. 5% in 2026, 7% in 2027
66+
scenario = Scenario(parameter_changes={
67+
"gov.dwp.universal_credit.rebalancing.standard_allowance_uplift": {
68+
"2026-01-01": 0.05,
69+
"2027-01-01": 0.07,
70+
"2028-01-01": 0.07,
71+
"2029-01-01": 0.07
72+
}
73+
})
74+
75+
sim = Simulation(scenario=scenario)
76+
```
77+
78+
### Changing the health element amount for new claimants
79+
80+
```python
81+
from policyengine_uk import Simulation, Scenario
82+
83+
# Set the new claimant health element to £250 per month
84+
scenario = Scenario(parameter_changes={
85+
"gov.dwp.universal_credit.rebalancing.new_claimant_health_element": {
86+
"2026-01-01": 250.00
87+
}
88+
})
89+
90+
sim = Simulation(scenario=scenario)
91+
```
92+
93+
## Legislative reference
94+
95+
The reforms are based on provisions in the Universal Credit Bill, available at: https://bills.parliament.uk/publications/62123/documents/6889.

docs/book/programs/gov/dcms/bbc/tv-licence.ipynb

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,10 @@
128128
"\n",
129129
"df = pd.DataFrame()\n",
130130
"df[\"Date\"] = [\n",
131-
" parameter.instant_str\n",
132-
" for parameter in dcms.bbc.tv_licence.colour.values_list\n",
131+
" parameter.instant_str for parameter in dcms.bbc.tv_licence.colour.values_list\n",
133132
"]\n",
134133
"df[\"Full TV Licence Fee\"] = [\n",
135-
" f\"£{parameter.value:.2f}\"\n",
136-
" for parameter in dcms.bbc.tv_licence.colour.values_list\n",
134+
" f\"£{parameter.value:.2f}\" for parameter in dcms.bbc.tv_licence.colour.values_list\n",
137135
"]\n",
138136
"df[\"Blind TV Licence Fee\"] = [\n",
139137
" f\"£{0.5 * parameter.value:.2f}\"\n",
@@ -332,13 +330,11 @@
332330
"for i in range(len(aged_discount[\"Reformed value\"])):\n",
333331
" aged_discount[\"Current value\"] += [\"100%\"]\n",
334332
" aged_discount[\"Change against current\"] += [\n",
335-
" f\"{aged_discount['Reformed value'][i]-1:.0%}\"\n",
333+
" f\"{aged_discount['Reformed value'][i] - 1:.0%}\"\n",
336334
" ]\n",
337-
" aged_discount[\"Reformed value\"][\n",
338-
" i\n",
339-
" ] = f\"{aged_discount['Reformed value'][i]:.0%}\"\n",
335+
" aged_discount[\"Reformed value\"][i] = f\"{aged_discount['Reformed value'][i]:.0%}\"\n",
340336
" aged_discount[\"Reference\"] += [\n",
341-
" f\"<a href=\\\"{reference_list_ad[i]}\\\">Budgetary impact of changing aged discount to {aged_discount['Reformed value'][i]}</a>\"\n",
337+
" f'<a href=\"{reference_list_ad[i]}\">Budgetary impact of changing aged discount to {aged_discount[\"Reformed value\"][i]}</a>'\n",
342338
" ]\n",
343339
"\n",
344340
"df_aged_discount = pd.DataFrame(aged_discount)\n",
@@ -406,11 +402,11 @@
406402
"for i in range(len(licence_fee[\"Reformed value\"])):\n",
407403
" licence_fee[\"Current value\"] += [\"£159\"]\n",
408404
" licence_fee[\"Change against current\"] += [\n",
409-
" f\"£{licence_fee['Reformed value'][i]-159}\"\n",
405+
" f\"£{licence_fee['Reformed value'][i] - 159}\"\n",
410406
" ]\n",
411407
" licence_fee[\"Reformed value\"][i] = f\"£{licence_fee['Reformed value'][i]}\"\n",
412408
" licence_fee[\"Reference\"] += [\n",
413-
" f\"<a href=\\\"{reference_list_fee[i]}\\\">Budgetary impact of changing licence fee to {licence_fee['Reformed value'][i]}</a>\"\n",
409+
" f'<a href=\"{reference_list_fee[i]}\">Budgetary impact of changing licence fee to {licence_fee[\"Reformed value\"][i]}</a>'\n",
414410
" ]\n",
415411
"\n",
416412
"df_licence_fee = pd.DataFrame(licence_fee)\n",

docs/book/programs/gov/dwp/pension-credit.ipynb

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,8 @@
167167
"\n",
168168
"parameters = system.parameters\n",
169169
"\n",
170-
"carer_addition = (\n",
171-
" parameters.gov.dwp.pension_credit.guarantee_credit.carer.addition\n",
172-
")\n",
173-
"child_addition = (\n",
174-
" parameters.gov.dwp.pension_credit.guarantee_credit.child.addition\n",
175-
")\n",
170+
"carer_addition = parameters.gov.dwp.pension_credit.guarantee_credit.carer.addition\n",
171+
"child_addition = parameters.gov.dwp.pension_credit.guarantee_credit.child.addition\n",
176172
"disabled_child = (\n",
177173
" parameters.gov.dwp.pension_credit.guarantee_credit.child.disability.addition\n",
178174
")\n",
@@ -1506,12 +1502,8 @@
15061502
"\n",
15071503
"parameters = system.parameters\n",
15081504
"\n",
1509-
"threshold_single = (\n",
1510-
" parameters.gov.dwp.pension_credit.savings_credit.threshold.SINGLE\n",
1511-
")\n",
1512-
"threshold_couple = (\n",
1513-
" parameters.gov.dwp.pension_credit.savings_credit.threshold.COUPLE\n",
1514-
")\n",
1505+
"threshold_single = parameters.gov.dwp.pension_credit.savings_credit.threshold.SINGLE\n",
1506+
"threshold_couple = parameters.gov.dwp.pension_credit.savings_credit.threshold.COUPLE\n",
15151507
"\n",
15161508
"elements = [threshold_single, threshold_couple] # [...]\n",
15171509
"\n",

docs/book/programs/gov/dwp/universal-credit.ipynb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,7 @@
194194
"disabled_child_amount = (\n",
195195
" parameters.gov.dwp.universal_credit.elements.child.disabled.amount\n",
196196
")\n",
197-
"higher_amount = (\n",
198-
" parameters.gov.dwp.universal_credit.elements.child.first.higher_amount\n",
199-
")\n",
197+
"higher_amount = parameters.gov.dwp.universal_credit.elements.child.first.higher_amount\n",
200198
"\n",
201199
"elements = [\n",
202200
" carer_amount,\n",

docs/book/programs/gov/hmrc/child-benefit.ipynb

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -169,29 +169,27 @@
169169
" data[\"Value\"] += [f\"£{parameter.value:.2f}\"]\n",
170170
" try:\n",
171171
" data[\"Reference\"] += [\n",
172-
" f\"<a href=\\\"{parameter.metadata['reference'][0]['href']}\\\">{parameter.metadata['reference'][0]['title']}</a>\"\n",
172+
" f'<a href=\"{parameter.metadata[\"reference\"][0][\"href\"]}\">{parameter.metadata[\"reference\"][0][\"title\"]}</a>'\n",
173173
" ]\n",
174174
" except:\n",
175175
" data[\"Reference\"] += [\"\"]\n",
176176
"\n",
177177
"\n",
178-
"for (\n",
179-
" parameter\n",
180-
") in parameters.gov.hmrc.child_benefit.amount.additional.values_list:\n",
178+
"for parameter in parameters.gov.hmrc.child_benefit.amount.additional.values_list:\n",
181179
" data[\"Date\"] += [parameter.instant_str]\n",
182180
" data[\"Name\"] += [\"Additional\"]\n",
183181
" data[\"Value\"] += [f\"£{parameter.value:.2f}\"]\n",
184182
" try:\n",
185183
" data[\"Reference\"] += [\n",
186-
" f\"<a href=\\\"{parameter.metadata['reference'][0]['href']}\\\">{parameter.metadata['reference'][0]['title']}</a>\"\n",
184+
" f'<a href=\"{parameter.metadata[\"reference\"][0][\"href\"]}\">{parameter.metadata[\"reference\"][0][\"title\"]}</a>'\n",
187185
" ]\n",
188186
" except:\n",
189187
" data[\"Reference\"] += [\"\"]\n",
190188
"\n",
191189
"\n",
192-
"pd.DataFrame(data).sort_values(\"Date\").set_index(\n",
193-
" [\"Date\", \"Name\"]\n",
194-
").style.format(lambda x: x)"
190+
"pd.DataFrame(data).sort_values(\"Date\").set_index([\"Date\", \"Name\"]).style.format(\n",
191+
" lambda x: x\n",
192+
")"
195193
]
196194
},
197195
{
@@ -296,15 +294,13 @@
296294
" for i in range(n):\n",
297295
" sim.add_person(age=10, name=str(i))\n",
298296
" sim.add_benunit(members=[str(i) for i in range(n)])\n",
299-
" return f'£{sim.calc(\"child_benefit\").sum():.2f}'\n",
297+
" return f\"£{sim.calc('child_benefit').sum():.2f}\"\n",
300298
"\n",
301299
"\n",
302300
"pd.DataFrame(\n",
303301
" {\n",
304302
" \"Number of children\": list(range(1, 7)),\n",
305-
" \"Child Benefit (Annual)\": list(\n",
306-
" map(get_cb_for_n_children, range(1, 7))\n",
307-
" ),\n",
303+
" \"Child Benefit (Annual)\": list(map(get_cb_for_n_children, range(1, 7))),\n",
308304
" }\n",
309305
").set_index(\"Number of children\")"
310306
]

docs/book/programs/gov/hmrc/fuel-duty.ipynb

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,10 @@
126126
"df = pd.DataFrame()\n",
127127
"\n",
128128
"df[\"Date of change\"] = [\n",
129-
" parameter.instant_str\n",
130-
" for parameter in hmrc.fuel_duty.petrol_and_diesel.values_list\n",
129+
" parameter.instant_str for parameter in hmrc.fuel_duty.petrol_and_diesel.values_list\n",
131130
"]\n",
132131
"df[\"Fuel duty rate (£/litre)\"] = [\n",
133-
" parameter.value\n",
134-
" for parameter in hmrc.fuel_duty.petrol_and_diesel.values_list\n",
132+
" parameter.value for parameter in hmrc.fuel_duty.petrol_and_diesel.values_list\n",
135133
"]\n",
136134
"df.sort_values(\"Date of change\", inplace=True)\n",
137135
"df.set_index(\"Date of change\")"
@@ -1072,11 +1070,7 @@
10721070
" height=600,\n",
10731071
" width=800,\n",
10741072
" template=\"plotly_white\",\n",
1075-
").update_xaxes(\n",
1076-
" tickangle=45, tickfont={\"size\": 10}\n",
1077-
").update_traces(\n",
1078-
" line_shape=\"hv\"\n",
1079-
")"
1073+
").update_xaxes(tickangle=45, tickfont={\"size\": 10}).update_traces(line_shape=\"hv\")"
10801074
]
10811075
}
10821076
],

0 commit comments

Comments
 (0)