Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ <h2>Five model classes, side by side.</h2>
<tr><th scope="row"><strong>Question class</strong></th><td>Selected near-term UK fiscal and macro scenarios</td><td>What drove the UK economy at the latest model-data vintage; short-run forecasts and revision narratives</td><td>US macro dynamics: monetary and fiscal shocks, quarter by quarter</td><td>Long-run incentives: labour supply, saving, the capital stock over decades</td><td>What a specific family pays and receives; distributional effects</td></tr>
<tr><th scope="row"><strong>Method</strong></th><td>Structural macroeconometric emulator — Gauss–Seidel over 372 equations per quarter</td><td>Bayesian structural VAR — posterior sampling with zero + sign restrictions naming the shocks</td><td>Large-scale macroeconometric model — 284 equations solved under VAR expectations</td><td>Dynamic general equilibrium (OLG) — steady-state root-find and transition-path iteration</td><td>Static tax-benefit microsimulation — direct rule evaluation, no behavioural response</td></tr>
<tr><th scope="row"><strong>Country</strong></th><td>UK</td><td>UK</td><td>US</td><td>UK (OG-UK)</td><td>UK &amp; US</td></tr>
<tr><th scope="row"><strong>Data vintage</strong></th><td>OBR <em>Economic and Fiscal Outlook</em>, March 2026</td><td>ONS, BoE and FRED series — coefficients estimated through 2023Q2; conditioned through the 2026Q1 data edge</td><td>The Fed's <code>model.xml</code> and LONGBASE database, April 2026</td><td>ONS, OBR and BoE national accounts as calibration targets; OG-UK 0.3.2 (from GitHub)</td><td>UK &amp; US statute; enhanced FRS 2023–24 microdata for UK population runs</td></tr>
<tr><th scope="row"><strong>Data vintage</strong></th><td>OBR <em>Economic and Fiscal Outlook</em>, March 2026</td><td>ONS, BoE and FRED series — coefficients estimated through 2025Q1; conditioned through the 2026Q1 data edge</td><td>The Fed's <code>model.xml</code> and LONGBASE database, April 2026</td><td>ONS, OBR and BoE national accounts as calibration targets; OG-UK 0.3.2 (from GitHub)</td><td>UK &amp; US statute; enhanced FRS 2023–24 microdata for UK population runs</td></tr>
<tr><th scope="row"><strong>Evidence class</strong> (<a href="/validation/">numbers →</a>)</th><td>Validated for selected scenarios; published anchor is not out-of-sample validation</td><td>Validated replication for selected outputs</td><td>Validated software replication; substantive comparisons are approximate</td><td>Research prototype; calibrated counterfactual with no ground truth</td><td>Deterministic rule calculation; population estimates add survey uncertainty</td></tr>
<tr><th scope="row"><strong>Reform scoring</strong></th><td>Yes — shocks to exogenous model variables; static-costing bridge from PolicyEngine reforms</td><td>No — the baseline/conditioning member: it reads the economy reforms are scored against</td><td>No — shock experiments only (funds-rate or fiscal shocks under VAR expectations); <code>score_reform</code> refuses <code>model="frbus"</code>, as no PolicyEngine reform bridge exists</td><td>Yes — a PolicyEngine policy translated into estimated tax functions</td><td>Yes — a PolicyEngine parameter + a new value, applied directly to households</td></tr>
<tr><th scope="row"><strong>Cannot establish</strong></th><td>Arbitrary reform incidence or borrowing through the current adapter</td><td>The causal effect of a specific statutory reform</td><td>PolicyEngine reform effects, anticipated-policy paths, or MCE scenarios</td><td>A short-run forecast or independently validated reform effect</td><td>GDP, inflation, interest rates, or macro feedback</td></tr>
Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ <h3>OBR macroeconometric model</h3>
<h3>Bank of England structural VAR model</h3>
<p>
Diagnose the latest model-data vintage and forecast GDP and inflation
with uncertainty ranges. Coefficients are estimated through 2023Q2;
with uncertainty ranges. Coefficients are estimated through 2025Q1;
the model does not score reforms.
</p>
<span class="strategy-paper-link mono">read the model →</span>
Expand Down
4 changes: 2 additions & 2 deletions integration/src/policyengine_macro/capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@
"runtime": "minutes per estimation and identification run",
"uncertainty": "posterior 68% and 90% intervals",
"status": "validated replication for selected outputs",
"estimation_sample": "1992Q1-2023Q2",
"estimation_sample": "1992Q1-2025Q1",
"data_edge": "2026Q1",
"data_vintage": "2026Q1 conditioning data; estimation ends 2023Q2",
"data_vintage": "2026Q1 conditioning data; estimation ends 2025Q1",
},
"frb-us": {
"display_name": "Federal Reserve FRB-US implementation",
Expand Down
29 changes: 18 additions & 11 deletions integration/src/policyengine_macro/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,17 +725,24 @@ def _covid_dummies(index) -> np.ndarray:
# Estimation window for the hosted SVAR (start of Great-Moderation sample;
# end held fixed so hosted results only move on a deliberate refresh). The
# provenance strings are derived from the actual df_est index, never retyped.
#
# 2026-07-24 refresh: bumped 2023Q2 -> 2025Q1 (126 -> 133 observations) to match
# the sample boe-var-model's production replication now runs on. Held one
# quarter short of the 2026Q1 data edge, matching the repo, so the forecast
# origin sits outside the estimation sample.
_SVAR_EST_START = "1992Q1"
_SVAR_EST_END = "2023Q2"

# Default posterior draws for hosted calls. Measured on the 2026Q1 vintage
# (Apple Silicon, single process): draws=500 -> 42 accepted, ESS 15.5, ~23s;
# 1000 -> 76 accepted, ESS 26.5, ~44s; 2000 -> 165 accepted, ESS 63.6,
# ~117s. Acceptance is ~8% and ESS ~3% of draws, so 2000 is the smallest
# default that clears the 100-accepted-draws reliability threshold at
# roughly two minutes of first-call runtime (results are cached in-process).
# Full ESS >= 100 needs ~3500 draws (~3.5 min); a `warnings` entry flags the
# residual ESS shortfall honestly instead of hiding it.
_SVAR_EST_END = "2025Q1"

# Default posterior draws for hosted calls. Re-measured on the 1992Q1-2025Q1
# estimation sample (seed 0), since the longer sample tightens the posterior
# and lowers the sign-restriction acceptance rate: draws=500 -> 35 accepted,
# ESS 13.6; 1000 -> 69, ESS 31.5; 2000 -> 135, ESS 65.3; 3500 -> 233,
# ESS 101.4. Acceptance is ~6.8% and ESS ~3.3% of draws (was ~8% and ~3% on
# the old 2023Q2 sample), so 2000 remains the smallest default that clears
# the 100-accepted-draws reliability threshold -- 1000 would not (69).
# Full ESS >= 100 still needs ~3500 draws; a `warnings` entry flags the
# residual ESS shortfall honestly instead of hiding it. First-call runtime is
# a couple of minutes end to end and results are cached in-process.
_SVAR_DEFAULT_DRAWS = 2000


Expand Down Expand Up @@ -2705,7 +2712,7 @@ def svar_summary() -> dict:
"provenance": _provenance(
model_id="boe-svar",
distribution="boe-var-model",
data_vintage="estimation through 2023Q2; conditioning data through 2026Q1",
data_vintage="estimation through 2025Q1; conditioning data through 2026Q1",
baseline="committed replication and forecast artefacts",
),
}
Expand Down
12 changes: 6 additions & 6 deletions integration/src/policyengine_macro/mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ def frbus_summary() -> dict:
def forecast_uk(horizons: int = 12, draws: int = 2000) -> dict:
"""Forecast UK YoY GDP growth and CPI inflation with the UK SVAR model.

Estimates a Bayesian VAR (1992Q1-2023Q2 sample, sign-identified structural
Estimates a Bayesian VAR (1992Q1-2025Q1 sample, sign-identified structural
shocks) and simulates the predictive distribution from the latest data
quarter. Returns, per future quarter, the median and 68%/90% bands for YoY
GDP growth and YoY CPI inflation, both in percent. The response includes a
Expand All @@ -299,11 +299,11 @@ def forecast_uk(horizons: int = 12, draws: int = 2000) -> dict:

Args:
horizons: Forecast horizon in quarters (default 12 = 3 years).
draws: Posterior draws (default 2000: ~165 accepted draws, ESS ~64,
about two minutes of runtime on first call; ~3500 draws reaches
ESS >= 100 in ~3.5 minutes; 500 responds in ~25s but yields ESS
~15 and a warning). Results are cached in-process, so repeated
calls with the same (horizons, draws) are instant.
draws: Posterior draws (default 2000: ~135 accepted draws, ESS ~65,
a couple of minutes on first call; ~3500 draws reaches ESS >= 100;
500 is faster but yields ~35 accepted, ESS ~14, and a warning).
Results are cached in-process, so repeated calls with the same
(horizons, draws) are instant.
"""
return core.svar_forecast(horizons=horizons, draws=draws)

Expand Down
21 changes: 18 additions & 3 deletions integration/tests/test_site_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,25 @@ def _read(path: str) -> str:


def test_svar_site_uses_runtime_estimation_endpoint():
"""The published estimation endpoint must match the one the server actually
estimates on.

Derived from ``_SVAR_EST_END`` rather than hardcoded: the point of this
guard is to catch the site drifting away from the runtime, and a literal
makes it fail on the refresh it is supposed to permit while still missing
drift in the other direction."""
from policyengine_macro.core import _SVAR_EST_END

pages = _read("svar/index.html") + _read("docs/index.html")
assert "estimated through 2023Q2" in pages
assert "estimation sample to 2025Q1" not in pages
assert "Estimation to 2025Q1" not in pages
assert f"estimated through {_SVAR_EST_END}" in pages

# No other quarter may be presented as the estimation endpoint.
stale = {
q
for q in re.findall(r"estimat\w*\s+(?:through|to)\s+(\d{4}Q[1-4])", pages)
if q != _SVAR_EST_END
}
assert not stale, f"site quotes stale estimation endpoint(s): {sorted(stale)}"


def test_og_is_not_labelled_as_us_model():
Expand Down
2 changes: 1 addition & 1 deletion models/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ <h1 class="reveal page-title" style="--d:1">Choose the model that fits the quest
<div class="band-head"><span class="kicker mono">all models</span><h2>Inspect the model before you use it.</h2><p class="section-intro">Each guide covers inputs, outputs, method, validation, limitations, and how to run the model.</p></div>
<div class="strategy-grid">
<a class="strategy-card" href="/obr/"><header><span class="card-id mono">obr-macro</span><span class="tag-row"><span class="tag tag-country">UK</span><span class="tag tag-hosted">hosted</span></span></header><h3>OBR macroeconometric model</h3><p><strong>Selected near-term scenarios.</strong> Trace GDP, consumption, and investment against the official forecast. Validated for selected shocks; borrowing is not yet returned.</p><span class="strategy-paper-link mono">open model →</span></a>
<a class="strategy-card" href="/svar/"><header><span class="card-id mono">boe-svar</span><span class="tag-row"><span class="tag tag-country">UK</span><span class="tag tag-hosted">hosted</span></span></header><h3>Bank of England structural VAR</h3><p><strong>UK conditions and forecasts.</strong> Identify shocks and forecast GDP and inflation with uncertainty ranges. Estimated through 2023Q2; conditioned through 2026Q1.</p><span class="strategy-paper-link mono">open model →</span></a>
<a class="strategy-card" href="/svar/"><header><span class="card-id mono">boe-svar</span><span class="tag-row"><span class="tag tag-country">UK</span><span class="tag tag-hosted">hosted</span></span></header><h3>Bank of England structural VAR</h3><p><strong>UK conditions and forecasts.</strong> Identify shocks and forecast GDP and inflation with uncertainty ranges. Estimated through 2025Q1; conditioned through 2026Q1.</p><span class="strategy-paper-link mono">open model →</span></a>
<a class="strategy-card" href="/frb-us/"><header><span class="card-id mono">frb-us</span><span class="tag-row"><span class="tag tag-country">US</span><span class="tag tag-hosted">hosted</span></span></header><h3>Federal Reserve macroeconomic model</h3><p><strong>Selected US shocks.</strong> Run raw monetary and fiscal levers under VAR expectations. No reform bridge or model-consistent expectations.</p><span class="strategy-paper-link mono">open model →</span></a>
<a class="strategy-card" href="/olg/"><header><span class="card-id mono">psl-og</span><span class="tag-row"><span class="tag tag-country">UK</span><span class="tag tag-local">local</span><span class="tag tag-experimental">experimental</span></span></header><h3>PSL overlapping generations</h3><p><strong>Long-run incentives and capital.</strong> Run local steady-state reform analysis; transition paths are available from the package. Calibrated, not outcome-validated.</p><span class="strategy-paper-link mono">open model →</span></a>
<a class="strategy-card" href="/pe/"><header><span class="card-id mono">pe-microsim</span><span class="tag-row"><span class="tag tag-country">UK</span><span class="tag tag-country">US</span><span class="tag tag-hosted">hosted</span></span></header><h3>PolicyEngine microsimulation</h3><p><strong>Households and distribution.</strong> Calculate taxes, benefits, net income, and population impacts. Population results depend on survey data; no macro feedback.</p><span class="strategy-paper-link mono">open model →</span></a>
Expand Down
6 changes: 3 additions & 3 deletions svar/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ <h2>Eight variables, six named shocks.</h2>
<div class="prose">
<p>
A VAR in eight quarterly variables — three global, five UK — estimated
over the inflation-targeting era — <strong>1992Q1–2023Q2</strong> in
over the inflation-targeting era — <strong>1992Q1–2025Q1</strong> in
both the paper replication and the hosted adapter. The hosted forecast
conditions on observations through <strong>2026Q1</strong>, but does
not re-estimate its coefficients on those later observations. It is estimated in
Expand Down Expand Up @@ -178,7 +178,7 @@ <h2>Eight variables, six named shocks.</h2>
so medians and bands are correct but noisier than the paper's. The
hosted forecast conditions on the current vintage — data through
2026Q1 — with 6,000 draws (430 accepted, ESS 197.9), while retaining
the 2023Q2 coefficient-estimation endpoint.
the 2025Q1 coefficient-estimation endpoint.
</p>
</div>
</section>
Expand Down Expand Up @@ -368,7 +368,7 @@ <h2>Where this departs from the Bank's model.</h2>
<tbody>
<tr>
<th scope="row"><strong>Frozen data edge</strong></th>
<td>Coefficient estimation to 2023Q2; conditioning data to 2026Q1 (refreshed July 2026). Results shift with data revisions; the replication claims are scored on the paper's original sample.</td>
<td>Coefficient estimation to 2025Q1; conditioning data to 2026Q1 (refreshed July 2026). Results shift with data revisions; the replication claims are scored on the paper's original sample.</td>
</tr>
<tr>
<th scope="row"><strong>Proxied world aggregates</strong></th>
Expand Down