diff --git a/frontend/src/components/regime/RegimeQuadrant.tsx b/frontend/src/components/regime/RegimeQuadrant.tsx index 36b18a8..ae65e71 100644 --- a/frontend/src/components/regime/RegimeQuadrant.tsx +++ b/frontend/src/components/regime/RegimeQuadrant.tsx @@ -3,6 +3,7 @@ import { useQuery } from '@tanstack/react-query'; import { ScatterChart, Scatter, + Cell, XAxis, YAxis, ZAxis, @@ -29,6 +30,33 @@ interface QPoint { date: string; } +/** Centered moving average to de-noise the path; today (last) kept exact. */ +function smoothTrail(points: QPoint[], half = 2): QPoint[] { + const n = points.length; + return points.map((p, i) => { + if (i === n - 1) return { ...p }; + let sx = 0; + let sy = 0; + let c = 0; + for (let j = Math.max(0, i - half); j <= Math.min(n - 1, i + half); j++) { + sx += points[j].x; + sy += points[j].y; + c += 1; + } + return { x: sx / c, y: sy / c, date: p.date }; + }); +} + +/** Recency gradient: 0 = oldest (muted slate), 1 = newest (bright blue). */ +function recencyColor(t: number): string { + const lerp = (a: number, b: number) => Math.round(a + (b - a) * t); + const r = lerp(71, 96); + const g = lerp(85, 165); + const b = lerp(105, 250); + const alpha = (0.3 + 0.7 * t).toFixed(2); + return `rgba(${r}, ${g}, ${b}, ${alpha})`; +} + function QuadrantTip({ active, payload }: { active?: boolean; payload?: { payload: QPoint }[] }) { if (!active || !payload?.length) return null; const p = payload[0].payload; @@ -54,6 +82,7 @@ export default function RegimeQuadrant() { .map((p) => ({ x: p.index, y: p.early_warning as number, date: p.date })); }, [history.data]); + const trail = useMemo(() => smoothTrail(points), [points]); const latest = points.length ? points[points.length - 1] : null; return ( @@ -110,10 +139,18 @@ export default function RegimeQuadrant() { axisLine={false} label={{ value: 'Early warning', angle: -90, position: 'insideLeft', fill: '#6b7280', fontSize: 10 }} /> - + } /> - {/* Trail (chronological path) */} - + {/* Smoothed trail with a recency gradient (old → new) */} + + {trail.map((_, i) => ( + + ))} + {/* Today */} {latest && ( ④ Real downturn — regime breaking, broad

- White dot = today; trail = path over the last {TRAIL} sessions. The tell isn't a single spot but the - move ①→④ (early warning rolling over while the regime index climbs = divergence resolving downward). - Observational — not wired into trades. + White dot = today; the trail fades from muted (older) to bright blue (newer) over the last {TRAIL}{' '} + sessions, smoothed. The tell isn't a single spot but the move ①→④ (early warning rolling over while + the regime index climbs = divergence resolving downward). Observational — not wired into trades.

)}