From ca9e44a41a4a996de702b8479c31f4e1bf3844fa Mon Sep 17 00:00:00 2001 From: u1 Date: Sat, 10 Jan 2026 23:02:30 +0000 Subject: [PATCH] feat(visualizer): add DLOB depth bands panel --- .../features/market/DlobDepthBandsPanel.tsx | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 apps/visualizer/src/features/market/DlobDepthBandsPanel.tsx diff --git a/apps/visualizer/src/features/market/DlobDepthBandsPanel.tsx b/apps/visualizer/src/features/market/DlobDepthBandsPanel.tsx new file mode 100644 index 0000000..6b0d27b --- /dev/null +++ b/apps/visualizer/src/features/market/DlobDepthBandsPanel.tsx @@ -0,0 +1,75 @@ +import type { CSSProperties } from 'react'; +import { useMemo } from 'react'; +import type { DlobDepthBandRow } from './useDlobDepthBands'; + +function formatUsd(v: number | null | undefined): string { + if (v == null || !Number.isFinite(v)) return '—'; + if (v >= 1_000_000) return `$${(v / 1_000_000).toFixed(2)}M`; + if (v >= 1000) return `$${(v / 1000).toFixed(0)}K`; + if (v >= 1) return `$${v.toFixed(2)}`; + return `$${v.toPrecision(4)}`; +} + +function formatPct(v: number | null | undefined): string { + if (v == null || !Number.isFinite(v)) return '—'; + return `${v.toFixed(0)}%`; +} + +function bandRowStyle(askScale: number, bidScale: number): CSSProperties { + const a = Number.isFinite(askScale) && askScale > 0 ? Math.min(1, askScale) : 0; + const b = Number.isFinite(bidScale) && bidScale > 0 ? Math.min(1, bidScale) : 0; + return { ['--ask-scale' as any]: a, ['--bid-scale' as any]: b } as CSSProperties; +} + +export default function DlobDepthBandsPanel({ rows }: { rows: DlobDepthBandRow[] }) { + const sorted = useMemo(() => rows.slice().sort((a, b) => a.bandBps - b.bandBps), [rows]); + + const maxUsd = useMemo(() => { + let max = 0; + for (const r of sorted) { + if (r.askUsd != null && Number.isFinite(r.askUsd)) max = Math.max(max, r.askUsd); + if (r.bidUsd != null && Number.isFinite(r.bidUsd)) max = Math.max(max, r.bidUsd); + } + return max; + }, [sorted]); + + return ( +
+
+
Depth (bands)
+
±bps around mid
+
+ +
+
+ Band + Ask USD + Bid USD + Bid % +
+ + {sorted.length ? ( + sorted.map((r) => ( +
0 ? (r.askUsd || 0) / maxUsd : 0, maxUsd > 0 ? (r.bidUsd || 0) / maxUsd : 0)} + title={`band=${r.bandBps}bps bid=${r.bidUsd ?? '—'} ask=${r.askUsd ?? '—'} imbalance=${r.imbalance ?? '—'}`} + > + {r.bandBps} bps + {formatUsd(r.askUsd)} + {formatUsd(r.bidUsd)} + + {r.imbalance == null || !Number.isFinite(r.imbalance) + ? '—' + : formatPct(((r.imbalance + 1) / 2) * 100)} + +
+ )) + ) : ( +
No depth band rows yet.
+ )} +
+
+ ); +}