From df0155c62410a95a51ba5a4b4aaba707571eb938 Mon Sep 17 00:00:00 2001 From: vahid-ahmadi Date: Wed, 26 Aug 2026 15:37:42 +0100 Subject: [PATCH] Lead the landing page with how the models connect ## Hero "Two economies, seven open models. / Each one says how far to trust it." becomes "An open platform for policy analysis and economic research." The lede is shorter and now names the six macro models rather than describing them in the abstract, each linked to its own page. ## Structure Cut 01 - the core. It opened the page with one model's detail -- a GBP 50,000 income waterfall broken down to the penny -- before the reader knew what the suite was. The microsimulation is still the core of the story; it is now told through the joins, where it is the thing everything else connects to. Cut 04 - the economy now. Live UK and US indicators belong on Forecasts, not on a page about models. This also removes the home page's only generated content, so economy/build.py no longer renders it: home_uk_now, home_us_now, render_home and the HOME_PAGE wiring are all gone rather than left pointing at markers that no longer exist, which would have failed the build with "expected one home-uk-now block, found 0". "How the models connect" moves from 03 to 01 and now explains the shape before the parts: the microsimulation knows every household and nothing about the economy around them, each macro model joins at one named point, in one of two directions, and nothing connects macro model to macro model. 02 is re-storied from "six models add what the microsimulation does not cover" to "six models, six different questions" -- each line says what you would come to that model FOR, rather than what it adds to something else. Pick by the question you have, not the model you know. Sections renumber 01 / 02 / 03. Site suite 1564 passed; contract, nav and economy build all clean. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Lcj9DDqam9KmVCfhEdnJcJ --- economy/build.py | 133 ------------------------------ index.html | 209 ++++++++++++----------------------------------- 2 files changed, 50 insertions(+), 292 deletions(-) diff --git a/economy/build.py b/economy/build.py index 0f09a4b..9eb3147 100644 --- a/economy/build.py +++ b/economy/build.py @@ -12,7 +12,6 @@ ROOT = Path(__file__).resolve().parents[1] PAGE = ROOT / "economy" / "index.html" -HOME_PAGE = ROOT / "index.html" US_PAGE = ROOT / "economy" / "us" / "index.html" @@ -443,135 +442,6 @@ def replace(html: str, name: str, value: str) -> str: return updated -def home_uk_now() -> str: - """Homepage 'UK at a glance' table: outturns beside next-open forecasts.""" - gdp = load("uk_gdp_cvm") - cpi = load("uk_cpi_yoy") - unemployment = load("uk_unemployment_rate") - forecast = json.loads( - (ROOT / "papers" / "boe-svar" / "figures" / "current_forecast.json").read_text() - ) - # Archived round file keeps its original name; the satellite's display - # name is "svar-unemployment satellite". - unemp_round = json.loads( - (ROOT / "forecasts" / "rounds" / "2026-07-28" / "okun-unemployment.json").read_text() - ) - - growth = gdp_growth(gdp) - g_now = growth[-1] - c_now = latest(cpi) - u_now = latest(unemployment) - - def next_open(fc: dict, variable: str, last_observed: str) -> tuple[str, dict]: - for period, values in fc.items(): - if period > last_observed and variable in values: - return period, values[variable] - period = list(fc)[-1] - return period, fc[period][variable] - - g_period, g_fc = next_open(forecast["forecast"], "gdp", g_now["period"]) - c_period, c_fc = next_open(forecast["forecast"], "cpi", c_now["period"]) - u_period, u_fc = next_open(unemp_round["forecast"], "unemployment", u_now["period"]) - - def rng(fc: dict) -> str: - return (f'
68% range ' - f"{fmt(fc['lo68'])}%–{fmt(fc['hi68'])}%") - - caption = ( - "ONS outturns (as of " - f"{max(gdp['vintage'], cpi['vintage'], unemployment['vintage'])}) beside " - 'archived forecast rounds. ' - 'Full horizon →' - ) - def row(name, unit, out_v, out_p, fc_v, fc_p, fc_extra): - return ( - '
\n' - f' {name} {unit}\n' - f' {out_v} {out_p}\n' - ' \n' - f' {fc_v} {fc_p}{fc_extra}\n' - "
" - ) - - def rng(fc: dict) -> str: - return f"68% range {fmt(fc['lo68'])}%\u2013{fmt(fc['hi68'])}%" - - return "\n".join( - [ - '
', - '
' - 'indicator' - 'latest outturn' - 'model near-term forecast
', - row("Real GDP growth", "y/y", f"{fmt(g_now['value'])}%", g_now["period"], - f"{fmt(g_fc['median'])}%", g_period, rng(g_fc)), - row("CPI inflation", "y/y", f"{fmt(c_now['value'])}%", c_now["period"], - f"{fmt(c_fc['median'])}%", c_period, rng(c_fc)), - row("Unemployment rate", "", f"{fmt(u_now['value'])}%", u_now["period"], - f"{fmt(u_fc['median'])}%", u_period, rng(u_fc)), - "
", - f'

{caption}

', - ] - ) - - -def home_us_now() -> str: - """Homepage 'US at a glance' table: outturns beside the LONGBASE baseline.""" - gdp = load("us_real_gdp") - cpi = load("us_cpi") - unemployment = load("us_unemployment_rate") - baseline = longbase_baseline() - - g_now = gdp_growth(gdp)[-1] - c_now = yoy_growth(cpi, 12)[-1] - u_now = latest(unemployment) - - g_base = baseline_next_open(baseline, g_now["period"]) - c_base = baseline_next_open(baseline, c_now["period"]) - u_base = baseline_next_open(baseline, u_now["period"]) - - caption = ( - "FRED outturns (as of " - f"{max(gdp['vintage'], cpi['vintage'], unemployment['vintage'])}) beside " - "the FRB/US LONGBASE conditioning baseline — not a forecast. " - 'Full sources →' - ) - def row(name, unit, out_v, out_p, base_v, base_p): - return ( - '
\n' - f' {name} {unit}\n' - f' {out_v} {out_p}\n' - ' \n' - f' {base_v} {base_p}\n' - "
" - ) - - return "\n".join( - [ - '
', - '
' - 'indicator' - 'latest outturn' - 'LONGBASE baseline
', - row("Real GDP growth", "y/y", f"{fmt(g_now['value'])}%", g_now["period"], - f"{fmt(g_base['gdp_yoy_pct'])}%", g_base["quarter"]), - row("CPI inflation", "y/y", f"{fmt(c_now['value'])}%", c_now["period"], - f"{fmt(c_base['cpi_yoy_pct'])}%", c_base["quarter"]), - row("Unemployment rate", "", f"{fmt(u_now['value'])}%", u_now["period"], - f"{fmt(u_base['unemployment_pct'])}%", u_base["quarter"]), - "
", - f'

{caption}

', - ] - ) - - -def render_home() -> str: - html = HOME_PAGE.read_text() - html = replace(html, "home-uk-now", home_uk_now()) - html = replace(html, "home-us-now", home_us_now()) - return html - - def render_uk() -> str: """The UK hub. ``economy-topics`` belongs to economy/topics.py, not here.""" html = PAGE.read_text() @@ -608,13 +478,11 @@ def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--check", action="store_true") args = parser.parse_args() - rendered_home = render_home() rendered_uk = render_uk() rendered_us = render_us() if args.check: stale = [] for path, rendered in ( - (HOME_PAGE, rendered_home), (PAGE, rendered_uk), (US_PAGE, rendered_us), ): @@ -625,7 +493,6 @@ def main() -> int: return 1 print("UK and US Economy pages match committed data") return 0 - HOME_PAGE.write_text(rendered_home) PAGE.write_text(rendered_uk) US_PAGE.write_text(rendered_us) print("updated UK and US Economy pages") diff --git a/index.html b/index.html index 4d08ff8..9619925 100644 --- a/index.html +++ b/index.html @@ -44,14 +44,18 @@

one core model · six extensions · uk & us

- Two economies, seven open models. - Each one says how far to trust it. + An open platform for policy analysis + and economic research.

- The core is the tax-benefit engine behind policyengine.org, running - here in process for the UK and the US: what a reform does to each - household, and what it costs. Six macro models extend it with GDP, - inflation, interest rates, long-run effects and climate. + The tax-benefit engine behind policyengine.org — what a reform + does to each UK or US household, and what it costs — joined to six + macro models: the OBR emulator, the + Bank of England SVAR, the + Fed’s FRB/US, a + US HANK, an + overlapping-generations model and + DEFINE-UK for climate.

Run a hosted model @@ -146,99 +150,11 @@

Every model here says where it fails.

-
-
- 01 — the core -

Start at the microsimulation.

-

A reform goes in; household incomes, revenue and a distribution come out. It does not forecast — that is what the six extensions are for.

-
- -
pe-microsimUKUShosted
-

PolicyEngine tax-benefit microsimulation

-

The only model here covering both countries, and the only one that works at household level. Tax and benefit law written as code, run over survey data.

- open the microsimulation → -
-
-
-

one household — exact

-
-
-
UK employment income
-
£50,000
-
-
-
Income tax - personal allowance £12,570: the first £12,570 is not taxed
-
£7,486
-
-
-
National Insurance
-
£2,994
-
-
-
=Net income
-
£39,520
-
-
-

Household arithmetic is exact: 50,000 − 7,486 − 2,994 = 39,520, to the penny. See the case →

-
-
-

the whole population — an estimate

-
    -
  • Population totals are survey-weighted estimates that inherit survey uncertainty, published without an interval.
  • -
  • The HMRC costing comparison is a benchmark, not a validation.
  • -
-

Same run, same code — but the household case can be checked line by line and a national total cannot. How far to trust it →

-
-
-
- -
-
- 02 — the extensions -

Six models add what the microsimulation does not cover.

-

The microsimulation returns no GDP, inflation or interest rates. Each model below adds some of them — the evidence for it, and the limit of that evidence, on the same line.

-
-
    -
  1. - obr-macroanchored replication -

    Adds the knock-on effects on the UK economy.

    -

    Tracks the March 2026 EFO to 0.15% GDP MAPE with the anchors held; free-running, 4.48%.

    -
  2. -
  3. - boe-svarforecast evaluation -

    Adds a UK forecast for the next few quarters.

    -

    Replicates the paper’s GDP decomposition (37.4% vs ~40%), 8pp short on CPI. Against a drifting random walk, no forecast skill survives the 64 tests run.

    -
  4. -
  5. - frb-ussoftware replication -

    Adds how the US economy responds to a shock.

    -

    Matches the Fed’s own pyfrbus inside its two releases’ disagreement (~1×10⁻⁸); no predictive claim.

    -
  6. -
  7. - us-hankpublished replication -

    Adds who gains and loses, by wealth group.

    -

    Solves Auclert et al. (2021) to four decimals — the headline targets are inputs, not results.

    -
  8. -
  9. - psl-ogcalibration only -

    Adds long-run effects on work, saving and capital.

    -

    +1pp on the basic rate: GDP −£5.0bn (−0.14%), revenue +0.29pp of GDP by 2030. Targets met by construction; no independent outcome benchmark exists.

    -
  10. -
  11. - define-ukpartial replication -

    Adds UK climate-policy scenarios: emissions, energy, green investment.

    -

    Baseline replicates the manual; scenarios are design-gated and paper-anchored. Deltas only, never levels.

    -
  12. -
-

Inspect all seven models, compare them side by side, or read the evidence for each.

-
-
- 03 — the joins + 01 — how it fits together

How the models connect.

-

One score can run several models in a row: score_reform accepts microsim, obr, og and og+microsim.

+

The microsimulation is the core: it knows every household, and nothing about the economy around them. Each macro model joins it at one named point, in one of two directions — a reform’s cost goes out to a macro model and feedback comes back (score_reform accepts microsim, obr, og, og+microsim), or a macro path comes in and is pushed down onto households as incidence. Nothing connects macro model to macro model.

Every bridge starts or ends at the microsimulation. @@ -297,75 +213,50 @@

How the models connect.

united statesNo US macro bridge exists, so a US reform is scored statically.

-
+
- 04 — the economy now -

The latest data, and what the models expect next.

-

ONS and FRED outturns beside the archived forecast rounds. boe-svar is the only forecaster here and it is UK-only; the US column shows the path FRB/US starts from, which is not a forecast.

-
-
- - + 02 — what each one is for +

Six models, six different questions.

+

Pick by the question you have, not by the model you know. Each line is what that model is for, and what its evidence does and does not support.

-
-
- -
-
indicatorlatest outturnmodel near-term forecast
-
- Real GDP growth y/y - 0.9% 2026Q1 - - 1.1% 2026Q268% range 0.6%–1.7% -
-
- CPI inflation y/y - 2.8% 2026Q2 - - 2.5% 2026Q368% range 1.8%–3.3% -
-
- Unemployment rate - 4.9% 2026Q2 - - 5.0% 2026Q368% range 4.9%–5.0% -
-
-

ONS outturns (as of 2026-08-26) beside archived forecast rounds. Full horizon →

- -
- -
+
    +
  1. + obr-macroanchored replication +

    For scoring a UK reform with its knock-on effects on GDP, not just its cost.

    +

    Tracks the March 2026 EFO to 0.15% GDP MAPE with the anchors held; free-running, 4.48%.

    +
  2. +
  3. + boe-svarforecast evaluation +

    For reading what has been driving UK GDP and inflation, and what comes next.

    +

    Replicates the paper’s GDP decomposition (37.4% vs ~40%), 8pp short on CPI. Against a drifting random walk, no forecast skill survives the 64 tests run.

    +
  4. +
  5. + frb-ussoftware replication +

    For tracing how the US economy responds to a rate or spending shock.

    +

    Matches the Fed’s own pyfrbus inside its two releases’ disagreement (~1×10⁻⁸); no predictive claim.

    +
  6. +
  7. + us-hankpublished replication +

    For seeing which US households absorb a shock, by wealth.

    +

    Solves Auclert et al. (2021) to four decimals — the headline targets are inputs, not results.

    +
  8. +
  9. + psl-ogcalibration only +

    For the decades-long effects on work, saving and the capital stock.

    +

    +1pp on the basic rate: GDP −£5.0bn (−0.14%), revenue +0.29pp of GDP by 2030. Targets met by construction; no independent outcome benchmark exists.

    +
  10. +
  11. + define-ukpartial replication +

    For UK climate-policy scenarios: emissions, energy, green investment.

    +

    Baseline replicates the manual; scenarios are design-gated and paper-anchored. Deltas only, never levels.

    +
  12. +
+

Inspect all seven models, compare them side by side, or read the evidence for each.

- 05 — start using it + 03 — start using it

Run a hosted model, or use the code directly.

Connect the public MCP server with no PolicyEngine account or API key, use the shared CLI, or call each Python package directly.