Skip to content

fix(USMetrics): anchor trend windows to the latest observation; report rate series in percentage points - #2253

Closed
davdunc wants to merge 1 commit into
danielmiessler:mainfrom
davdunc:fix/usmetrics-trend-anchoring
Closed

davdunc wants to merge 1 commit into
danielmiessler:mainfrom
davdunc:fix/usmetrics-trend-anchoring

Conversation

@davdunc

@davdunc davdunc commented Sep 27, 2026

Copy link
Copy Markdown

Problem

calculateTrend() in LifeOS/install/skills/USMetrics/Tools/GenerateAnalysis.ts anchored every trend window to today and took the first observation after the cutoff as the start. Monthly and quarterly FRED series lag the calendar by 1–3 months, so the "1Y" column silently covered a shorter span, and FRED's missing-value observations (.) pushed the start further forward.

Reproduced on 2026-09-27 against live FRED data (before this change):

Series Window actually used Reported "1Y" True YoY (date-matched)
CPIAUCSL 2025-11-01 → 2026-08-01 (9 months) +2.8% +3.4%
GDPC1 2025-10-01 → 2026-04-01 (2 quarters) +0.9% +2.1%
UNRATE 2025-11-01 → 2026-08-01 (9 months) −8.9% −0.20pp

A second defect in the same column: percent change applied to series that are already percentages produced lines such as GDP Growth Rate: +200.0% and Fed Funds Rate: +807% over 10 years.

Fix

  • Anchor each window to the latest observation; choose the start as the observation nearest to latest − N years.
  • Return null (rendered —) when no observation lies within tolerance of the target, instead of a mislabelled window. Tolerance is half the series' median observation spacing, floored at 3 days (covers weekends/holidays in daily series).
  • Fetch years + 1 of history so the 10Y window has a real start candidate for lagged series.
  • Sort observations by date before windowing.
  • Series in RATE_SERIES (UNRATE, FEDFUNDS, DGS10, …) report changes in percentage points; formatValue now reuses the same set (GDP Growth Rate previously printed as a bare number). One SCALE table holds the dead band / "significant move" / momentum thresholds per unit, used by the snapshot, detail tables, extremes and momentum sections.
  • Export the pure functions and guard main() with import.meta.main so the module is testable; add GenerateAnalysis.test.ts (fixed-date fixtures, no wall-clock dependence).

Verification

  • bun test LifeOS/install/skills/USMetrics/Tools/GenerateAnalysis.test.ts: 14 pass, 0 fail.
  • Live run after the change (2026-09-27): CPI +3.4%, Real GDP +2.1%, Core PCE +3.3%, U-3 −0.20pp, GDP Growth Rate −2.30pp, Fed Funds −0.70pp, 10Y +1.02pp, 30Y mortgage +0.73pp, all equal to values computed directly from date-matched FRED observations.
  • Independent re-derivation from FRED (separate script, same day) matched all 12 snapshot series; simulating the old method against the same data reproduced wrong values on 10 of 12 (e.g. UMCSENT −3.5% vs true −11.2%, PSAVERT −0.90pp vs true −1.50pp).
  • No — cells appear in the live detail tables (every priority series has a start within tolerance for all four horizons).

Not in this PR (follow-ups)

  • Tools/FetchFredSeries.ts has its own calculateTrendStats with the same anchored-to-today logic; a shared trend module would let both tools use one implementation.
  • RATE_SERIES, SERIES_NAMES and FetchFredSeries.ts's CORE_SERIES are three hand-maintained series tables; consolidating them is a larger change.

🤖 Generated with Claude Code

…t rate series in pp

calculateTrend() filtered observations to those on/after (today - N years) and
took the first survivor as the window start. Monthly and quarterly FRED series
lag the calendar by 1-3 months, so the "1Y" column silently covered 9 months
for CPI and 6 months for real GDP (CPI +2.8% vs the true +3.4% YoY; GDPC1
+0.9% vs +2.1%). Missing observations ("." in FRED) pushed the start further
forward.

- Anchor every window to the latest observation and pick the start as the
  observation nearest to (latest - N years); return null when nothing is
  within 183 days of the target (sparse annual series).
- Sort observations by date before windowing.
- Series that are already percentages (UNRATE, FEDFUNDS, DGS10, ...) now
  report changes in percentage points instead of percent-of-percent
  ("Fed Funds +807% over 10 years", "GDP Growth Rate +200%"); direction uses a
  0.10pp dead band. Extremes and momentum sections use the same units.
- Export the pure functions and guard main() with import.meta.main so the
  module is testable; add GenerateAnalysis.test.ts with fixed-date fixtures.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@danielmiessler

Copy link
Copy Markdown
Owner

Thanks @davdunc. Confirmed: the trend windows were anchored to today, so lagging monthly and quarterly series reported 9-month and 2-quarter spans as "1Y". calculateTrend now anchors to the latest observation, picks the start nearest the target date, and returns nothing when no observation is close enough; rate series report in percentage points (skills/USMetrics/Tools/GenerateAnalysis.ts, with tests). Next release carries it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants