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

Hide 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,
    }
)
Hide 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.