Skip to content

Commit 892fdaa

Browse files
MaxGhenisclaude
andauthored
Add ONS household interest income uprating for savings income (#1412)
* Add ONS household interest income uprating for savings income Previously savings_interest_income used per capita GDP for uprating, which only grew ~15% from 2020-2023. However, household interest income grew dramatically due to interest rate rises: - 2020: £16bn - 2021: £19.6bn (+22.5%) - 2022: £43.3bn (+121%) - 2023: £86bn (+99%) - 2024: £98.2bn (+14%) This adds a new parameter based on ONS National Accounts D.41g (HAXV series) that captures actual household interest received, and updates the savings_interest_income variable to use it for uprating. Fixes issue #228 - savings income tax base was too low to match OBR costings. Source: https://www.ons.gov.uk/economy/grossdomesticproductgdp/timeseries/haxv/ukea 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Use OBR non-labour income growth as proxy for savings income forecast Update household_interest_income parameter to use OBR non-labour income growth rates (from Table 1.12) for 2025-2030 instead of holding flat. This provides a reasonable projection for savings income growth when the specific ONS D.41g series isn't forecast. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 30b9e38 commit 892fdaa

14 files changed

Lines changed: 146 additions & 43 deletions

File tree

changelog_entry.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
- bump: minor
2+
changes:
3+
added:
4+
- Add ONS household interest income uprating index from National Accounts D.41g (HAXV series) that reflects actual growth in household interest received
5+
changed:
6+
- Update savings_interest_income variable to use ONS household interest income index for uprating instead of per capita GDP. This captures the dramatic growth in household interest income (from £16bn in 2020 to £98bn in 2024) due to interest rate rises.

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,12 @@
128128
"\n",
129129
"df = pd.DataFrame()\n",
130130
"df[\"Date\"] = [\n",
131-
" parameter.instant_str for parameter in dcms.bbc.tv_licence.colour.values_list\n",
131+
" parameter.instant_str\n",
132+
" for parameter in dcms.bbc.tv_licence.colour.values_list\n",
132133
"]\n",
133134
"df[\"Full TV Licence Fee\"] = [\n",
134-
" f\"£{parameter.value:.2f}\" for parameter in dcms.bbc.tv_licence.colour.values_list\n",
135+
" f\"£{parameter.value:.2f}\"\n",
136+
" for parameter in dcms.bbc.tv_licence.colour.values_list\n",
135137
"]\n",
136138
"df[\"Blind TV Licence Fee\"] = [\n",
137139
" f\"£{0.5 * parameter.value:.2f}\"\n",
@@ -332,7 +334,9 @@
332334
" aged_discount[\"Change against current\"] += [\n",
333335
" f\"{aged_discount['Reformed value'][i] - 1:.0%}\"\n",
334336
" ]\n",
335-
" aged_discount[\"Reformed value\"][i] = f\"{aged_discount['Reformed value'][i]:.0%}\"\n",
337+
" aged_discount[\"Reformed value\"][\n",
338+
" i\n",
339+
" ] = f\"{aged_discount['Reformed value'][i]:.0%}\"\n",
336340
" aged_discount[\"Reference\"] += [\n",
337341
" f'<a href=\"{reference_list_ad[i]}\">Budgetary impact of changing aged discount to {aged_discount[\"Reformed value\"][i]}</a>'\n",
338342
" ]\n",

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,12 @@
167167
"\n",
168168
"parameters = system.parameters\n",
169169
"\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",
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",
172176
"disabled_child = (\n",
173177
" parameters.gov.dwp.pension_credit.guarantee_credit.child.disability.addition\n",
174178
")\n",
@@ -1502,8 +1506,12 @@
15021506
"\n",
15031507
"parameters = system.parameters\n",
15041508
"\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",
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",
15071515
"\n",
15081516
"elements = [threshold_single, threshold_couple] # [...]\n",
15091517
"\n",

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

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

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,9 @@
175175
" data[\"Reference\"] += [\"\"]\n",
176176
"\n",
177177
"\n",
178-
"for parameter in parameters.gov.hmrc.child_benefit.amount.additional.values_list:\n",
178+
"for (\n",
179+
" parameter\n",
180+
") in parameters.gov.hmrc.child_benefit.amount.additional.values_list:\n",
179181
" data[\"Date\"] += [parameter.instant_str]\n",
180182
" data[\"Name\"] += [\"Additional\"]\n",
181183
" data[\"Value\"] += [f\"£{parameter.value:.2f}\"]\n",
@@ -187,9 +189,9 @@
187189
" data[\"Reference\"] += [\"\"]\n",
188190
"\n",
189191
"\n",
190-
"pd.DataFrame(data).sort_values(\"Date\").set_index([\"Date\", \"Name\"]).style.format(\n",
191-
" lambda x: x\n",
192-
")"
192+
"pd.DataFrame(data).sort_values(\"Date\").set_index(\n",
193+
" [\"Date\", \"Name\"]\n",
194+
").style.format(lambda x: x)"
193195
]
194196
},
195197
{
@@ -300,7 +302,9 @@
300302
"pd.DataFrame(\n",
301303
" {\n",
302304
" \"Number of children\": list(range(1, 7)),\n",
303-
" \"Child Benefit (Annual)\": list(map(get_cb_for_n_children, range(1, 7))),\n",
305+
" \"Child Benefit (Annual)\": list(\n",
306+
" map(get_cb_for_n_children, range(1, 7))\n",
307+
" ),\n",
304308
" }\n",
305309
").set_index(\"Number of children\")"
306310
]

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,12 @@
126126
"df = pd.DataFrame()\n",
127127
"\n",
128128
"df[\"Date of change\"] = [\n",
129-
" parameter.instant_str for parameter in hmrc.fuel_duty.petrol_and_diesel.values_list\n",
129+
" parameter.instant_str\n",
130+
" for parameter in hmrc.fuel_duty.petrol_and_diesel.values_list\n",
130131
"]\n",
131132
"df[\"Fuel duty rate (£/litre)\"] = [\n",
132-
" parameter.value for parameter in hmrc.fuel_duty.petrol_and_diesel.values_list\n",
133+
" parameter.value\n",
134+
" for parameter in hmrc.fuel_duty.petrol_and_diesel.values_list\n",
133135
"]\n",
134136
"df.sort_values(\"Date of change\", inplace=True)\n",
135137
"df.set_index(\"Date of change\")"
@@ -1070,7 +1072,11 @@
10701072
" height=600,\n",
10711073
" width=800,\n",
10721074
" template=\"plotly_white\",\n",
1073-
").update_xaxes(tickangle=45, tickfont={\"size\": 10}).update_traces(line_shape=\"hv\")"
1075+
").update_xaxes(\n",
1076+
" tickangle=45, tickfont={\"size\": 10}\n",
1077+
").update_traces(\n",
1078+
" line_shape=\"hv\"\n",
1079+
")"
10741080
]
10751081
}
10761082
],

docs/book/programs/gov/hmrc/income-tax.ipynb

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,9 @@
207207
"from policyengine_uk import Simulation\n",
208208
"\n",
209209
"\n",
210-
"def calculate_taxes(employment_income, dividend_income, savings_interest_income):\n",
210+
"def calculate_taxes(\n",
211+
" employment_income, dividend_income, savings_interest_income\n",
212+
"):\n",
211213
" simulation = Simulation(\n",
212214
" situation={\n",
213215
" \"people\": {\n",
@@ -220,7 +222,9 @@
220222
" }\n",
221223
" )\n",
222224
" income_tax = simulation.calculate(\"income_tax\")[0]\n",
223-
" dividend_income_tax = simulation.calculate(\"dividend_income_tax\")[0] # Added\n",
225+
" dividend_income_tax = simulation.calculate(\"dividend_income_tax\")[\n",
226+
" 0\n",
227+
" ] # Added\n",
224228
" savings_income_tax = simulation.calculate(\"savings_income_tax\")[0] # Added\n",
225229
"\n",
226230
" return income_tax, dividend_income_tax, savings_income_tax\n",
@@ -241,7 +245,9 @@
241245
" data[\"Dividend Income (£)\"],\n",
242246
" data[\"Savings Interest Income (£)\"],\n",
243247
"):\n",
244-
" income_tax, div_tax, sav_tax = calculate_taxes(emp_income, div_income, sav_income)\n",
248+
" income_tax, div_tax, sav_tax = calculate_taxes(\n",
249+
" emp_income, div_income, sav_income\n",
250+
" )\n",
245251
" data[\"Income Tax (£)\"].append(income_tax)\n",
246252
" data[\"Dividend Income Tax (£)\"].append(div_tax)\n",
247253
" data[\"Savings Income Tax (£)\"].append(sav_tax)\n",
@@ -518,8 +524,12 @@
518524
"from policyengine_uk.system import system\n",
519525
"import pandas as pd\n",
520526
"\n",
521-
"default = system.parameters.gov.hmrc.income_tax.allowances.annual_allowance.default\n",
522-
"minimum = system.parameters.gov.hmrc.income_tax.allowances.annual_allowance.minimum\n",
527+
"default = (\n",
528+
" system.parameters.gov.hmrc.income_tax.allowances.annual_allowance.default\n",
529+
")\n",
530+
"minimum = (\n",
531+
" system.parameters.gov.hmrc.income_tax.allowances.annual_allowance.minimum\n",
532+
")\n",
523533
"reduction_rate = (\n",
524534
" system.parameters.gov.hmrc.income_tax.allowances.annual_allowance.reduction_rate\n",
525535
")\n",
@@ -549,7 +559,9 @@
549559
"\n",
550560
"\n",
551561
"max = system.parameters.gov.hmrc.income_tax.allowances.marriage_allowance.max\n",
552-
"rounding_increment = system.parameters.gov.hmrc.income_tax.allowances.marriage_allowance.rounding_increment\n",
562+
"rounding_increment = (\n",
563+
" system.parameters.gov.hmrc.income_tax.allowances.marriage_allowance.rounding_increment\n",
564+
")\n",
553565
"takeup_rate = (\n",
554566
" system.parameters.gov.hmrc.income_tax.allowances.marriage_allowance.takeup_rate\n",
555567
")\n",
@@ -574,7 +586,9 @@
574586
"}\n",
575587
"\n",
576588
"\n",
577-
"deduction_rate = system.parameters.gov.hmrc.income_tax.allowances.married_couples_allowance.deduction_rate\n",
589+
"deduction_rate = (\n",
590+
" system.parameters.gov.hmrc.income_tax.allowances.married_couples_allowance.deduction_rate\n",
591+
")\n",
578592
"married_couples_allowance_data = {\n",
579593
" \"Attribute\": [\"Married Couples Allowance\"],\n",
580594
" \"Type\": [\"Deduction Rate\"],\n",
@@ -584,7 +598,9 @@
584598
"}\n",
585599
"\n",
586600
"\n",
587-
"amount = system.parameters.gov.hmrc.income_tax.allowances.personal_allowance.amount\n",
601+
"amount = (\n",
602+
" system.parameters.gov.hmrc.income_tax.allowances.personal_allowance.amount\n",
603+
")\n",
588604
"values = [item.value for item in amount.values_list]\n",
589605
"dates = [item.instant_str for item in amount.values_list]\n",
590606
"personal_allowance_data = {\n",
@@ -596,7 +612,9 @@
596612
"}\n",
597613
"\n",
598614
"\n",
599-
"addtional_threshold = system.parameters.gov.hmrc.income_tax.allowances.personal_savings_allowance.additional\n",
615+
"addtional_threshold = (\n",
616+
" system.parameters.gov.hmrc.income_tax.allowances.personal_savings_allowance.additional\n",
617+
")\n",
600618
"basic_threshold = (\n",
601619
" system.parameters.gov.hmrc.income_tax.allowances.personal_savings_allowance.basic\n",
602620
")\n",
@@ -624,7 +642,9 @@
624642
"}\n",
625643
"\n",
626644
"\n",
627-
"dividend_allowance = system.parameters.gov.hmrc.income_tax.allowances.dividend_allowance\n",
645+
"dividend_allowance = (\n",
646+
" system.parameters.gov.hmrc.income_tax.allowances.dividend_allowance\n",
647+
")\n",
628648
"dividend_allowance_data = {\n",
629649
" \"Attribute\": [\"Dividend Allowance\"],\n",
630650
" \"Type\": [\"Dividend Allowance\"],\n",
@@ -634,7 +654,9 @@
634654
"}\n",
635655
"\n",
636656
"\n",
637-
"property_allowance = system.parameters.gov.hmrc.income_tax.allowances.property_allowance\n",
657+
"property_allowance = (\n",
658+
" system.parameters.gov.hmrc.income_tax.allowances.property_allowance\n",
659+
")\n",
638660
"property_allowance_data = {\n",
639661
" \"Attribute\": [\"Property Allowance\"],\n",
640662
" \"Type\": [\"Property Allowance\"],\n",
@@ -644,7 +666,9 @@
644666
"}\n",
645667
"\n",
646668
"\n",
647-
"trading_allowance = system.parameters.gov.hmrc.income_tax.allowances.trading_allowance\n",
669+
"trading_allowance = (\n",
670+
" system.parameters.gov.hmrc.income_tax.allowances.trading_allowance\n",
671+
")\n",
648672
"trading_allowance_data = {\n",
649673
" \"Attribute\": [\"Trading Allowance\"],\n",
650674
" \"Type\": [\"Trading Allowance\"],\n",

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6287,7 +6287,9 @@
62876287
"stamp_duty = sim.calculate(\"stamp_duty_land_tax\")\n",
62886288
"home_price = sim.calculate(\"main_residential_property_purchased\")\n",
62896289
"\n",
6290-
"marginal_rate = (stamp_duty[1:] - stamp_duty[:-1]) / (home_price[1:] - home_price[:-1])\n",
6290+
"marginal_rate = (stamp_duty[1:] - stamp_duty[:-1]) / (\n",
6291+
" home_price[1:] - home_price[:-1]\n",
6292+
")\n",
62916293
"home_price = home_price[:-1]\n",
62926294
"\n",
62936295
"df = pd.DataFrame(\n",
@@ -6303,10 +6305,14 @@
63036305
"\n",
63046306
"# left shows marginal rate, right shows total SDLT\n",
63056307
"\n",
6306-
"fig = make_subplots(rows=1, cols=2, subplot_titles=(\"Marginal SDLT rate\", \"Total SDLT\"))\n",
6308+
"fig = make_subplots(\n",
6309+
" rows=1, cols=2, subplot_titles=(\"Marginal SDLT rate\", \"Total SDLT\")\n",
6310+
")\n",
63076311
"\n",
63086312
"fig.add_trace(\n",
6309-
" px.line(df, x=\"Home price\", y=\"Marginal SDLT rate\", title=\"Marginal SDLT rate\")\n",
6313+
" px.line(\n",
6314+
" df, x=\"Home price\", y=\"Marginal SDLT rate\", title=\"Marginal SDLT rate\"\n",
6315+
" )\n",
63106316
" .update_traces(line_shape=\"hv\")\n",
63116317
" .data[0],\n",
63126318
" row=1,\n",

docs/book/programs/gov/ofgem/energy-price-guarantee.ipynb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,7 +1011,9 @@
10111011
"\n",
10121012
"df = pd.DataFrame()\n",
10131013
"\n",
1014-
"df[\"Date\"] = [parameter.instant_str for parameter in ofgem.energy_price_cap.values_list]\n",
1014+
"df[\"Date\"] = [\n",
1015+
" parameter.instant_str for parameter in ofgem.energy_price_cap.values_list\n",
1016+
"]\n",
10151017
"df[\"Energy price cap\"] = [\n",
10161018
" parameter.value for parameter in ofgem.energy_price_cap.values_list\n",
10171019
"]\n",
@@ -1105,9 +1107,9 @@
11051107
"xaxis": "x",
11061108
"y": [
11071109
8.373929949593731e-10,
1108-
1.674785989918746e-9,
1109-
2.512178984878119e-9,
1110-
3.3495719798374923e-9,
1110+
1.674785989918746e-09,
1111+
2.512178984878119e-09,
1112+
3.3495719798374923e-09,
11111113
1.2009600411861108,
11121114
2.40192007902265,
11131115
3.602880116859188,
@@ -1996,7 +1998,9 @@
19961998
" *[f\"2023-{month:02d}\" for month in range(1, 13)],\n",
19971999
"]\n",
19982000
"\n",
1999-
"epg_costs = [sim.calc(\"monthly_epg_subsidy\", period).sum() for period in time_periods]\n",
2001+
"epg_costs = [\n",
2002+
" sim.calc(\"monthly_epg_subsidy\", period).sum() for period in time_periods\n",
2003+
"]\n",
20002004
"\n",
20012005
"df = pd.DataFrame(\n",
20022006
" {\n",

docs/book/programs/gov/revenue_scotland/land-and-buildings-transaction-tax.ipynb

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,9 @@
12171217
" color=\"Label\",\n",
12181218
" title=\"Land and Buildings Transaction Tax (LBTT) rates over property price thresholds\",\n",
12191219
" )\n",
1220-
" .update_layout(yaxis_tickformat=\",.0%\", xaxis_tickprefix=\"£\", legend_title=\"\")\n",
1220+
" .update_layout(\n",
1221+
" yaxis_tickformat=\",.0%\", xaxis_tickprefix=\"£\", legend_title=\"\"\n",
1222+
" )\n",
12211223
" .update_traces(line_shape=\"hv\")\n",
12221224
")\n",
12231225
"fig = format_fig(fig)\n",
@@ -6239,7 +6241,9 @@
62396241
"lbtt_sim = sim.calculate(\"land_and_buildings_transaction_tax\")\n",
62406242
"home_price = sim.calculate(\"main_residential_property_purchased\")\n",
62416243
"\n",
6242-
"marginal_rate = (lbtt_sim[1:] - lbtt_sim[:-1]) / (home_price[1:] - home_price[:-1])\n",
6244+
"marginal_rate = (lbtt_sim[1:] - lbtt_sim[:-1]) / (\n",
6245+
" home_price[1:] - home_price[:-1]\n",
6246+
")\n",
62436247
"home_price = home_price[:-1]\n",
62446248
"\n",
62456249
"df = pd.DataFrame(\n",
@@ -6255,10 +6259,14 @@
62556259
"\n",
62566260
"# left shows marginal rate, right shows total LBTT\n",
62576261
"\n",
6258-
"fig = make_subplots(rows=1, cols=2, subplot_titles=(\"Marginal LBTT rate\", \"Total LBTT\"))\n",
6262+
"fig = make_subplots(\n",
6263+
" rows=1, cols=2, subplot_titles=(\"Marginal LBTT rate\", \"Total LBTT\")\n",
6264+
")\n",
62596265
"\n",
62606266
"fig.add_trace(\n",
6261-
" px.line(df, x=\"Home price\", y=\"Marginal LBTT rate\", title=\"Marginal LBTT rate\")\n",
6267+
" px.line(\n",
6268+
" df, x=\"Home price\", y=\"Marginal LBTT rate\", title=\"Marginal LBTT rate\"\n",
6269+
" )\n",
62626270
" .update_traces(line_shape=\"hv\")\n",
62636271
" .data[0],\n",
62646272
" row=1,\n",

0 commit comments

Comments
 (0)