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.