Baseline forecast#
To anchor PolicyEngine to official forecasts, this page presents both the Office for Budget Responsibility (OBR)’s forecast for public sector net debt (PSND) and that implied by PolicyEngine.
PSND is generally used as the key fiscal metric in the UK, and all talk of ‘headroom’ and ‘cost’ of fiscal measures is in relation to this metric. In calculating PolicyEngine’s projection for PSND, we use the OBR’s forecast and subtract the cost and revenue projections for benefits modelled in PolicyEngine:
Income Tax
National Insurance (all classes)
VAT
Stamp Duty Land Tax
Fuel duties
Universal Credit
Child Benefit
Working Tax Credit
Child Tax Credit
Housing Benefit
Personal Independence Payment
Disability Living Allowance
TV Licence
Show code cell source
OBR_PSND = [2_540, 2_691, 2_793, 2_820, 2_903, 2_995, 3_078]
from policyengine_uk_data.storage import STORAGE_FOLDER
import pandas as pd
tax_ben = pd.read_csv(STORAGE_FOLDER / "tax_benefit.csv")
REVENUES = [
"income_tax",
"ni_employee",
"ni_employer",
"vat",
"fuel_duties",
"tv_licence_fee",
]
SPENDING = [
"universal_credit",
"tax_credits",
"housing_benefit",
"pip",
"dla",
]
tax_ben_revenues = (
tax_ben[tax_ben.name.isin(REVENUES)].set_index("name").loc[REVENUES]
)
tax_ben_spending = (
tax_ben[tax_ben.name.isin(SPENDING)].set_index("name").loc[SPENDING]
)
from policyengine_uk import Microsimulation
PE_REVENUES = [
"income_tax",
"ni_employee",
"ni_employer",
"vat",
"fuel_duty",
"tv_licence",
]
PE_SPENDING = [
"universal_credit",
"tax_credits",
"housing_benefit",
"pip",
"dla",
]
baseline = Microsimulation()
pe_psnd = []
for year in range(2022, 2029):
obr_psnd = OBR_PSND[year - 2022]
obr_totals = (
tax_ben_spending[str(year)].sum() - tax_ben_revenues[str(year)].sum()
)
pe_revenues = sum(
baseline.calculate(variable, map_to="household", period=year).sum()
/ 1e9
for variable in PE_REVENUES
)
pe_spending = sum(
baseline.calculate(variable, map_to="household", period=year).sum()
/ 1e9
for variable in PE_SPENDING
)
pe_totals = pe_spending - pe_revenues
pe_psnd.append(obr_psnd + pe_totals - obr_totals)
df = pd.DataFrame(
{
"year": range(2022, 2029),
"OBR": OBR_PSND,
"PE": pe_psnd,
}
)
Show code cell source
df["diff"] = df["PE"] - df["OBR"]
df["diff_pct"] = (df["diff"] / df["OBR"]).round(3)
df["PE"] = df["PE"].round(0)
df["diff"] = df["diff"].round(1)
df
year | OBR | PE | diff | diff_pct | |
---|---|---|---|---|---|
0 | 2022 | 2540 | 2538.0 | -1.5 | -0.001 |
1 | 2023 | 2691 | 2693.0 | 2.2 | 0.001 |
2 | 2024 | 2793 | 2800.0 | 7.5 | 0.003 |
3 | 2025 | 2820 | 2831.0 | 10.5 | 0.004 |
4 | 2026 | 2903 | 2915.0 | 12.2 | 0.004 |
5 | 2027 | 2995 | 3013.0 | 18.1 | 0.006 |
6 | 2028 | 3078 | 3106.0 | 27.8 | 0.009 |
PolicyEngine appears to predict a higher PSND trend line than the OBR, increasing linearly through to 2028-29. This is over 90% explained by PolicyEngine projecting lower income tax receipts than the OBR. It’s unclear why this is the case, and it might not actually be a bug: PolicyEngine optimises household weights to fit targets including the income tax targets, as well as income statistics. This could be the model saying that income tax receipts being lower is more consistent with other projections for the UK household sector.