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 ( +