diff --git a/frontend/src/components/regime/RegimeQuadrant.tsx b/frontend/src/components/regime/RegimeQuadrant.tsx new file mode 100644 index 0000000..36b18a8 --- /dev/null +++ b/frontend/src/components/regime/RegimeQuadrant.tsx @@ -0,0 +1,146 @@ +import { useMemo } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import { + ScatterChart, + Scatter, + XAxis, + YAxis, + ZAxis, + CartesianGrid, + Tooltip, + ResponsiveContainer, + ReferenceLine, + ReferenceArea, +} from 'recharts'; +import { getRegimeHistory } from '../../api/regime'; +import { Callout } from '../ui/Callout'; +import { SkeletonCard } from '../ui/Skeleton'; + +// Lazy-loaded (see RegimePage) so recharts stays in the regime-tab chunk. + +// Quadrant dividers. Regime < 40 ≈ intact; early-warning > 60 ≈ elevated. +const X_DIV = 40; // regime index +const Y_DIV = 60; // early warning +const TRAIL = 60; // sessions shown + +interface QPoint { + x: number; + y: number; + date: string; +} + +function QuadrantTip({ active, payload }: { active?: boolean; payload?: { payload: QPoint }[] }) { + if (!active || !payload?.length) return null; + const p = payload[0].payload; + return ( +
+ 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. +
+ > + )} +