Methodology
Every weight, every input, every transformation. Copy this page to your own spreadsheet and reconcile the numbers — that is the whole point.
How the score is built
- Collect. Each indicator is pulled from a named public source on a defined cadence (see the table per pillar).
- Normalise. Each indicator's current reading is mapped to its empirical percentile against an expanding baseline running from
2019-01-01to today, excluding the COVID shock window2020-04-01–2020-06-30. The baseline includes today's reading itself; for series with hundreds of observations, this contributes <1/n to the rank and is harmless. We also compute a z-score against the same baseline and surface it on the homepage tiles for context, but the score itself does not use the z-score as an input.
Why this window: 2019-01-01 is the earliest date for which the four-pillar mix (gilt yields, OBR fiscal vintages, ONS LMS, MHCLG quarterly housing) is jointly available without splicing methodology breaks. The upper bound is "today" — every recompute folds today's reading back into the baseline, so the percentile mass shifts glacially as new data arrives. The 2020-Q2 exclusion drops the three-month window when GDP fell ~20% in a quarter and gilt issuance broke 9% of GDP — including those points would mean a normal-times reading like "gilt 30y at 5%" landed at the 60th percentile rather than its true ~95th, because the dispersion would be artificially fattened by a shock that has now passed. The exclusion is fixed and disclosed (versus a moving window or a "robust" trimming that would silently change the score). - Direct. Where a rising raw value is bad for the underlying condition (e.g. gilt yields, borrowing, inactivity), we invert the percentile (
p → 1 - p) so every indicator yields a public score on the same axis: higher = more room to move, lower = closer to the rope giving way. - Bound to [0, 100]. The direction-adjusted percentile is multiplied by 100. The chart below illustrates the shape, but note: production uses the empirical CDF over each indicator's actual baseline, not a normal CDF — the shape varies per indicator (gilt yields are roughly log-normal; planning consents heavily skewed).
- Pillar score. Weighted arithmetic mean of the pillar's indicators. Arithmetic at the pillar level means individual inputs are additive and debuggable.
- Headline Tightrope Score. Weighted geometric mean of the four pillar scores. Geometric at the headline level means one weak pillar pulls the headline down hard — the correct behaviour for a tightrope metric where one failing support cannot be fully offset elsewhere.
Score bands
The headline and pillar scores are bucketed into five bands at 20-point steps. Since score schema v2, lower scores are worse: critical is 0–20 and slack is 80–100. The same thresholds drive colour choices throughout the site and the OG-card rendering; change one and the tests in packages/shared/src/bands.test.ts flag the drift.
| Range | Band | Editorial label | Swatch |
|---|---|---|---|
| 0 ≤ score < 20 | Critical | Off the wire | #C84B3C |
| 20 ≤ score < 40 | Acute | Running out of rope | #FE5500 |
| 40 ≤ score < 60 | Strained | Wire wobbling | #EE9944 |
| 60 ≤ score < 80 | Steady | Holding the line | #79CAC4 |
| 80 ≤ score ≤ 100 | Slack | Room to move | #5FB27C |
ECDF — schematic of the percentile → [0, 100] mapping
Schematic only: y-axis is the [0, 100] Tightrope Score; x-axis is a value's rank in the baseline (illustrated here against a standard-normal shape for legibility). Production uses each indicator's actual empirical CDF, which varies in shape — see the per-indicator distributions in the API /methodology/baselines response.
Inputs, weights and sources by pillar
A note on the Direction column: it describes whether a rising raw value is good or bad for the underlying condition (e.g. rising gilt yields = "Rising = worse"). The public Tightrope Score is always high-good — we apply the inversion in step 3 above before the score reaches the page. So a "Rising = worse" indicator can sit next to a healthy-looking pillar score without contradiction: the indicator's raw direction and the pillar's score axis are not the same axis.
Market Stability 40% of headline
Are markets giving the Government room to move? — cadence intraday.
| Indicator | Weight | Unit | Direction | Source |
|---|---|---|---|---|
| 10-year gilt yield UK 10-year nominal zero-coupon gilt yield (BoE IUDMNZC, daily close). | 18% | % | Rising = worse | Bank of England -- Statistical Database (gilt yields) ↗ |
| 20-year gilt yield UK 20-year nominal zero-coupon gilt yield (BoE IUDLNZC). Sensitivity proxy for long-duration borrowing. Indicator ID preserved as gilt_30y for DB continuity. | 16% | % | Rising = worse | Bank of England -- Statistical Database (gilt yields) ↗ |
| GBP / USD Sterling vs. US dollar. | 7% | ccy | Rising = better | Bank of England -- Exchange rates ↗ |
| GBP trade-weighted index Broad effective exchange rate index for sterling. | 7% | index | Rising = better | Bank of England -- Exchange rates ↗ |
| FTSE 250 Mid-cap index -- cleaner domestic UK read than FTSE 100. | 10% | index | Rising = better | LSEG -- FTSE 250 ↗ |
| 5y breakeven inflation 5y nominal minus 5y real gilt yield -- market-implied CPI/RPI 5y ahead, a direct proxy for OBR's CPI inflation path over the forecast horizon. | 12% | % | Rising = worse | Bank of England -- Statistical Database (gilt yields) ↗ |
| Brent crude in GBP Brent dated spot price converted to GBP -- the single largest swing input to OBR's CPI energy subcomponent and fuel-duty receipts. | 10% | GBP/bbl | Rising = worse | US EIA -- Europe Brent Spot Price (FOB) ↗ |
| UK housebuilder composite Equal-weighted price index of the five largest listed UK housebuilders (rebased 100 = 2019 avg) -- leads OBR's residential investment and construction GVA lines by 3-6 months. Pulled from EODHD's daily EOD feed; falls back to a weekly editorial fixture if the EODHD free-tier daily quota is exhausted. | 8% | index | Rising = better | EODHD -- UK housebuilder composite (daily EOD) ↗ |
| S&P Global UK Services PMI Headline Services PMI -- 50 = no change. Services is ~80% of UK GVA, so this leads OBR's real-GDP growth forecast by roughly one quarter. | 5% | index | Rising = better | S&P Global -- UK Services PMI ↗ |
| GfK consumer confidence GfK/NIESR consumer confidence headline index -- leading signal for household-consumption growth, the largest single expenditure line in OBR's GDP decomposition. | 4% | index | Rising = better | GfK / NIESR -- Consumer Confidence Barometer ↗ |
| RICS house-price balance Net balance of RICS surveyors reporting price rises vs. falls -- leads residential investment in OBR's expenditure GDP by 1-2 quarters. | 3% | % | Rising = better | RICS -- UK Residential Market Survey ↗ |
Fiscal Room 30% of headline
Is the fiscal buffer expanding or shrinking? — cadence event.
| Indicator | Weight | Unit | Direction | Source |
|---|---|---|---|---|
| Current-budget headroom (forecast) OBR forecast: surplus against the stability rule at the FY 2029/30 target year. Updated on each OBR EFO (twice-yearly). | 35% | GBPbn | Rising = better | Office for Budget Responsibility -- Economic & Fiscal Outlook ↗ |
| PSNFL trajectory deviation (forecast) OBR forecast: deviation of PSNFL path from OBR baseline, percentage points of GDP. | 15% | pp | Rising = worse | Office for Budget Responsibility -- Economic & Fiscal Outlook ↗ |
| Public-sector net borrowing Monthly public-sector net borrowing excluding public-sector banks (ONS J5II). Higher values = more borrowing. | 15% | GBPbn | Rising = worse | ONS -- Public Sector Finances ↗ |
| Central-government net interest payable Central-government net interest payable, monthly (ONS NMFX). | 15% | GBPbn | Rising = worse | ONS -- Public Sector Finances ↗ |
| Index-linked gilt share of stock Share of outstanding gilt stock (inflation-uplifted nominal) that is index-linked -- inflation-sensitivity proxy. Source: DMO D1A gilts-in-issue feed. | 10% | % | Rising = worse | UK Debt Management Office -- gilts in issue ↗ |
| Long-dated share of conventional gilt stock Long / Ultra-Long conventional gilts as % of all conventional gilt stock (DMO D1A). Higher = more exposure to long-dated rate moves. Methodology note: the historical indicator was a flow-based annual issuance share; we switched to this stock-based measure in 2026-04 because the flow report is behind a ShieldSquare bot-check. The stock share captures the same structural signal without the flow measure's intra-year seasonality. | 10% | % | Rising = worse | UK Debt Management Office -- gilts in issue ↗ |
Labour & Living-Standards Resilience 20% of headline
Is the labour force getting healthier and more engaged? — cadence monthly.
| Indicator | Weight | Unit | Direction | Source |
|---|---|---|---|---|
| Economic inactivity rate, 16-64 Share of 16-64 population neither in work nor looking for work. | 22% | % | Rising = worse | ONS -- Labour Market Statistics ↗ |
| Health-related inactivity (m) Millions reporting long-term sickness as main reason for inactivity. | 18% | m | Rising = worse | ONS -- Labour Market Statistics ↗ |
| Unemployment rate, 16+ ILO unemployment rate. | 10% | % | Rising = worse | ONS -- Labour Market Statistics ↗ |
| Vacancies per unemployed person Tightness of the labour market; falling = slack. | 10% | ratio | Rising = better | ONS -- Labour Market Statistics ↗ |
| AWE regular pay index Average Weekly Earnings -- whole economy regular-pay index (2015=100, SA, excl. arrears). Rising means earnings are growing. | 10% | index | Rising = better | ONS -- Real-Time Indicators ↗ |
| Real regular pay growth, YoY CPIH-adjusted regular pay, year-on-year. | 10% | % | Rising = better | ONS -- Labour Market Statistics ↗ |
| Effective 2y fix mortgage rate BoE IADB IUMBV34: monthly effective rate on new fixed-rate 2-year mortgages to households at 75% LTV. The canonical economist's reference (interest income ÷ balance, not advertised front-book rate). | 12% | % | Rising = worse | boe_mortgage_rates |
| Direct-debit failure rate ONS real-time indicators -- share of direct debits failing. | 8% | % | Rising = worse | ONS -- Real-Time Indicators ↗ |
Growth Delivery 10% of headline
Are the promised growth reforms visibly delivering? — cadence event · progress measures score higher when raw delivery is higher.
| Indicator | Weight | Unit | Direction | Source |
|---|---|---|---|---|
| Net housing additions vs. trajectory Latest net additions as % of OBR trajectory for the year. | 25% | % | Rising = better | MHCLG / DLUHC -- Housing Statistics ↗ |
| Planning consents vs. baseline Quarterly residential planning-decisions-granted as a % of a self-declared estimated 2019 pre-COVID quarterly baseline of 11,500. The denominator is an estimate — archived MHCLG PDFs can tighten it; the caveat is intentional and surfaced here. | 20% | % | Rising = better | MHCLG / DLUHC -- Housing Statistics ↗ |
| New towns milestones hit Milestones hit as % of committed milestones YTD. | 15% | % | Rising = better | gov.uk -- Announcements RSS ↗ |
| BICS rollout vs. target Firms onboarded to the British Industrial Competitiveness Scheme as a percentage of its published 10,000-firm target. See the matching delivery commitment row for the target date. | 15% | % | Rising = better | Department for Energy Security and Net Zero ↗ |
| Industrial Strategy milestones Industrial Strategy milestones on/ahead of schedule vs. slipped/missed. | 15% | % | Rising = better | Department for Business and Trade ↗ |
| SMR fleet progress Small Modular Reactor programme progress against published milestones. | 10% | % | Rising = better | gov.uk -- Announcements RSS ↗ |
What the historical chart covers
Every trajectory and sparkline on this site reads from stored pillar scores. Those scores are produced by two pipelines and the difference matters:
- Live recompute — runs every five minutes against every indicator in a pillar. This is what powers the headline score, the 24-hour change, and the latest points on every chart.
- Historical backfill — writes one row per UTC day for every day in range. For quorum and scoring, it uses only indicators whose values carry a defensible time-series. Two kinds of indicator are excluded: those whose values are editorial interpretations of political milestones (e.g. delivery progress scores), and those whose upstream feed only exposes today's snapshot with no machine-addressable history (e.g. DMO gilts-in-issue). In both cases a score prior to today was never a score those indicators contributed to.
| Pillar | Indicator (live-only) | Why excluded from backfill |
|---|---|---|
| Fiscal | Index-linked gilt share of stock Share of outstanding gilt stock (inflation-uplifted nominal) that is index-linked -- inflation-sensitivity proxy. Source: DMO D1A gilts-in-issue feed. | DMO D1A feed only exposes today's gilts-in-issue snapshot; archived stock composition is not machine-addressable, so historical values cannot be reconstructed from primary source. |
| Fiscal | Long-dated share of conventional gilt stock Long / Ultra-Long conventional gilts as % of all conventional gilt stock (DMO D1A). Higher = more exposure to long-dated rate moves. Methodology note: the historical indicator was a flow-based annual issuance share; we switched to this stock-based measure in 2026-04 because the flow report is behind a ShieldSquare bot-check. The stock share captures the same structural signal without the flow measure's intra-year seasonality. | DMO D1A feed only exposes today's gilts-in-issue snapshot; archived stock composition is not machine-addressable, so historical values cannot be reconstructed from primary source. |
| Delivery | New towns milestones hit Milestones hit as % of committed milestones YTD. | Editorial judgement against published departmental milestones — backfilling a score for a prior date would invent an assessment that was never made at the time. |
| Delivery | BICS rollout vs. target Firms onboarded to the British Industrial Competitiveness Scheme as a percentage of its published 10,000-firm target. See the matching delivery commitment row for the target date. | Editorial judgement against the BICS rollout plan — the cumulative-firms figure is published quarterly only; intra-quarter historical days would be fabricated. |
| Delivery | Industrial Strategy milestones Industrial Strategy milestones on/ahead of schedule vs. slipped/missed. | Editorial judgement against the Industrial Strategy commitments — historical days predate the current milestone list and cannot be scored against it retroactively. |
| Delivery | SMR fleet progress Small Modular Reactor programme progress against published milestones. | Editorial judgement against the SMR programme plan — progress assessments are episodic ministerial statements, not a time-series. |
Implemented by historicalIndicatorsForPillar() in packages/shared/src/historicalSubset.ts and exercised by packages/shared/src/historicalSubset.test.ts. Drop hasHistoricalSeries: false on a catalog entry and the disclosure above picks it up automatically.
Missing days on the trajectory
Each trajectory sparkline covers a 30-day window with one plot-point per UTC day. A day is plotted when the pillar meets quorum: at least half of its observed indicators are within their freshness window. A day that fails quorum is not plotted — the line skips that day rather than carrying forward the previous value.
Under each trajectory chart we report "Scored N of 30 days" so readers can see how complete the rendered series is. If N < 30, the missing days failed quorum (typically a batch of indicators not yet ingested for that day, or an adapter outage). The homepage's stale chip surfaces today's quorum state; the trajectory coverage count surfaces the last 30 days at a glance.
Last successful ingestion per source
success = fetch returned a payload different from the previous run (new data). unchanged = fetch returned the same bytes as last time (upstream has not republished since the previous "success" row; common for monthly ONS / semi-annual OBR series between publications). Both are healthy states.
| Source | Last run | Status |
|---|---|---|
| Bank of England -- 5y breakeven inflation | 2 Jun 2026 | unchanged |
| boe_breakevens:historical | 27 Apr 2026 | success |
| Bank of England -- Exchange rates | 2 Jun 2026 | unchanged |
| boe_fx:historical | 27 Apr 2026 | success |
| boe_mortgage_rates | 2 Jun 2026 | unchanged |
| boe_sonia | 23 Apr 2026 | unchanged |
| boe_sonia:historical | 19 Apr 2026 | success |
| Bank of England -- Statistical Database (gilt yields) | 2 Jun 2026 | unchanged |
| boe_yields:historical | 27 Apr 2026 | success |
| delivery_milestones | 2 Jun 2026 | unchanged |
| UK Debt Management Office -- gilts in issue | 2 Jun 2026 | unchanged |
| US EIA -- Europe Brent Spot Price (FOB) | 2 Jun 2026 | dlq |
| eia_brent:historical | 27 Apr 2026 | success |
| EODHD -- UK housebuilder composite (daily EOD) | 2 Jun 2026 | success |
| gov.uk -- Announcements RSS | 2 Jun 2026 | success |
| growth_sentiment | 2 Jun 2026 | dlq |
| growth_sentiment:historical | 27 Apr 2026 | success |
| ice_gas | 23 Apr 2026 | unchanged |
| LSEG -- FTSE 250 | 2 Jun 2026 | dlq |
| lseg:historical | 27 Apr 2026 | success |
| lseg_housebuilders | 22 Apr 2026 | unchanged |
| MHCLG / DLUHC -- Housing Statistics | 2 Jun 2026 | unchanged |
| mhclg:historical | 19 Apr 2026 | partial |
| Moneyfacts -- UK Mortgage Rates | 27 Apr 2026 | success |
| moneyfacts:historical | 27 Apr 2026 | success |
| Office for Budget Responsibility -- Economic & Fiscal Outlook | 2 Jun 2026 | unchanged |
| obr_efo:historical | 27 Apr 2026 | success |
| ONS -- Labour Market Statistics | 2 Jun 2026 | unchanged |
| ons_lms:historical | 27 Apr 2026 | success |
| ONS -- Public Sector Finances | 2 Jun 2026 | unchanged |
| ons_psf:historical | 27 Apr 2026 | success |
| ONS -- Real-Time Indicators | 2 Jun 2026 | unchanged |
| ons_rti:historical | 27 Apr 2026 | success |
| seed | 17 Apr 2026 | success |
| twelve_data_housebuilders | 22 Apr 2026 | unchanged |
| unknown | 2 Jun 2026 | dlq |
Pillar quorum & staleness
Not every indicator is fresh every day. ONS PSF publishes monthly with a 45-day lag; OBR EFO publishes twice a year; even the BoE's daily rate feed is quiet at weekends. We want the headline score to keep running when a single upstream goes quiet, but we also don't want to publish a pillar score composed mostly of stale readings. The quorum rule is the bright line between those.
- Per-indicator freshness. Every indicator carries its own
maxStaleMs— tuned to the publisher's cadence, not a pillar-wide window. An ONS PSF observation is "fresh" if it is within ~90 days; an OBR EFO figure is "fresh" within ~220 days. See the inputs table above. - Pillar quorum = half the observed indicators. For each pillar we count how many indicators have any observation on file (observedCount). The quorum is
ceil(observedCount × 0.5), floored at 1. If fewer than that many indicators are fresh, the pillar is marked stale. - Missing ≠ stale. An indicator with no observations at all (a data-engineering gap) is excluded from the quorum denominator. It shows up in the /sources page as "no data" without tripping the pillar into stale.
- What happens when a pillar is stale. The ingest recompute still computes a pillar score from the observations on file, but it does not persist a new
pillar_scoresrow for that pillar and does not write the headline row. The UI surfaces the stale chip on that pillar tile and on the nav bar. - Historical backfill is stricter. The backfill only writes a
pillar_scoresrow for days that met quorum, so gap days never carry forward (see "Missing days on the trajectory" below). The headline row is written only when all four pillars met quorum on that day.
Implemented by evaluatePillarFreshness() in packages/shared/src/staleness.ts. Tested via packages/shared/src/staleness.test.ts. The exact fraction (0.5) is exported as PILLAR_FRESHNESS_QUORUM_FRACTION so live recompute and backfill share one constant.
Upstream revisions & our correction policy
Official statistics are revised. The ONS labour-market release, the PSF outturn figures, and MHCLG's quarterly housing numbers are all routinely restated — sometimes for several releases in a row. Our ingest policy acknowledges that:
- Upstream revisions overwrite silently. Our
indicator_observationswrites useINSERT OR REPLACEkeyed on(indicator_id, observed_at). When ONS issues a revised value for February's PSF, the next ingest run replaces the old row. The current snapshot on this site always reflects the latest upstream vintage. - What we don't do: we don't keep a vintage history of every prior value ONS published. Reconstructing "what the ONS said on Tuesday vs Thursday" is not a goal of this project — that's what the ONS revision log is for. We link to it from each affected indicator's source card.
- What counts as a correction: a Tightrope correction is a change we made on our side — a methodology change, a sign fix, a label mistake, an editorial figure restated. Every one appears dated in the corrections log with both original and revised values. Upstream revisions by the ONS, OBR, BoE, etc. are not Tightrope corrections — they are restatements by the primary source, and we link to the source rather than duplicate its version history here.
- Backfill idempotency: when we re-ingest historical data, the default is
INSERT OR REPLACEso a reprocessed batch converges to the upstream's current truth. The admin path can flip toINSERT OR IGNOREto freeze a vintage (used, e.g., when auditing what a prior recompute had seen).
Implemented in apps/ingest/src/lib/observations.ts (writeObservations / writeHistoricalObservations). Tested in apps/ingest/src/tests/backfillObservations.test.ts.
Open questions & known limitations
- The baseline window excludes 2020 Q2 but retains the 2021–22 energy shock period. Changing the baseline requires a versioned methodology update — no silent rebaselining.
- Intra-pillar weights sum to 1 per pillar; they are normalised at scoring time even if the raw
weightvalues inINDICATORSdon't sum to exactly 1 (they are target weights). - Delivery progress indicators are rising-good: higher raw progress produces a higher public score, just like higher real pay or stronger market confidence.
- The geometric mean at the headline level cannot handle a zero input. In practice no pillar score reaches 0. We guard against it anyway by adding a tiny epsilon (
1e-6) inside thelogand subtracting the same epsilon afterexp— so a zero pillar pulls the headline strongly toward zero (as an unguarded geometric mean would) without producinglog(0). SeeweightedGeometricMeaninpackages/methodology/src/normalise.ts.
Corrections, source code, snapshots
Every correction to a published number appears in the corrections log, dated, with the original and revised values and the reason. The source code for the scoring library is MIT-licensed on GitHub, and the nightly snapshot of the D1 database is committed publicly — if you believe a number on this site is wrong, you can verify it end-to-end.
Licensing & attribution
UK public-sector data on this site (Bank of England, OBR, ONS, DMO, MHCLG, DESNZ, gov.uk, parliament.uk, legislation.gov.uk) is reproduced under the Open Government Licence v3.0. Per-source terms — including non-OGL feeds (S&P Global PMI, EODHD, Moneyfacts, RICS, GfK / NIESR) — are listed on the sources page. The application code is MIT-licensed.