Models / OBR macro

model 01 — OBR macroeconometric model · obr-macro · UK · hosted

UK fiscal reform, quarter by quarter.

Run selected UK tax and spending scenarios and trace GDP, consumption, and investment over 3–5 years against the March 2026 EFO baseline. Borrowing is not yet returned by the PolicyEngine Macro adapter.

01 — what it is

An open Python implementation of the OBR's published equations.

The OBR's model is a computational representation of the UK National Accounts — first built by HM Treasury in 1970 and jointly maintained by the OBR and the Treasury since 2010. The OBR publishes its full equation listing, but as EViews source that few can run. This project transpiles that listing to Python and solves it: 372 published equations in 17 thematic groups over 636 variables — 383 solved by the model (roughly 29 econometrically estimated, 216 identities, and 138 calibrated or technical relationships) and 253 exogenous inputs, which are the judgements and assumptions you get to change.

It is a structural macroeconometric model in the HM Treasury / Cowles-Commission tradition. Where the OLG model derives behaviour from optimising households and firms, this one reproduces the estimated relationships the official forecaster actually relies on.

March 2026 OBR outlook for real GDP and household consumption Indexed line chart from 2026Q1 to 2031Q1. Real GDP rises from 100 to 108.0 and household consumption from 100 to 108.8. March 2026 EFO outlook · 2026Q1 = 100 100 102 104 106 108 110 real GDPconsumption 2026Q1 2027Q1 2028Q1 2029Q1 2030Q1 2031Q1
Latest official baseline available on 21 July 2026. Levels are indexed to 2026Q1 so the two series can share one honest scale. Source: OBR March 2026 detailed forecast tables; committed values in papers/obr-macro/figures/current_outlook.csv.
02 — how it works

Transpile, solve, swap the closure.

The transpile–solve–shock pipeline, stage by stage
stagewhat it does
Transpile Rewrites every EViews line — dlog(), @elem, lags like X(-1), error-correction terms — into an equivalent Python expression, compiled once per solve.
Initialise Loads history from the OBR's detailed forecast tables. Of the 261 endogenous variables the listing referenced but the outlook did not print, 199 had ONS series codes; a vendored 348-series ONS snapshot supplies them, giving the circular fiscal and financial sub-blocks real balancing data.
Gauss–Seidel solve The equations depend on each other, so each quarter is solved simultaneously: cycle, update, repeat to convergence. Quarters then solve sequentially forward. A twelve-quarter anchored solve takes roughly 30–40 seconds single-threaded; a reform run is two such solves.
Anchor Add-factors — the residuals reconciling each behavioural equation to data — are set so the baseline reproduces the published EFO. This is the OBR's own device, used the OBR's own way.
Closure swap The GDP identity is switched off in the OBR file, so for a shock GDPM is swapped in and inventories (DINV) become the residual — output responds to the policy instead of being fixed.
Shock & compare Solves shocked and baseline paths with identical structure and reports the difference, isolating the policy effect from the underlying forecast.

For forecasting rather than one-off shocks, the emulator fits add-factors over a base window of 2024Q1–2025Q4, holds them constant, and projects to 2027Q4 with exogenous inputs extrapolated (seasonal cash series at their trailing annual mean). Scenario solves run twelve quarters, 2025Q1–2027Q4. The anchored horizon extends to 2031Q1 (GDP reproduced to 0.29% at 2031Q1); anchored unemployment (LFSUR) is unreliable beyond 2027Q4, drifting to 0.9% against the EFO's 4.1% by 2031Q1.

03 — how to run it

Shock a variable, read the deviation.

The package installs straight from GitHub — the equation listing, the March 2026 EFO forecast tables, and the ONS snapshot ship inside it, so there is no data step. A reform is one call: pick an exogenous variable, apply a shock, read the deviation from baseline.

pip install git+https://github.com/PolicyEngine/obr-macroeconomic-model
from obr_macro import run_reform

# £5bn/yr government spending shock (£1.25bn per quarter)
results = run_reform(
    name="Fiscal Stimulus",
    var="CGG",        # government consumption (exogenous)
    shock=1250,        # £1.25bn per quarter, in £m
    periods=12,
)
print(results[["period", "delta_gdp_bn", "pct_gdp"]])
# ΔGDP ≈ £1.25bn in the impact quarter, near-flat thereafter —
# under the demand closure the impact multiplier is ~1 by construction

two solves, so about a minute. run the five bundled scenarios with charts via python -m obr_macro.reform_analysis — a £5bn spending rise, a 5pp corporation-tax cut and rise, £10bn public real investment (~£3bn nominal CGIPS per quarter), and a £10bn austerity cut. Charts land in outputs/.

Reforms enter through the 253 exogenous inputs; results come out of the 383 the model solves. The test is simply whether a variable has its own equation. The most useful levers:

The most useful OBR model levers and readouts, with units
codevariablerole & units
CGGGovernment consumptionexogenous — spending shocks, £m per quarter
CGIPSCentral government investment (nominal)exogenous — public investment, £m per quarter
TCPROCorporation tax rateexogenous — rate, e.g. −0.05 for a 5pp cut
HHDI_ADDFACTORExternally costed household reformvirtual instrument — quarterly £m; positive revenue raised lowers HHDI through a held add-factor
R / GILT / RXBank Rate · gilt yields · exchange rateexogenous — monetary & market assumptions
GDPMGDP at market pricesendogenous — headline output
CONSPrivate consumptionendogenous
IBUS / IFBusiness / total investmentendogenous
LFSURUnemployment rateendogenous — rate, pp

the full searchable glossary — all 636 codes with descriptions, ONS series and equations — is the model package's variable and equation references.

04 — what it can answer

Second-round effects on a static costing.

The emulator's designed job is the second half of the official workflow: microsimulation supplies the direct Exchequer effect, the macro model supplies the economy-wide feedback. A static-costing bridge wires PolicyEngine's microsimulation into the emulator so a statutory reform can be scored end to end.

Worked example — 1p on the basic rate of income tax from April 2026, scored 2026–2030. PolicyEngine puts the direct yield at £6.46bn in 2026, rising to £7.38bn by 2030; HMRC's June 2025 ready reckoner puts 1p at £6.9bn in 2026–27 rising to about £8.2bn by 2028–29, so the static costing sits inside the official range, toward its lower end. Converted to a positive quarterly revenue-costing path of £1.62bn to £1.85bn, the HHDI_ADDFACTOR adapter applies the corresponding negative held add-factor to household disposable income. Propagated through the consumption function, GDP falls 0.020% (−£0.14bn) on impact in 2026Q1, deepening to 0.058% (−£0.40bn) by 2027Q4 on the March 2026 baseline — the sign and order of magnitude the OBR's own indirect-effect conventions imply.

It cannot tell you who wins and loses — pair it with PolicyEngine for distributional analysis. It cannot give you a long-run structural counterfactual — that is the OLG model. And it cannot tell you what state the economy is in today — that is the SVAR.
05 — how far to trust it

Excellent anchored. Weak free-running. Both published.

There are three configurations and they earn very different levels of trust. Anchored is a by-construction invariant, not a forecast claim: the add-factors absorb the tracking error, which is exactly what anchoring means, and CI hard-fails the build if it slips.

anchored — real GDP 0.15% MAPE vs published EFO; CI fails at 1%
anchored — consumption 0.25% MAPE vs published EFO; CI fails at 1%
held-add-factor forecast 0.37% MAPE real GDP, 2026Q1–2027Q4 window; 6 of 8 computed in band
free-running 5.75% MAPE real GDP; 4 of 11 computed in band — report-only
The three emulator configurations and how each one scores
configurationwhat it ishow it scores
Anchored Add-factors on, matched to the March 2026 EFO. The configuration every reform score uses. GDP 0.15% MAPE, consumption 0.25% over 2025Q1–2027Q4; the horizon extends to 2031Q1, where GDP is reproduced to 0.29% (anchored unemployment is unreliable beyond 2027Q4). Hard-gated in CI, alongside finiteness and a 0.5%-of-GDP expenditure-identity check. Structural sign invariants gated too.
Held add-factors Add-factors fitted 2024Q1–2025Q4, held flat, projected to 2027Q4 — a genuine forecast. Scored over 2026Q1–2027Q4 on the March 2026 baseline. GDP 0.37%, consumption 0.33%; 6 of 8 computed variables within band. (The working paper's November-2025-vintage scorecard, over its own longer window, reports GDP 2.2% and consumption 3.6%.)
Free-running De-seeded, add-factors off. The raw structural dynamics with no OBR judgement. Weak, and reported as such. GDP 5.75%, consumption 9.56%, household income 14.15%, business investment 16.12%, company profits 79.80%. The model contracts 1–2%/yr while the EFO grows.

"In band" collapses per-metric thresholds — rates within 1.0pp, net balances within 1.5% of GDP, levels within 10% MAPE. Two caveats travel with the forecast configuration: it is initialised at the EFO values it is scored against, and its add-factor base window extends into quarters that are themselves OBR forecast rather than outturn, so the held add-factors partly encode "agree with the OBR". Full scorecards in the working paper; charts and source data in validation.

This is an independent implementation built from the OBR's published model code and forecast data. It is not produced, maintained, or endorsed by the Office for Budget Responsibility, and its results should not be presented as official OBR estimates.

06 — limits

What is broken, and what is merely absent.

Known limits of the OBR emulator
limitdetail
Two household-income equations never fire The listing's only bare log() left-hand sides — log(HHTFA) and log(NDIVHH) — do not execute, because their right-hand-side inputs MAJGDP and CORP are absent from the databank entirely. The profits → dividends → household-income channel therefore does not run: a 5pp corporation-tax rise moves FYCPR by −£1,780m while ΔNDIVHH is exactly zero. A parser fix landed and is numerically inert; reviving the channel needs a CORP series, a calibration decision we have not made. Every published figure is computed with these channels inert.
Impact multiplier ~1 by construction Under the demand closure a spending shock lands directly in the GDP identity and the behavioural second round is largely inactive. The OBR's published impact multiplier for current spending is 0.6.
Corporation-tax closure is specialised The investment closure activates the published TCPRO → cost-of-capital → investment chain and is available through obr_shock. It reconstructs the business-investment equation from the OBR's commented, truncated published line, anchors its level with held add-factors, and freezes MSGVA, PIF, and PIRHH to a shared tracking path to prevent uncalibrated feedback. CI hard-gates the expected sign and a £50bn-per-quarter bound; this is a controlled scenario closure, not a fully calibrated supply block.
Passthrough channels Exports, imports and CPI are exogenous in this setup, held at the OBR value. They score 0.00% error without being behavioural wins — 10 of 21 scorecard lines are passthroughs and are labelled as such.
Approximated add-factors Recent corrections are averaged and held flat. The OBR's judgemental, quarter-by-quarter add-factors are not reproduced.
Vintage October 2025 equation listing, March 2026 EFO, current-vintage ONS series. Where the ONS has revised history the identities don't close exactly, and that slack lands in the add-factors. The working paper's results remain a November 2025 EFO vintage study.
No behavioural micro Aggregate equations only. Distributional questions belong to PolicyEngine.