fix: smooth the quadrant trail and fade it by recency
The single solid trail line read like a tangle. Make older→newer legible: the path now fades from muted slate (older) to bright blue (newer) via per-point colors, the connecting line is faint, and the points are de-noised with a centered moving average (today kept exact). Easier to see the direction of travel through the quadrants. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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 }}
|
||||
/>
|
||||
<ZAxis range={[16, 16]} />
|
||||
<ZAxis range={[13, 13]} />
|
||||
<Tooltip cursor={{ strokeDasharray: '3 3', stroke: 'rgba(255,255,255,0.2)' }} content={<QuadrantTip />} />
|
||||
{/* Trail (chronological path) */}
|
||||
<Scatter data={points} fill="#60a5fa" fillOpacity={0.5} line={{ stroke: 'rgba(96,165,250,0.3)' }} isAnimationActive={false} />
|
||||
{/* Smoothed trail with a recency gradient (old → new) */}
|
||||
<Scatter
|
||||
data={trail}
|
||||
line={{ stroke: 'rgba(96,165,250,0.18)', strokeWidth: 1.5 }}
|
||||
isAnimationActive={false}
|
||||
>
|
||||
{trail.map((_, i) => (
|
||||
<Cell key={i} fill={recencyColor(trail.length <= 1 ? 1 : i / (trail.length - 1))} />
|
||||
))}
|
||||
</Scatter>
|
||||
{/* Today */}
|
||||
{latest && (
|
||||
<Scatter
|
||||
@@ -135,9 +172,9 @@ export default function RegimeQuadrant() {
|
||||
<span><span className="text-red-400">④ Real downturn</span> — regime breaking, broad</span>
|
||||
</div>
|
||||
<p className="mt-2 text-[11px] leading-relaxed text-gray-600">
|
||||
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.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user