"use client"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from "react"; import type { AlertEvent, ClassifierHitEvent, Cursor, EquityCandle, EquityPrint, EquityPrintJoin, FlowPacket, InferredDarkEvent, LiveServerMessage, LiveSubscription, OptionNBBO, OptionPrint } from "@islandflow/types"; import { createChart, type IChartApi, type SeriesMarker, type UTCTimestamp } from "lightweight-charts"; const MAX_ITEMS = 500; const NBBO_MAX_AGE_MS = Number(process.env.NEXT_PUBLIC_NBBO_MAX_AGE_MS); const NBBO_MAX_AGE_MS_SAFE = Number.isFinite(NBBO_MAX_AGE_MS) && NBBO_MAX_AGE_MS > 0 ? NBBO_MAX_AGE_MS : 1000; const LOCAL_HOSTS = new Set(["localhost", "127.0.0.1"]); const CANDLE_INTERVALS = [ { label: "1m", ms: 60000 }, { label: "5m", ms: 300000 } ]; type CandlestickSeries = ReturnType; type EquityOverlayPoint = { ts: number; price: number; size: number; offExchangeFlag: boolean; }; type ChartCandle = { time: UTCTimestamp; open: number; high: number; low: number; close: number; }; const formatIntervalLabel = (intervalMs: number): string => { const match = CANDLE_INTERVALS.find((interval) => interval.ms === intervalMs); if (match) { return match.label; } if (intervalMs >= 60000) { return `${Math.round(intervalMs / 60000)}m`; } if (intervalMs >= 1000) { return `${Math.round(intervalMs / 1000)}s`; } return `${intervalMs}ms`; }; const toChartTime = (ts: number): UTCTimestamp => { return Math.floor(ts / 1000) as UTCTimestamp; }; type ChartTimeLike = number | string | { year: number; month: number; day: number }; const chartTimeToMs = (value: ChartTimeLike): number | null => { if (typeof value === "number") { return Math.floor(value * 1000); } if (typeof value === "string") { const parsed = Date.parse(value); return Number.isFinite(parsed) ? parsed : null; } if (value && typeof value === "object") { const { year, month, day } = value; if ( Number.isFinite(year) && Number.isFinite(month) && Number.isFinite(day) && year >= 1970 && month >= 1 && month <= 12 && day >= 1 && day <= 31 ) { return Date.UTC(year, month - 1, day); } } return null; }; const toChartCandle = (candle: EquityCandle): ChartCandle => { return { time: toChartTime(candle.ts), open: candle.open, high: candle.high, low: candle.low, close: candle.close }; }; const clamp = (value: number, min: number, max: number): number => { if (!Number.isFinite(value)) { return min; } return Math.max(min, Math.min(max, value)); }; const sampleToLimit = (items: T[], limit: number): T[] => { if (items.length <= limit) { return items; } const safeLimit = Math.max(1, Math.floor(limit)); const step = Math.ceil(items.length / safeLimit); const sampled: T[] = []; for (let idx = 0; idx < items.length; idx += step) { sampled.push(items[idx]); } return sampled; }; const readErrorDetail = async (response: Response): Promise => { const text = await response.text(); if (!text) { return ""; } try { const payload = JSON.parse(text) as { detail?: string; error?: string; message?: string; }; return payload.detail ?? payload.error ?? payload.message ?? text; } catch { return text; } }; type WsStatus = "connecting" | "connected" | "disconnected"; type TapeMode = "live" | "replay"; type MessageType = | "option-print" | "option-nbbo" | "equity-print" | "equity-candle" | "equity-join" | "flow-packet" | "inferred-dark" | "classifier-hit" | "alert"; type StreamMessage = { type: MessageType; payload: T; }; type ReplayCursor = { ts: number; seq: number; }; type ReplayResponse = { data: T[]; next: ReplayCursor | null; }; const inferTracePrefix = (traceId: string): string => { const match = traceId.match(/^(.*)-\d+$/); return match ? match[1] : traceId; }; const extractTracePrefix = (item: T): string | null => { const traceId = (item as { trace_id?: string }).trace_id; if (!traceId) { return null; } return inferTracePrefix(traceId); }; const extractReplaySource = (item: T): string | null => { const prefix = extractTracePrefix(item); if (!prefix) { return null; } const normalized = prefix.toLowerCase(); if (normalized.startsWith("synthetic")) { return "synthetic"; } if (normalized.startsWith("databento")) { return "databento"; } if (normalized.startsWith("alpaca")) { return "alpaca"; } if (normalized.startsWith("ibkr")) { return "ibkr"; } return prefix; }; type SortableItem = { ts?: number; source_ts?: number; ingest_ts?: number; seq?: number; trace_id?: string; id?: string; }; const extractSortTs = (item: SortableItem): number => item.ts ?? item.source_ts ?? item.ingest_ts ?? 0; const extractSortSeq = (item: SortableItem): number => item.seq ?? 0; const buildItemKey = (item: SortableItem): string | null => { if (item.trace_id) { return `${item.trace_id}:${item.seq ?? ""}`; } if (item.id) { return `id:${item.id}`; } return null; }; const mergeNewest = (incoming: T[], existing: T[]): T[] => { const combined = [...incoming, ...existing]; if (combined.length === 0) { return combined; } const seen = new Set(); const deduped: T[] = []; for (const item of combined) { const key = buildItemKey(item); if (key) { if (seen.has(key)) { continue; } seen.add(key); } deduped.push(item); } deduped.sort((a, b) => { const delta = extractSortTs(b) - extractSortTs(a); if (delta !== 0) { return delta; } return extractSortSeq(b) - extractSortSeq(a); }); return deduped.slice(0, MAX_ITEMS); }; type TapeState = { status: WsStatus; items: T[]; lastUpdate: number | null; replayTime: number | null; replayComplete: boolean; paused: boolean; dropped: number; togglePause: () => void; }; const buildWsUrl = (path: string): string => { const envBase = process.env.NEXT_PUBLIC_API_URL; if (envBase) { const url = new URL(envBase); url.protocol = url.protocol === "https:" ? "wss:" : "ws:"; url.pathname = path; url.search = ""; url.hash = ""; return url.toString(); } const { protocol, hostname } = window.location; const wsProtocol = protocol === "https:" ? "wss" : "ws"; const isLocal = LOCAL_HOSTS.has(hostname); const host = isLocal ? `${hostname}:4000` : window.location.host; return `${wsProtocol}://${host}${path}`; }; const buildApiUrl = (path: string): string => { const envBase = process.env.NEXT_PUBLIC_API_URL; if (envBase) { const url = new URL(envBase); const secure = url.protocol === "https:" || url.protocol === "wss:"; url.protocol = secure ? "https:" : "http:"; url.pathname = path; url.search = ""; url.hash = ""; return url.toString(); } const { protocol, hostname } = window.location; const httpProtocol = protocol === "https:" ? "https" : "http"; const isLocal = LOCAL_HOSTS.has(hostname); const host = isLocal ? `${hostname}:4000` : window.location.host; return `${httpProtocol}://${host}${path}`; }; const formatPrice = (price: number): string => { if (!Number.isFinite(price)) { return "0.00"; } return price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }; const formatSize = (size: number): string => { return size.toLocaleString(); }; const formatTime = (ts: number): string => { return new Date(ts).toLocaleTimeString(); }; const formatConfidence = (value: number): string => `${Math.round(value * 100)}%`; const formatPct = (value: number): string => `${Math.round(value * 100)}%`; const formatUsd = (value: number): string => { if (!Number.isFinite(value)) { return "0.00"; } return value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }; const normalizeContractId = (value: string): string => value.trim(); const formatContractLabel = (value: string): string => { const normalized = normalizeContractId(value); if (!normalized) { return "Unknown contract"; } if (/^\d+$/.test(normalized)) { return `Instrument ${normalized}`; } return normalized; }; const formatDateTime = (ts: number): string => { const date = new Date(ts); return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; }; const humanizeClassifierId = (value: string): string => { if (!value) { return "Classifier"; } return value .split("_") .map((part) => (part ? part[0].toUpperCase() + part.slice(1) : part)) .join(" "); }; const normalizeDirection = (value: string): "bullish" | "bearish" | "neutral" => { const normalized = value.toLowerCase(); if (normalized === "bullish" || normalized === "bearish" || normalized === "neutral") { return normalized; } return "neutral"; }; const extractUnderlying = (contractId: string): string => { const match = contractId.match(/^(.+)-\d{4}-\d{2}-\d{2}-/); if (match?.[1]) { return match[1].toUpperCase(); } return contractId.split("-")[0]?.toUpperCase() ?? contractId.toUpperCase(); }; const extractEquityTraceFromJoin = (joinId: string): string | null => { const match = joinId.match(/^equityjoin:(.+)$/); return match?.[1] ?? null; }; const inferDarkUnderlying = ( event: InferredDarkEvent, equityPrints: Map, equityJoins: Map ): string | null => { for (const ref of event.evidence_refs) { const join = equityJoins.get(ref); if (!join) { continue; } const underlying = join.features.underlying_id; if (typeof underlying === "string" && underlying.length > 0) { return underlying.toUpperCase(); } } const match = event.trace_id.match(/^dark:(?:stealth_accumulation|distribution):([^:]+):/); if (match?.[1]) { return match[1].toUpperCase(); } for (const ref of event.evidence_refs) { const traceId = extractEquityTraceFromJoin(ref); if (!traceId) { continue; } const print = equityPrints.get(traceId); if (print) { return print.underlying_id.toUpperCase(); } } return null; }; const parseNumber = (value: unknown, fallback: number): number => { if (typeof value === "number" && Number.isFinite(value)) { return value; } if (typeof value === "string") { const parsed = Number(value); if (Number.isFinite(parsed)) { return parsed; } } return fallback; }; const parseBoolean = (value: unknown, fallback = false): boolean => { if (typeof value === "boolean") { return value; } if (typeof value === "number") { return value !== 0; } if (typeof value === "string") { const normalized = value.trim().toLowerCase(); if (["true", "1", "yes", "on"].includes(normalized)) { return true; } if (["false", "0", "no", "off"].includes(normalized)) { return false; } } return fallback; }; const getJoinString = (join: EquityPrintJoin, key: string): string | null => { const value = join.features[key]; return typeof value === "string" ? value : null; }; const getJoinNumber = (join: EquityPrintJoin, key: string, fallback = Number.NaN): number => { return parseNumber(join.features[key], fallback); }; const getJoinBoolean = (join: EquityPrintJoin, key: string): boolean => { return parseBoolean(join.features[key], false); }; type NbboSide = "AA" | "A" | "B" | "BB"; const classifyNbboSide = (price: number, quote: OptionNBBO | null | undefined): NbboSide | null => { if (!quote || !Number.isFinite(price)) { return null; } const bid = quote.bid; const ask = quote.ask; if (!Number.isFinite(bid) || !Number.isFinite(ask) || ask <= 0) { return null; } const spread = Math.max(0, ask - bid); const epsilon = Math.max(0.01, spread * 0.05); if (price > ask + epsilon) { return "AA"; } if (price >= ask - epsilon) { return "A"; } if (price < bid - epsilon) { return "BB"; } if (price <= bid + epsilon) { return "B"; } const mid = (bid + ask) / 2; return price >= mid ? "A" : "B"; }; type ListScrollState = { listRef: React.RefObject; isAtTop: boolean; isAtTopRef: React.MutableRefObject; missed: number; resumeTick: number; onNewItems: (count: number) => void; jumpToTop: () => void; }; const useListScroll = (): ListScrollState => { const listRef = useRef(null); const [isAtTop, setIsAtTop] = useState(true); const [missed, setMissed] = useState(0); const [resumeTick, setResumeTick] = useState(0); const isAtTopRef = useRef(true); const prevAtTopRef = useRef(true); useEffect(() => { isAtTopRef.current = isAtTop; }, [isAtTop]); const updateScrollState = useCallback(() => { const el = listRef.current; if (!el) { return; } const atTop = el.scrollTop <= 2; if (atTop && !prevAtTopRef.current) { setResumeTick((prev) => prev + 1); } prevAtTopRef.current = atTop; isAtTopRef.current = atTop; setIsAtTop(atTop); if (atTop) { setMissed(0); } }, [isAtTopRef]); useEffect(() => { const el = listRef.current; if (!el) { return; } const onScroll = () => { updateScrollState(); }; updateScrollState(); el.addEventListener("scroll", onScroll); return () => { el.removeEventListener("scroll", onScroll); }; }, [updateScrollState]); const onNewItems = useCallback((count: number) => { if (count <= 0) { return; } if (isAtTopRef.current) { setMissed(0); return; } setMissed((prev) => prev + count); }, []); const jumpToTop = useCallback(() => { const el = listRef.current; if (!el) { return; } isAtTopRef.current = true; el.scrollTop = 0; updateScrollState(); }, [isAtTopRef, listRef, updateScrollState]); return { listRef, isAtTop, isAtTopRef, missed, resumeTick, onNewItems, jumpToTop }; }; const useScrollAnchor = ( listRef: React.RefObject, isAtTopRef: React.MutableRefObject ) => { const pendingRef = useRef<{ height: number } | null>(null); const capture = useCallback(() => { if (isAtTopRef.current) { pendingRef.current = null; return; } const el = listRef.current; if (!el) { return; } pendingRef.current = { height: el.scrollHeight }; }, [isAtTopRef, listRef]); const apply = useCallback(() => { const pending = pendingRef.current; if (!pending) { return; } const el = listRef.current; if (!el) { return; } if (isAtTopRef.current) { pendingRef.current = null; return; } const delta = el.scrollHeight - pending.height; if (delta !== 0) { el.scrollTop = Math.max(0, el.scrollTop + delta); } pendingRef.current = null; }, [isAtTopRef, listRef]); return { capture, apply }; }; const statusLabel = (status: WsStatus, paused: boolean, mode: TapeMode): string => { if (paused) { return "Paused"; } if (mode === "replay") { return status === "disconnected" ? "Replay Down" : "Replay"; } switch (status) { case "connected": return "Live"; case "connecting": return "Connecting"; case "disconnected": default: return "Disconnected"; } }; type TapeConfig = { mode: TapeMode; wsPath: string; replayPath: string; latestPath?: string; liveEnabled?: boolean; expectedType: MessageType; batchSize?: number; pollMs?: number; captureScroll?: () => void; onNewItems?: (count: number) => void; getItemTs?: (item: T) => number; getReplayKey?: (item: T) => string | null; replaySourceKey?: string | null; onReplaySourceKey?: (key: string | null) => void; }; const useTape = ( config: TapeConfig ): TapeState => { const { mode, wsPath, replayPath, expectedType, latestPath, onNewItems, captureScroll } = config; const batchSize = config.batchSize ?? 40; const pollMs = config.pollMs ?? 1000; const getItemTs = config.getItemTs ?? extractSortTs; const getReplayKey = config.getReplayKey ?? extractTracePrefix; const replaySourceKey = config.replaySourceKey ?? null; const onReplaySourceKey = config.onReplaySourceKey; const [status, setStatus] = useState("connecting"); const [items, setItems] = useState([]); const [lastUpdate, setLastUpdate] = useState(null); const [replayTime, setReplayTime] = useState(null); const [replayComplete, setReplayComplete] = useState(false); const [paused, setPaused] = useState(false); const [dropped, setDropped] = useState(0); const reconnectRef = useRef(null); const socketRef = useRef(null); const cursorRef = useRef({ ts: 0, seq: 0 }); const replayEndRef = useRef(null); const replayCompleteRef = useRef(false); const replaySourceRef = useRef(null); const replaySourceNotifiedRef = useRef(null); const emptyPollsRef = useRef(0); const pausedRef = useRef(paused); const pendingRef = useRef([]); const pendingCountRef = useRef(0); const flushHandleRef = useRef(null); useEffect(() => { pausedRef.current = paused; }, [paused]); const cancelFlush = useCallback(() => { if (flushHandleRef.current !== null) { cancelAnimationFrame(flushHandleRef.current); flushHandleRef.current = null; } }, []); const scheduleFlush = useCallback(() => { if (flushHandleRef.current !== null) { return; } flushHandleRef.current = requestAnimationFrame(() => { flushHandleRef.current = null; const buffered = pendingRef.current; if (buffered.length === 0) { return; } pendingRef.current = []; const pendingCount = pendingCountRef.current; pendingCountRef.current = 0; if (onNewItems && pendingCount > 0) { onNewItems(pendingCount); } if (captureScroll) { captureScroll(); } setItems((prev) => mergeNewest(buffered, prev)); setLastUpdate(Date.now()); }); }, [captureScroll, onNewItems]); const togglePause = useCallback(() => { setPaused((prev) => { const next = !prev; if (!next) { setDropped(0); } return next; }); }, []); useEffect(() => { setItems([]); setLastUpdate(null); setReplayTime(null); setReplayComplete(false); replayCompleteRef.current = false; replaySourceRef.current = null; replaySourceNotifiedRef.current = null; emptyPollsRef.current = 0; setDropped(0); setStatus("connecting"); cursorRef.current = { ts: 0, seq: 0 }; pendingRef.current = []; pendingCountRef.current = 0; cancelFlush(); }, [mode, replaySourceKey, cancelFlush]); useEffect(() => { if (mode !== "replay" || !latestPath) { replayEndRef.current = null; return; } let active = true; replayEndRef.current = null; setReplayComplete(false); replayCompleteRef.current = false; const fetchReplayEnd = async () => { try { const url = new URL(buildApiUrl(latestPath)); url.searchParams.set("limit", "1"); if (replaySourceKey) { url.searchParams.set("source", replaySourceKey); } const response = await fetch(url.toString()); if (!response.ok) { throw new Error(`Replay baseline failed with ${response.status}`); } const payload = (await response.json()) as { data?: T[] }; const latest = payload.data?.[0]; if (active && latest) { replayEndRef.current = getItemTs(latest); } } catch (error) { console.warn("Failed to load replay end cursor", error); } }; void fetchReplayEnd(); return () => { active = false; }; }, [mode, latestPath, getItemTs, replaySourceKey]); useEffect(() => { if (mode !== "live" || config.liveEnabled === false) { return; } let active = true; const connect = () => { if (!active) { return; } setStatus("connecting"); const socket = new WebSocket(buildWsUrl(wsPath)); socketRef.current = socket; socket.onopen = () => { if (!active) { return; } setStatus("connected"); }; socket.onmessage = (event) => { if (!active) { return; } try { const message = JSON.parse(event.data) as StreamMessage; if (!message || message.type !== expectedType) { return; } if (pausedRef.current) { setDropped((prev) => prev + 1); setLastUpdate(Date.now()); return; } pendingRef.current.push(message.payload); pendingCountRef.current += 1; scheduleFlush(); } catch (error) { console.warn("Failed to parse websocket payload", error); } }; socket.onclose = () => { if (!active) { return; } setStatus("disconnected"); reconnectRef.current = window.setTimeout(() => { connect(); }, 1000); }; socket.onerror = () => { if (!active) { return; } setStatus("disconnected"); socket.close(); }; }; connect(); return () => { active = false; cancelFlush(); if (reconnectRef.current !== null) { window.clearTimeout(reconnectRef.current); } if (socketRef.current) { socketRef.current.close(); } }; }, [mode, wsPath, expectedType, scheduleFlush, cancelFlush]); useEffect(() => { if (mode !== "replay") { return; } let active = true; const poll = async () => { if (!active || pausedRef.current) { return; } if (replayCompleteRef.current) { return; } try { let keepPolling = true; while (keepPolling && active && !pausedRef.current) { const replayEnd = replayEndRef.current; const cursor = cursorRef.current; if (replayEnd !== null && cursor.ts >= replayEnd) { replayCompleteRef.current = true; setReplayComplete(true); setStatus("disconnected"); return; } const url = new URL(buildApiUrl(replayPath)); url.searchParams.set("after_ts", cursor.ts.toString()); url.searchParams.set("after_seq", cursor.seq.toString()); url.searchParams.set("limit", batchSize.toString()); const desiredSource = replaySourceKey ?? replaySourceRef.current; if (desiredSource) { url.searchParams.set("source", desiredSource); } const response = await fetch(url.toString()); if (!response.ok) { throw new Error(`Replay request failed with ${response.status}`); } const payload = (await response.json()) as ReplayResponse; let sourcePrefix = replaySourceRef.current; if (replaySourceKey) { if (sourcePrefix !== replaySourceKey) { sourcePrefix = replaySourceKey; replaySourceRef.current = replaySourceKey; } } else if (!sourcePrefix) { const firstWithTrace = payload.data.find((item) => getReplayKey(item)); if (firstWithTrace) { sourcePrefix = getReplayKey(firstWithTrace); replaySourceRef.current = sourcePrefix ?? null; } } if (onReplaySourceKey && sourcePrefix && replaySourceNotifiedRef.current !== sourcePrefix) { replaySourceNotifiedRef.current = sourcePrefix; onReplaySourceKey(sourcePrefix); } const filtered = sourcePrefix ? payload.data.filter((item) => getReplayKey(item) === sourcePrefix) : payload.data; const hasForeign = sourcePrefix && payload.data.some((item) => { const prefix = getReplayKey(item); return prefix !== null && prefix !== sourcePrefix; }); if (filtered.length > 0) { const nextItems = [...filtered].reverse(); pendingRef.current.push(...nextItems); pendingCountRef.current += nextItems.length; scheduleFlush(); const last = filtered.at(-1); if (last) { const lastTs = getItemTs(last); setReplayTime(lastTs); if (replayEnd !== null && lastTs >= replayEnd) { cursorRef.current = { ts: lastTs, seq: last.seq }; replayCompleteRef.current = true; setReplayComplete(true); setStatus("disconnected"); return; } } emptyPollsRef.current = 0; } else if (sourcePrefix) { emptyPollsRef.current += 1; } if (payload.next) { cursorRef.current = payload.next; } setStatus("connected"); keepPolling = filtered.length === batchSize; if (keepPolling) { await new Promise((resolve) => setTimeout(resolve, 0)); } if (!replaySourceKey && hasForeign) { replayCompleteRef.current = true; setReplayComplete(true); setStatus("disconnected"); return; } if (sourcePrefix && emptyPollsRef.current >= 3) { replayCompleteRef.current = true; setReplayComplete(true); setStatus("disconnected"); return; } } } catch (error) { console.warn("Replay poll failed", error); setStatus("disconnected"); } }; void poll(); const interval = window.setInterval(poll, pollMs); return () => { active = false; window.clearInterval(interval); cancelFlush(); }; }, [ mode, replayPath, batchSize, pollMs, scheduleFlush, cancelFlush, getItemTs, getReplayKey, replaySourceKey, onReplaySourceKey ]); return { status, items, lastUpdate, replayTime, replayComplete, paused, dropped, togglePause }; }; const toStaticTapeState = ( status: WsStatus, items: T[], lastUpdate: number | null ): TapeState => ({ status, items, lastUpdate, replayTime: null, replayComplete: false, paused: false, dropped: 0, togglePause: () => {} }); const useLiveStream = ( config: { enabled: boolean; wsPath: string; expectedType: MessageType; onNewItems?: (count: number) => void; captureScroll?: () => void; shouldHold?: () => boolean; resumeSignal?: number; } ): TapeState => { const [status, setStatus] = useState( config.enabled ? "connecting" : "disconnected" ); const [items, setItems] = useState([]); const [lastUpdate, setLastUpdate] = useState(null); const [replayTime] = useState(null); const [replayComplete] = useState(false); const [paused, setPaused] = useState(false); const [dropped, setDropped] = useState(0); const reconnectRef = useRef(null); const socketRef = useRef(null); const pausedRef = useRef(paused); const pendingRef = useRef([]); const pendingCountRef = useRef(0); const flushHandleRef = useRef(null); const holdRef = useRef([]); useEffect(() => { pausedRef.current = paused; }, [paused]); const cancelFlush = useCallback(() => { if (flushHandleRef.current !== null) { cancelAnimationFrame(flushHandleRef.current); flushHandleRef.current = null; } }, []); const scheduleFlush = useCallback(() => { if (flushHandleRef.current !== null) { return; } flushHandleRef.current = requestAnimationFrame(() => { flushHandleRef.current = null; const buffered = pendingRef.current; if (buffered.length === 0) { return; } pendingRef.current = []; const pendingCount = pendingCountRef.current; pendingCountRef.current = 0; if (config.onNewItems && pendingCount > 0) { config.onNewItems(pendingCount); } const shouldHold = config.shouldHold ? config.shouldHold() : false; if (!shouldHold && config.captureScroll) { config.captureScroll(); } if (shouldHold) { holdRef.current = mergeNewest(buffered, holdRef.current); setLastUpdate(Date.now()); return; } const nextBatch = holdRef.current.length > 0 ? [...holdRef.current, ...buffered] : buffered; holdRef.current = []; setItems((prev) => mergeNewest(nextBatch, prev)); setLastUpdate(Date.now()); }); }, [config.captureScroll, config.onNewItems, config.shouldHold]); const togglePause = useCallback(() => { setPaused((prev) => { const next = !prev; if (!next) { setDropped(0); } return next; }); }, []); useEffect(() => { if (!config.enabled) { setStatus("disconnected"); setItems([]); setLastUpdate(null); pendingRef.current = []; pendingCountRef.current = 0; holdRef.current = []; cancelFlush(); return; } let active = true; const connect = () => { if (!active) { return; } setStatus("connecting"); const socket = new WebSocket(buildWsUrl(config.wsPath)); socketRef.current = socket; socket.onopen = () => { if (!active) { return; } setStatus("connected"); }; socket.onmessage = (event) => { if (!active) { return; } try { const message = JSON.parse(event.data) as StreamMessage; if (!message || message.type !== config.expectedType) { return; } if (pausedRef.current) { setDropped((prev) => prev + 1); setLastUpdate(Date.now()); return; } pendingRef.current.push(message.payload); pendingCountRef.current += 1; scheduleFlush(); } catch (error) { console.warn("Failed to parse live stream payload", error); } }; socket.onclose = () => { if (!active) { return; } setStatus("disconnected"); reconnectRef.current = window.setTimeout(() => { connect(); }, 1000); }; socket.onerror = () => { if (!active) { return; } setStatus("disconnected"); socket.close(); }; }; connect(); return () => { active = false; cancelFlush(); if (reconnectRef.current !== null) { window.clearTimeout(reconnectRef.current); } if (socketRef.current) { socketRef.current.close(); } }; }, [config.enabled, config.expectedType, config.wsPath, scheduleFlush, cancelFlush]); useEffect(() => { if (config.resumeSignal === undefined) { return; } if (config.shouldHold && config.shouldHold()) { return; } if (holdRef.current.length === 0) { return; } setItems((prev) => mergeNewest(holdRef.current, prev)); holdRef.current = []; setLastUpdate(Date.now()); }, [config.resumeSignal, config.shouldHold]); return { status, items, lastUpdate, replayTime, replayComplete, paused, dropped, togglePause }; }; const useFlowStream = ( enabled: boolean, onNewItems?: (count: number) => void, captureScroll?: () => void, shouldHold?: () => boolean, resumeSignal?: number ): TapeState => { return useLiveStream({ enabled, wsPath: "/ws/flow", expectedType: "flow-packet", onNewItems, captureScroll, shouldHold, resumeSignal }); }; type LiveSessionState = { status: WsStatus; lastUpdate: number | null; options: OptionPrint[]; nbbo: OptionNBBO[]; equities: EquityPrint[]; equityJoins: EquityPrintJoin[]; flow: FlowPacket[]; classifierHits: ClassifierHitEvent[]; alerts: AlertEvent[]; inferredDark: InferredDarkEvent[]; chartCandles: EquityCandle[]; chartOverlay: EquityPrint[]; }; const getLiveSubscriptionKey = (subscription: LiveSubscription): string => { switch (subscription.channel) { case "equity-candles": return `${subscription.channel}|${subscription.underlying_id}|${subscription.interval_ms}`; case "equity-overlay": return `${subscription.channel}|${subscription.underlying_id}`; default: return subscription.channel; } }; const getLiveManifest = ( pathname: string, chartTicker: string, chartIntervalMs: number ): LiveSubscription[] => { const chartSubs: LiveSubscription[] = [ { channel: "equity-candles", underlying_id: chartTicker, interval_ms: chartIntervalMs }, { channel: "equity-overlay", underlying_id: chartTicker } ]; if (pathname === "/tape") { return [ { channel: "options" }, { channel: "nbbo" }, { channel: "equities" }, { channel: "flow" } ]; } if (pathname === "/signals") { return [{ channel: "alerts" }, { channel: "classifier-hits" }, { channel: "inferred-dark" }]; } if (pathname === "/charts") { return [...chartSubs, { channel: "classifier-hits" }, { channel: "inferred-dark" }]; } if (pathname === "/replay") { return []; } return [ { channel: "equities" }, { channel: "flow" }, { channel: "alerts" }, { channel: "classifier-hits" }, { channel: "inferred-dark" }, ...chartSubs ]; }; const useLiveSession = ( enabled: boolean, pathname: string, chartTicker: string, chartIntervalMs: number ): LiveSessionState => { const [status, setStatus] = useState(enabled ? "connecting" : "disconnected"); const [lastUpdate, setLastUpdate] = useState(null); const [options, setOptions] = useState([]); const [nbbo, setNbbo] = useState([]); const [equities, setEquities] = useState([]); const [equityJoins, setEquityJoins] = useState([]); const [flow, setFlow] = useState([]); const [classifierHits, setClassifierHits] = useState([]); const [alerts, setAlerts] = useState([]); const [inferredDark, setInferredDark] = useState([]); const [chartCandles, setChartCandles] = useState([]); const [chartOverlay, setChartOverlay] = useState([]); const socketRef = useRef(null); const reconnectRef = useRef(null); const subscribedKeysRef = useRef>(new Set()); const subscribedMapRef = useRef>(new Map()); const manifest = useMemo( () => getLiveManifest(pathname, chartTicker.toUpperCase(), chartIntervalMs), [pathname, chartTicker, chartIntervalMs] ); useEffect(() => { if (!enabled) { setStatus("disconnected"); setLastUpdate(null); setOptions([]); setNbbo([]); setEquities([]); setEquityJoins([]); setFlow([]); setClassifierHits([]); setAlerts([]); setInferredDark([]); setChartCandles([]); setChartOverlay([]); subscribedKeysRef.current = new Set(); subscribedMapRef.current = new Map(); if (socketRef.current) { socketRef.current.close(); socketRef.current = null; } if (reconnectRef.current !== null) { window.clearTimeout(reconnectRef.current); reconnectRef.current = null; } return; } let active = true; const syncSubscriptions = (socket: WebSocket) => { const nextKeys = new Set(manifest.map(getLiveSubscriptionKey)); const nextMap = new Map(manifest.map((sub) => [getLiveSubscriptionKey(sub), sub])); const currentKeys = subscribedKeysRef.current; const toSubscribe = manifest.filter((sub) => !currentKeys.has(getLiveSubscriptionKey(sub))); const toUnsubscribe = Array.from(currentKeys) .filter((key) => !nextKeys.has(key)) .map((key) => subscribedMapRef.current.get(key) ?? null) .filter((sub): sub is LiveSubscription => sub !== null); if (toUnsubscribe.length > 0) { socket.send(JSON.stringify({ op: "unsubscribe", subscriptions: toUnsubscribe })); } if (toSubscribe.length > 0) { socket.send(JSON.stringify({ op: "subscribe", subscriptions: toSubscribe })); } subscribedKeysRef.current = nextKeys; subscribedMapRef.current = nextMap; }; const handleMessage = (message: LiveServerMessage) => { if (message.op === "ready" || message.op === "heartbeat") { return; } if (message.op === "error") { console.warn("Live socket error", message.message); return; } const subscription = message.op === "snapshot" ? message.snapshot.subscription : message.subscription; const items = message.op === "snapshot" ? message.snapshot.items : [message.item]; const updateAt = Date.now(); const mergeItems = ( setter: React.Dispatch>, nextItems: T[] ) => { setter((prev) => message.op === "snapshot" ? (nextItems as T[]) : mergeNewest(nextItems as T[], prev) ); }; switch (subscription.channel) { case "options": mergeItems(setOptions, items as OptionPrint[]); break; case "nbbo": mergeItems(setNbbo, items as OptionNBBO[]); break; case "equities": mergeItems(setEquities, items as EquityPrint[]); break; case "equity-joins": mergeItems(setEquityJoins, items as EquityPrintJoin[]); break; case "flow": mergeItems(setFlow, items as FlowPacket[]); break; case "classifier-hits": mergeItems(setClassifierHits, items as ClassifierHitEvent[]); break; case "alerts": mergeItems(setAlerts, items as AlertEvent[]); break; case "inferred-dark": mergeItems(setInferredDark, items as InferredDarkEvent[]); break; case "equity-candles": mergeItems(setChartCandles, items as EquityCandle[]); break; case "equity-overlay": mergeItems(setChartOverlay, items as EquityPrint[]); break; } setLastUpdate(updateAt); }; const connect = () => { if (!active) { return; } setStatus("connecting"); const socket = new WebSocket(buildWsUrl("/ws/live")); socketRef.current = socket; socket.onopen = () => { if (!active) { return; } setStatus("connected"); syncSubscriptions(socket); }; socket.onmessage = (event) => { if (!active) { return; } try { const parsed = JSON.parse(event.data) as LiveServerMessage; handleMessage(parsed); } catch (error) { console.warn("Failed to parse live session payload", error); } }; socket.onclose = () => { if (!active) { return; } setStatus("disconnected"); subscribedKeysRef.current = new Set(); subscribedMapRef.current = new Map(); reconnectRef.current = window.setTimeout(connect, 1000); }; socket.onerror = () => { if (!active) { return; } setStatus("disconnected"); socket.close(); }; }; connect(); return () => { active = false; if (reconnectRef.current !== null) { window.clearTimeout(reconnectRef.current); } if (socketRef.current) { socketRef.current.close(); } }; }, [enabled]); useEffect(() => { const socket = socketRef.current; if (!enabled || !socket || socket.readyState !== WebSocket.OPEN) { return; } const nextKeys = new Set(manifest.map(getLiveSubscriptionKey)); const nextMap = new Map(manifest.map((sub) => [getLiveSubscriptionKey(sub), sub])); const currentKeys = subscribedKeysRef.current; const toSubscribe = manifest.filter((sub) => !currentKeys.has(getLiveSubscriptionKey(sub))); const removedKeys = Array.from(currentKeys).filter((key) => !nextKeys.has(key)); if (removedKeys.length > 0) { const removedSubs = removedKeys .map((key) => subscribedMapRef.current.get(key) ?? null) .filter((sub): sub is LiveSubscription => sub !== null); if (removedSubs.length > 0) { socket.send(JSON.stringify({ op: "unsubscribe", subscriptions: removedSubs })); } } if (toSubscribe.length > 0) { socket.send(JSON.stringify({ op: "subscribe", subscriptions: toSubscribe })); } subscribedKeysRef.current = nextKeys; subscribedMapRef.current = nextMap; }, [enabled, manifest]); return { status, lastUpdate, options, nbbo, equities, equityJoins, flow, classifierHits, alerts, inferredDark, chartCandles, chartOverlay }; }; type TapeStatusProps = { status: WsStatus; lastUpdate: number | null; replayTime: number | null; replayComplete: boolean; paused: boolean; dropped: number; mode: TapeMode; }; const TapeStatus = ({ status, lastUpdate: _lastUpdate, replayTime, replayComplete, paused, dropped, mode }: TapeStatusProps) => { const label = replayComplete ? "Replay Complete" : statusLabel(status, paused, mode); const pausedLabel = paused && dropped > 0 ? `+${dropped} queued` : ""; return (
{label} {mode === "replay" ? ( Replay time {replayTime ? formatTime(replayTime) : "—"} ) : null} {pausedLabel || "+000 queued"}
); }; type TapeControlsProps = { paused: boolean; onTogglePause: () => void; isAtTop: boolean; missed: number; onJump: () => void; }; const TapeControls = ({ paused, onTogglePause, isAtTop, missed, onJump }: TapeControlsProps) => { const active = !isAtTop && missed > 0; return (
{active ? `+${missed} new` : ""}
); }; type CandleChartProps = { ticker: string; intervalMs: number; mode: TapeMode; replayTime?: number | null; liveCandles?: EquityCandle[]; liveOverlayPrints?: EquityPrint[]; classifierHits: ClassifierHitEvent[]; inferredDark: InferredDarkEvent[]; onClassifierHitClick: (hit: ClassifierHitEvent) => void; onInferredDarkClick: (event: InferredDarkEvent) => void; }; type MarkerAction = | { kind: "hit"; hit: ClassifierHitEvent } | { kind: "dark"; event: InferredDarkEvent }; const CandleChart = ({ ticker, intervalMs, mode, replayTime = null, liveCandles = [], liveOverlayPrints = [], classifierHits, inferredDark, onClassifierHitClick, onInferredDarkClick }: CandleChartProps) => { const containerRef = useRef(null); const chartRef = useRef(null); const seriesRef = useRef(null); const socketRef = useRef(null); const reconnectRef = useRef(null); const overlaySocketRef = useRef(null); const overlayReconnectRef = useRef(null); const lastCandleRef = useRef<{ time: UTCTimestamp; seq: number } | null>(null); const markerLookupRef = useRef>(new Map()); const [visibleRangeMs, setVisibleRangeMs] = useState<{ from: number; to: number } | null>(null); const onHitClickRef = useRef(onClassifierHitClick); const onDarkClickRef = useRef(onInferredDarkClick); const overlayCanvasRef = useRef(null); const overlayCtxRef = useRef(null); const overlayDataRef = useRef([]); const overlayLiveRef = useRef([]); const overlayLastFetchRef = useRef<{ startTs: number; endTs: number; ticker: string } | null>( null ); const overlayFetchAbortRef = useRef(null); const overlayTimerRef = useRef(null); const [overlayEnabled, setOverlayEnabled] = useState(true); const drawOverlay = useCallback( (points: EquityOverlayPoint[]) => { const canvas = overlayCanvasRef.current; const ctx = overlayCtxRef.current; const chart = chartRef.current; if (!canvas || !ctx || !chart) { return; } ctx.clearRect(0, 0, canvas.width, canvas.height); if (!overlayEnabled || points.length === 0) { canvas.style.opacity = "0"; return; } const timeScale = chart.timeScale(); if (!seriesRef.current) { canvas.style.opacity = "0"; return; } const filtered = points.filter((point) => point.offExchangeFlag); const sampled = sampleToLimit(filtered, 1400); const maxRadius = 10; const minRadius = 2; const maxSize = Math.max(1, ...sampled.map((point) => point.size)); ctx.globalAlpha = 0.9; ctx.fillStyle = "rgba(31, 74, 123, 0.55)"; ctx.strokeStyle = "rgba(31, 74, 123, 0.95)"; for (const point of sampled) { const x = timeScale.timeToCoordinate(toChartTime(point.ts)); const y = seriesRef.current.priceToCoordinate(point.price); if (x === null || y === null) { continue; } const radius = clamp( minRadius + (Math.sqrt(point.size) / Math.sqrt(maxSize)) * (maxRadius - minRadius), minRadius, maxRadius ); ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); } ctx.globalAlpha = 1; canvas.style.opacity = "1"; }, [overlayEnabled] ); useEffect(() => { drawOverlay([...overlayDataRef.current, ...overlayLiveRef.current]); }, [drawOverlay, ticker, intervalMs, mode]); useEffect(() => { onHitClickRef.current = onClassifierHitClick; }, [onClassifierHitClick]); useEffect(() => { onDarkClickRef.current = onInferredDarkClick; }, [onInferredDarkClick]); const markerBundle = useMemo(() => { const lookup = new Map(); const markers: SeriesMarker[] = []; if (!visibleRangeMs) { return { markers, lookup }; } const { from, to } = visibleRangeMs; const inRangeHits = classifierHits .filter((hit) => hit.source_ts >= from && hit.source_ts <= to) .sort((a, b) => { const delta = a.source_ts - b.source_ts; if (delta !== 0) { return delta; } return a.seq - b.seq; }); const inRangeDark = inferredDark .filter((event) => event.source_ts >= from && event.source_ts <= to) .sort((a, b) => { const delta = a.source_ts - b.source_ts; if (delta !== 0) { return delta; } return a.seq - b.seq; }); const MAX_HIT_MARKERS = 220; const MAX_DARK_MARKERS = 120; const MAX_TOTAL_MARKERS = 320; const cappedHits = inRangeHits.length > MAX_HIT_MARKERS ? inRangeHits.slice(inRangeHits.length - MAX_HIT_MARKERS) : inRangeHits; const cappedDark = inRangeDark.length > MAX_DARK_MARKERS ? inRangeDark.slice(inRangeDark.length - MAX_DARK_MARKERS) : inRangeDark; for (const hit of cappedHits) { const direction = normalizeDirection(hit.direction); const markerId = `hit:${hit.trace_id}:${hit.seq}`; lookup.set(markerId, { kind: "hit", hit }); markers.push({ id: markerId, time: toChartTime(hit.source_ts), position: direction === "bullish" ? "belowBar" : "aboveBar", color: direction === "bullish" ? "#2f6d4f" : direction === "bearish" ? "#c46f2a" : "rgba(111, 91, 57, 0.9)", shape: direction === "bullish" ? "arrowUp" : direction === "bearish" ? "arrowDown" : "circle", text: hit.classifier_id ? hit.classifier_id.slice(0, 3).toUpperCase() : "H" }); } for (const event of cappedDark) { const markerId = `dark:${event.trace_id}:${event.seq}`; lookup.set(markerId, { kind: "dark", event }); markers.push({ id: markerId, time: toChartTime(event.source_ts), position: "aboveBar", color: "rgba(31, 74, 123, 0.9)", shape: "square", text: "D" }); } markers.sort((a, b) => { const delta = Number(a.time) - Number(b.time); if (delta !== 0) { return delta; } return String(a.id ?? "").localeCompare(String(b.id ?? "")); }); const cappedMarkers = markers.length > MAX_TOTAL_MARKERS ? markers.slice(markers.length - MAX_TOTAL_MARKERS) : markers; if (cappedMarkers !== markers) { const nextLookup = new Map(); for (const marker of cappedMarkers) { const id = marker.id; if (typeof id !== "string") { continue; } const action = lookup.get(id); if (action) { nextLookup.set(id, action); } } return { markers: cappedMarkers, lookup: nextLookup }; } return { markers: cappedMarkers, lookup }; }, [classifierHits, inferredDark, visibleRangeMs]); useEffect(() => { if (!seriesRef.current) { return; } markerLookupRef.current = markerBundle.lookup; seriesRef.current.setMarkers(markerBundle.markers); }, [markerBundle]); const replayBucket = useMemo(() => { if (mode !== "replay" || replayTime === null) { return null; } return Math.floor(replayTime / intervalMs); }, [mode, replayTime, intervalMs]); const replayEndTs = useMemo(() => { if (replayBucket === null) { return null; } return (replayBucket + 1) * intervalMs - 1; }, [replayBucket, intervalMs]); const [ready, setReady] = useState(false); const [status, setStatus] = useState(mode === "live" ? "connecting" : "connected"); const [lastUpdate, setLastUpdate] = useState(null); const [hasData, setHasData] = useState(false); const [error, setError] = useState(null); useLayoutEffect(() => { const container = containerRef.current; if (!container) { return; } const width = container.clientWidth || 600; const height = container.clientHeight || 360; const chart = createChart(container, { width, height, layout: { background: { color: "#fffdf7" }, textColor: "#4e3e25" }, grid: { vertLines: { color: "rgba(82, 64, 36, 0.12)" }, horzLines: { color: "rgba(82, 64, 36, 0.12)" } }, crosshair: { vertLine: { color: "rgba(47, 109, 79, 0.35)" }, horzLine: { color: "rgba(47, 109, 79, 0.35)" } }, timeScale: { borderColor: "rgba(111, 91, 57, 0.35)", timeVisible: true, secondsVisible: intervalMs < 60000 }, rightPriceScale: { borderColor: "rgba(111, 91, 57, 0.35)" } }); const overlayCanvas = document.createElement("canvas"); overlayCanvas.width = Math.max(1, Math.floor(width)); overlayCanvas.height = Math.max(1, Math.floor(height)); overlayCanvas.style.position = "absolute"; overlayCanvas.style.inset = "0"; overlayCanvas.style.pointerEvents = "none"; overlayCanvas.style.zIndex = "2"; overlayCanvas.style.opacity = "0"; container.style.position = "relative"; container.appendChild(overlayCanvas); overlayCanvasRef.current = overlayCanvas; overlayCtxRef.current = overlayCanvas.getContext("2d"); const series = chart.addCandlestickSeries({ upColor: "#2f6d4f", downColor: "#c46f2a", borderVisible: false, wickUpColor: "#2f6d4f", wickDownColor: "#c46f2a" }); chartRef.current = chart; seriesRef.current = series; setReady(true); const timeScale = chart.timeScale(); const updateVisibleRange = () => { const range = timeScale.getVisibleRange(); if (!range) { setVisibleRangeMs(null); return; } const from = chartTimeToMs(range.from); const to = chartTimeToMs(range.to); if (from === null || to === null) { setVisibleRangeMs(null); return; } setVisibleRangeMs({ from: Math.min(from, to), to: Math.max(from, to) }); }; const clickHandler = (param: { hoveredObjectId?: unknown }) => { const hovered = param.hoveredObjectId; if (hovered === null || hovered === undefined) { return; } const key = typeof hovered === "string" ? hovered : String(hovered); const action = markerLookupRef.current.get(key); if (!action) { return; } if (action.kind === "hit") { onHitClickRef.current(action.hit); } else { onDarkClickRef.current(action.event); } }; updateVisibleRange(); timeScale.subscribeVisibleTimeRangeChange(updateVisibleRange); chart.subscribeClick(clickHandler); const resizeObserver = new ResizeObserver((entries) => { const entry = entries[0]; if (!entry) { return; } const { width: nextWidth, height: nextHeight } = entry.contentRect; if (Number.isFinite(nextWidth) && Number.isFinite(nextHeight)) { const nextW = Math.max(1, Math.floor(nextWidth)); const nextH = Math.max(1, Math.floor(nextHeight)); chart.applyOptions({ width: nextW, height: nextH }); const canvas = overlayCanvasRef.current; if (canvas) { canvas.width = nextW; canvas.height = nextH; } } }); resizeObserver.observe(container); return () => { resizeObserver.disconnect(); timeScale.unsubscribeVisibleTimeRangeChange(updateVisibleRange); chart.unsubscribeClick(clickHandler); chart.remove(); chartRef.current = null; seriesRef.current = null; overlayCtxRef.current = null; overlayCanvasRef.current?.remove(); overlayCanvasRef.current = null; }; }, []); useEffect(() => { if (!ready || !seriesRef.current) { return; } if (mode === "replay" && replayBucket === null) { setError(null); setHasData(false); setLastUpdate(null); lastCandleRef.current = null; seriesRef.current.setData([]); overlayDataRef.current = []; overlayLiveRef.current = []; overlayLastFetchRef.current = null; setStatus("connected"); return; } let active = true; setError(null); setHasData(false); setLastUpdate(null); lastCandleRef.current = null; seriesRef.current.setData([]); overlayDataRef.current = []; overlayLiveRef.current = []; overlayLastFetchRef.current = null; setStatus(mode === "live" ? "connecting" : "connected"); const fetchCandles = async () => { try { const url = new URL(buildApiUrl("/candles/equities")); url.searchParams.set("underlying_id", ticker); url.searchParams.set("interval_ms", intervalMs.toString()); url.searchParams.set("limit", "300"); url.searchParams.set("cache", "1"); if (mode === "replay" && replayEndTs !== null) { url.searchParams.set("end_ts", replayEndTs.toString()); } const response = await fetch(url.toString()); if (!response.ok) { const detail = await readErrorDetail(response); throw new Error( `Candle fetch failed (${response.status})${detail ? `: ${detail}` : ""}` ); } const payload = (await response.json()) as { data?: EquityCandle[] }; if (!active || !seriesRef.current) { return; } const sorted = [...(payload.data ?? [])].sort((a, b) => { if (a.ts !== b.ts) { return a.ts - b.ts; } return a.seq - b.seq; }); const chartData = sorted.map(toChartCandle); seriesRef.current.setData(chartData); chartRef.current?.timeScale().fitContent(); drawOverlay([...overlayDataRef.current, ...overlayLiveRef.current]); if (sorted.length > 0) { const last = sorted[sorted.length - 1]; lastCandleRef.current = { time: toChartTime(last.ts), seq: last.seq }; setHasData(true); setLastUpdate(last.ingest_ts ?? last.ts); } } catch (error) { if (!active) { return; } setError(error instanceof Error ? error.message : String(error)); setStatus("disconnected"); setHasData(false); } }; const ensureOverlayListener = () => { if (!chartRef.current) { return; } const handler = () => { const combined = [...overlayDataRef.current, ...overlayLiveRef.current]; drawOverlay(combined); scheduleOverlayFetch(); }; chartRef.current.timeScale().subscribeVisibleTimeRangeChange(handler); return () => { chartRef.current?.timeScale().unsubscribeVisibleTimeRangeChange(handler); }; }; const cancelOverlayFetch = () => { if (overlayFetchAbortRef.current) { overlayFetchAbortRef.current.abort(); overlayFetchAbortRef.current = null; } }; const fetchOverlayRange = async (startTs: number, endTs: number) => { cancelOverlayFetch(); const abort = new AbortController(); overlayFetchAbortRef.current = abort; const url = new URL(buildApiUrl("/prints/equities/range")); url.searchParams.set("underlying_id", ticker); url.searchParams.set("start_ts", Math.floor(startTs).toString()); url.searchParams.set("end_ts", Math.floor(endTs).toString()); url.searchParams.set("limit", "2500"); const response = await fetch(url.toString(), { signal: abort.signal }); if (!response.ok) { const detail = await readErrorDetail(response); throw new Error( `Equity range fetch failed (${response.status})${detail ? `: ${detail}` : ""}` ); } const payload = (await response.json()) as { data?: EquityPrint[] }; const prints = payload.data ?? []; overlayDataRef.current = prints.map((print) => ({ ts: print.ts, price: print.price, size: print.size, offExchangeFlag: print.offExchangeFlag })); overlayLiveRef.current = []; overlayLastFetchRef.current = { startTs, endTs, ticker }; }; function scheduleOverlayFetch() { if (overlayTimerRef.current !== null) { window.clearTimeout(overlayTimerRef.current); } overlayTimerRef.current = window.setTimeout(() => { if (!active || !chartRef.current || !seriesRef.current) { return; } const timeScale = chartRef.current.timeScale(); const range = timeScale.getVisibleRange(); if (!range) { return; } const startTs = chartTimeToMs(range.from); const endTs = chartTimeToMs(range.to); if (startTs === null || endTs === null) { return; } const last = overlayLastFetchRef.current; const needsFetch = !last || last.ticker !== ticker || startTs < last.startTs || endTs > last.endTs || Math.abs(endTs - last.endTs) > intervalMs * 6; if (!needsFetch) { return; } void fetchOverlayRange(startTs, endTs) .then(() => { drawOverlay([...overlayDataRef.current, ...overlayLiveRef.current]); }) .catch((error) => { if (!active) { return; } if (error instanceof DOMException && error.name === "AbortError") { return; } console.warn("Overlay fetch failed", error); }); }, 180); } const overlayUnsubscribe = ensureOverlayListener(); scheduleOverlayFetch(); void fetchCandles(); return () => { active = false; cancelOverlayFetch(); if (overlayTimerRef.current !== null) { window.clearTimeout(overlayTimerRef.current); overlayTimerRef.current = null; } overlayUnsubscribe?.(); }; }, [ready, ticker, intervalMs, mode, replayBucket, replayEndTs]); useEffect(() => { if (!ready || mode !== "live" || !seriesRef.current) { if (socketRef.current) { socketRef.current.close(); } if (reconnectRef.current !== null) { window.clearTimeout(reconnectRef.current); reconnectRef.current = null; } if (overlaySocketRef.current) { overlaySocketRef.current.close(); } if (overlayReconnectRef.current !== null) { window.clearTimeout(overlayReconnectRef.current); overlayReconnectRef.current = null; } return; } if (mode !== "live" || !seriesRef.current) { return; } const sortedCandles = [...liveCandles].sort((a, b) => (a.ts - b.ts) || (a.seq - b.seq)); if (sortedCandles.length > 0) { seriesRef.current.setData(sortedCandles.map(toChartCandle)); const last = sortedCandles.at(-1); if (last) { lastCandleRef.current = { time: toChartTime(last.ts), seq: last.seq }; setHasData(true); setLastUpdate(last.ingest_ts ?? last.ts); setStatus("connected"); } } overlayLiveRef.current = liveOverlayPrints.map((print) => ({ ts: print.ts, price: print.price, size: print.size, offExchangeFlag: print.offExchangeFlag })); drawOverlay([...overlayDataRef.current, ...overlayLiveRef.current]); }, [ready, mode, liveCandles, liveOverlayPrints, drawOverlay]); useEffect(() => { if (!chartRef.current) { return; } chartRef.current.timeScale().applyOptions({ timeVisible: true, secondsVisible: intervalMs < 60000 }); }, [intervalMs]); const statusText = statusLabel(status, false, mode); const intervalLabel = formatIntervalLabel(intervalMs); const emptyLabel = mode === "live" ? status === "connected" ? `No candles yet. First ${intervalLabel} candle appears after the window closes.` : "Chart offline. Start candles service." : "No candles for this replay window."; return (
{statusText}
{lastUpdate ? `Updated ${formatTime(lastUpdate)}` : "Waiting for data"} Blue circles = off-exchange trades
{error ? (
Chart error: {error}
) : !hasData ? (
{emptyLabel}
) : null}
); }; type AlertSeverityStripProps = { alerts: AlertEvent[]; }; const AlertSeverityStrip = ({ alerts }: AlertSeverityStripProps) => { const windowMs = 30 * 60 * 1000; const now = Date.now(); const severityCounts = alerts.reduce( (acc, alert) => { if (now - alert.source_ts > windowMs) { return acc; } if (alert.severity === "high") { acc.high += 1; } else if (alert.severity === "medium") { acc.medium += 1; } else { acc.low += 1; } return acc; }, { high: 0, medium: 0, low: 0 } ); const directionCounts = alerts.reduce( (acc, alert) => { if (now - alert.source_ts > windowMs) { return acc; } const direction = normalizeDirection(alert.hits[0]?.direction ?? "neutral"); acc[direction] += 1; return acc; }, { bullish: 0, bearish: 0, neutral: 0 } ); const severityTotal = severityCounts.high + severityCounts.medium + severityCounts.low; const highPct = severityTotal > 0 ? (severityCounts.high / severityTotal) * 100 : 0; const mediumPct = severityTotal > 0 ? (severityCounts.medium / severityTotal) * 100 : 0; const lowPct = severityTotal > 0 ? (severityCounts.low / severityTotal) * 100 : 0; const directionTotal = directionCounts.bullish + directionCounts.bearish + directionCounts.neutral; const bullishPct = directionTotal > 0 ? (directionCounts.bullish / directionTotal) * 100 : 0; const bearishPct = directionTotal > 0 ? (directionCounts.bearish / directionTotal) * 100 : 0; const neutralPct = directionTotal > 0 ? (directionCounts.neutral / directionTotal) * 100 : 0; return (
Severity (last 30m) {severityTotal} alerts
{severityCounts.high > 0 ? `High ${severityCounts.high}` : ""}
{severityCounts.medium > 0 ? `Med ${severityCounts.medium}` : ""}
{severityCounts.low > 0 ? `Low ${severityCounts.low}` : ""}
Direction (last 30m) {directionTotal} alerts
{directionCounts.bullish > 0 ? `Bull ${directionCounts.bullish}` : ""}
{directionCounts.bearish > 0 ? `Bear ${directionCounts.bearish}` : ""}
{directionCounts.neutral > 0 ? `Neut ${directionCounts.neutral}` : ""}
); }; type EvidenceItem = | { kind: "flow"; id: string; packet: FlowPacket } | { kind: "print"; id: string; print: OptionPrint } | { kind: "unknown"; id: string }; type DarkEvidenceItem = | { kind: "join"; id: string; join: EquityPrintJoin } | { kind: "unknown"; id: string }; type AlertDrawerProps = { alert: AlertEvent; flowPacket: FlowPacket | null; evidence: EvidenceItem[]; onClose: () => void; }; const AlertDrawer = ({ alert, flowPacket, evidence, onClose }: AlertDrawerProps) => { const primary = alert.hits[0]; const direction = primary ? normalizeDirection(primary.direction) : "neutral"; const evidencePrints = evidence.filter((item) => item.kind === "print"); const unknownCount = evidence.filter((item) => item.kind === "unknown").length; return ( ); }; type ClassifierHitDrawerProps = { hit: ClassifierHitEvent; flowPacket: FlowPacket | null; evidence: EvidenceItem[]; onClose: () => void; }; const ClassifierHitDrawer = ({ hit, flowPacket, evidence, onClose }: ClassifierHitDrawerProps) => { const direction = normalizeDirection(hit.direction); const evidencePrints = evidence.filter((item) => item.kind === "print"); const unknownCount = evidence.filter((item) => item.kind === "unknown").length; return ( ); }; type DarkDrawerProps = { event: InferredDarkEvent; evidence: DarkEvidenceItem[]; underlying: string | null; onClose: () => void; }; const DarkDrawer = ({ event, evidence, underlying, onClose }: DarkDrawerProps) => { const joinEvidence = evidence.filter( (item): item is { kind: "join"; id: string; join: EquityPrintJoin } => item.kind === "join" ); const unknownCount = evidence.filter((item) => item.kind === "unknown").length; const traceRefs = event.evidence_refs.slice(0, 6); const extraRefs = Math.max(0, event.evidence_refs.length - traceRefs.length); return ( ); }; const formatFlowMetric = (value: number, suffix?: string): string => { if (suffix) { return `${value}${suffix}`; } return value.toLocaleString(); }; const useTerminalState = () => { const pathname = usePathname(); const [mode, setMode] = useState("live"); const [replaySource, setReplaySource] = useState(null); const [selectedAlert, setSelectedAlert] = useState(null); const [selectedDarkEvent, setSelectedDarkEvent] = useState(null); const [selectedClassifierHit, setSelectedClassifierHit] = useState(null); const [filterInput, setFilterInput] = useState(""); const [chartIntervalMs, setChartIntervalMs] = useState(CANDLE_INTERVALS[0].ms); const activeTickers = useMemo(() => { const parts = filterInput .split(/[,\s]+/) .map((value) => value.trim().toUpperCase()) .filter(Boolean); return Array.from(new Set(parts)); }, [filterInput]); const tickerSet = useMemo(() => new Set(activeTickers), [activeTickers]); const chartTicker = useMemo(() => activeTickers[0] ?? "SPY", [activeTickers]); const liveSession = useLiveSession(mode === "live", pathname, chartTicker, chartIntervalMs); const handleReplaySource = useCallback((value: string | null) => { setReplaySource(value); }, []); useEffect(() => { setReplaySource(null); }, [mode]); const optionsScroll = useListScroll(); const equitiesScroll = useListScroll(); const flowScroll = useListScroll(); const darkScroll = useListScroll(); const alertsScroll = useListScroll(); const classifierScroll = useListScroll(); const optionsAnchor = useScrollAnchor(optionsScroll.listRef, optionsScroll.isAtTopRef); const equitiesAnchor = useScrollAnchor(equitiesScroll.listRef, equitiesScroll.isAtTopRef); const flowAnchor = useScrollAnchor(flowScroll.listRef, flowScroll.isAtTopRef); const darkAnchor = useScrollAnchor(darkScroll.listRef, darkScroll.isAtTopRef); const alertsAnchor = useScrollAnchor(alertsScroll.listRef, alertsScroll.isAtTopRef); const classifierAnchor = useScrollAnchor( classifierScroll.listRef, classifierScroll.isAtTopRef ); const disableReplayGrouping = useCallback(() => null, []); const options = useTape({ mode, liveEnabled: false, wsPath: "/ws/options", replayPath: "/replay/options", latestPath: "/prints/options", expectedType: "option-print", batchSize: mode === "replay" ? 120 : undefined, pollMs: mode === "replay" ? 200 : undefined, captureScroll: optionsAnchor.capture, onNewItems: optionsScroll.onNewItems, getReplayKey: extractReplaySource, onReplaySourceKey: handleReplaySource }); const equities = useTape({ mode, liveEnabled: false, wsPath: "/ws/equities", replayPath: "/replay/equities", latestPath: "/prints/equities", expectedType: "equity-print", batchSize: mode === "replay" ? 120 : undefined, pollMs: mode === "replay" ? 200 : undefined, captureScroll: equitiesAnchor.capture, onNewItems: equitiesScroll.onNewItems }); const equityJoins = useTape({ mode, liveEnabled: false, wsPath: "/ws/equity-joins", replayPath: "/replay/equity-joins", latestPath: "/joins/equities", expectedType: "equity-join", batchSize: mode === "replay" ? 120 : undefined, pollMs: mode === "replay" ? 200 : undefined, getReplayKey: disableReplayGrouping }); const nbbo = useTape({ mode, liveEnabled: false, wsPath: "/ws/options-nbbo", replayPath: "/replay/nbbo", latestPath: "/nbbo/options", expectedType: "option-nbbo", batchSize: mode === "replay" ? 120 : undefined, pollMs: mode === "replay" ? 200 : undefined, getReplayKey: extractReplaySource, replaySourceKey: replaySource }); const inferredDark = useTape({ mode, liveEnabled: false, wsPath: "/ws/inferred-dark", replayPath: "/replay/inferred-dark", latestPath: "/dark/inferred", expectedType: "inferred-dark", batchSize: mode === "replay" ? 120 : undefined, pollMs: mode === "replay" ? 200 : undefined, captureScroll: darkAnchor.capture, onNewItems: darkScroll.onNewItems, getReplayKey: disableReplayGrouping }); const flow = useTape({ mode, liveEnabled: false, wsPath: "/ws/flow", replayPath: "/replay/flow", latestPath: "/flow/packets", expectedType: "flow-packet", batchSize: mode === "replay" ? 120 : undefined, pollMs: mode === "replay" ? 200 : undefined, captureScroll: flowAnchor.capture, onNewItems: flowScroll.onNewItems, getReplayKey: disableReplayGrouping }); const alerts = useTape({ mode, liveEnabled: false, wsPath: "/ws/alerts", replayPath: "/replay/alerts", latestPath: "/flow/alerts", expectedType: "alert", batchSize: mode === "replay" ? 120 : undefined, pollMs: mode === "replay" ? 200 : undefined, captureScroll: alertsAnchor.capture, onNewItems: alertsScroll.onNewItems, getReplayKey: disableReplayGrouping }); const classifierHits = useTape({ mode, liveEnabled: false, wsPath: "/ws/classifier-hits", replayPath: "/replay/classifier-hits", latestPath: "/flow/classifier-hits", expectedType: "classifier-hit", batchSize: mode === "replay" ? 120 : undefined, pollMs: mode === "replay" ? 200 : undefined, captureScroll: classifierAnchor.capture, onNewItems: classifierScroll.onNewItems, getReplayKey: disableReplayGrouping }); const optionsFeed = mode === "live" ? toStaticTapeState(liveSession.status, liveSession.options, liveSession.lastUpdate) : options; const nbboFeed = mode === "live" ? toStaticTapeState(liveSession.status, liveSession.nbbo, liveSession.lastUpdate) : nbbo; const equitiesFeed = mode === "live" ? toStaticTapeState(liveSession.status, liveSession.equities, liveSession.lastUpdate) : equities; const equityJoinsFeed = mode === "live" ? toStaticTapeState(liveSession.status, liveSession.equityJoins, liveSession.lastUpdate) : equityJoins; const flowFeed = mode === "live" ? toStaticTapeState(liveSession.status, liveSession.flow, liveSession.lastUpdate) : flow; const alertsFeed = mode === "live" ? toStaticTapeState(liveSession.status, liveSession.alerts, liveSession.lastUpdate) : alerts; const classifierHitsFeed = mode === "live" ? toStaticTapeState(liveSession.status, liveSession.classifierHits, liveSession.lastUpdate) : classifierHits; const inferredDarkFeed = mode === "live" ? toStaticTapeState(liveSession.status, liveSession.inferredDark, liveSession.lastUpdate) : inferredDark; useLayoutEffect(() => { optionsAnchor.apply(); }, [optionsFeed.items, optionsAnchor.apply]); useLayoutEffect(() => { equitiesAnchor.apply(); }, [equitiesFeed.items, equitiesAnchor.apply]); useLayoutEffect(() => { flowAnchor.apply(); }, [flowFeed.items, flowAnchor.apply]); useLayoutEffect(() => { darkAnchor.apply(); }, [inferredDarkFeed.items, darkAnchor.apply]); useLayoutEffect(() => { alertsAnchor.apply(); }, [alertsFeed.items, alertsAnchor.apply]); useLayoutEffect(() => { classifierAnchor.apply(); }, [classifierHitsFeed.items, classifierAnchor.apply]); const nbboMap = useMemo(() => { const map = new Map(); for (const quote of nbboFeed.items) { const contractId = normalizeContractId(quote.option_contract_id); const existing = map.get(contractId); if ( !existing || quote.ts > existing.ts || (quote.ts === existing.ts && quote.seq >= existing.seq) ) { map.set(contractId, quote); } } return map; }, [nbboFeed.items]); const optionPrintMap = useMemo(() => { const map = new Map(); for (const print of optionsFeed.items) { if (print.trace_id) { map.set(print.trace_id, print); } } return map; }, [optionsFeed.items]); const equityPrintMap = useMemo(() => { const map = new Map(); for (const print of equitiesFeed.items) { if (print.trace_id) { map.set(print.trace_id, print); } } return map; }, [equitiesFeed.items]); const equityJoinMap = useMemo(() => { const map = new Map(); for (const join of equityJoinsFeed.items) { map.set(join.id, join); } return map; }, [equityJoinsFeed.items]); const flowPacketMap = useMemo(() => { const map = new Map(); for (const packet of flowFeed.items) { map.set(packet.id, packet); } return map; }, [flowFeed.items]); const [fetchedOptionPrintMap, setFetchedOptionPrintMap] = useState>( () => new Map() ); const [fetchedFlowPacketMap, setFetchedFlowPacketMap] = useState>( () => new Map() ); const [fetchedEquityJoinMap, setFetchedEquityJoinMap] = useState>( () => new Map() ); const mergedOptionPrintMap = useMemo(() => { const merged = new Map(optionPrintMap); for (const [key, value] of fetchedOptionPrintMap) { merged.set(key, value); } return merged; }, [optionPrintMap, fetchedOptionPrintMap]); const mergedFlowPacketMap = useMemo(() => { const merged = new Map(flowPacketMap); for (const [key, value] of fetchedFlowPacketMap) { merged.set(key, value); } return merged; }, [flowPacketMap, fetchedFlowPacketMap]); const mergedEquityJoinMap = useMemo(() => { const merged = new Map(equityJoinMap); for (const [key, value] of fetchedEquityJoinMap) { merged.set(key, value); } return merged; }, [equityJoinMap, fetchedEquityJoinMap]); useEffect(() => { if (!selectedAlert || mode !== "live") { return; } const packetId = selectedAlert.evidence_refs[0]; if (packetId && !mergedFlowPacketMap.has(packetId)) { void fetch(buildApiUrl(`/flow/packets/${encodeURIComponent(packetId)}`)) .then((response) => response.json()) .then((payload: { data?: FlowPacket | null }) => { if (!payload.data) { return; } setFetchedFlowPacketMap((prev) => new Map(prev).set(payload.data!.id, payload.data!)); }) .catch((error) => console.warn("Failed to fetch flow packet evidence", error)); } const missingPrintIds = selectedAlert.evidence_refs.filter( (id) => !mergedFlowPacketMap.has(id) && !mergedOptionPrintMap.has(id) ); if (missingPrintIds.length > 0) { const url = new URL(buildApiUrl("/option-prints/by-trace")); for (const traceId of missingPrintIds) { url.searchParams.append("trace_id", traceId); } void fetch(url.toString()) .then((response) => response.json()) .then((payload: { data?: OptionPrint[] }) => { const next = new Map(); for (const item of payload.data ?? []) { next.set(item.trace_id, item); } if (next.size > 0) { setFetchedOptionPrintMap((prev) => new Map([...prev, ...next])); } }) .catch((error) => console.warn("Failed to fetch option print evidence", error)); } }, [selectedAlert, mode, mergedFlowPacketMap, mergedOptionPrintMap]); useEffect(() => { if (!selectedDarkEvent || mode !== "live") { return; } const missingIds = selectedDarkEvent.evidence_refs.filter((id) => !mergedEquityJoinMap.has(id)); if (missingIds.length === 0) { return; } const url = new URL(buildApiUrl("/equity-joins/by-id")); for (const id of missingIds) { url.searchParams.append("id", id); } void fetch(url.toString()) .then((response) => response.json()) .then((payload: { data?: EquityPrintJoin[] }) => { const next = new Map(); for (const item of payload.data ?? []) { next.set(item.id, item); } if (next.size > 0) { setFetchedEquityJoinMap((prev) => new Map([...prev, ...next])); } }) .catch((error) => console.warn("Failed to fetch dark evidence joins", error)); }, [selectedDarkEvent, mode, mergedEquityJoinMap]); const selectedEvidence = useMemo((): EvidenceItem[] => { if (!selectedAlert) { return []; } return selectedAlert.evidence_refs.map((id) => { const packet = mergedFlowPacketMap.get(id); if (packet) { return { kind: "flow", id, packet }; } const print = mergedOptionPrintMap.get(id); if (print) { return { kind: "print", id, print }; } return { kind: "unknown", id }; }); }, [selectedAlert, mergedFlowPacketMap, mergedOptionPrintMap]); const selectedFlowPacket = useMemo(() => { if (!selectedAlert) { return null; } const packetId = selectedAlert.evidence_refs[0]; return packetId ? mergedFlowPacketMap.get(packetId) ?? null : null; }, [selectedAlert, mergedFlowPacketMap]); const selectedDarkEvidence = useMemo((): DarkEvidenceItem[] => { if (!selectedDarkEvent) { return []; } return selectedDarkEvent.evidence_refs.map((id) => { const join = mergedEquityJoinMap.get(id); if (join) { return { kind: "join", id, join }; } return { kind: "unknown", id }; }); }, [selectedDarkEvent, mergedEquityJoinMap]); const selectedDarkUnderlying = useMemo(() => { if (!selectedDarkEvent) { return null; } return inferDarkUnderlying(selectedDarkEvent, equityPrintMap, mergedEquityJoinMap); }, [selectedDarkEvent, mergedEquityJoinMap, equityPrintMap]); useEffect(() => { if (mode !== "live") { setSelectedAlert(null); } setSelectedDarkEvent(null); setSelectedClassifierHit(null); }, [mode]); const extractPacketContract = useCallback((packet: FlowPacket): string => { const contract = packet.features.option_contract_id; if (typeof contract === "string") { return contract; } const match = packet.id.match(/^flowpacket:([^:]+):/); return match?.[1] ?? packet.id; }, []); const extractUnderlyingFromTrace = useCallback((traceId: string): string | null => { const match = traceId.match(/flowpacket:([^:]+):/); if (!match?.[1]) { return null; } return extractUnderlying(match[1]); }, []); const extractPacketIdFromClassifierHitTrace = useCallback((traceId: string): string | null => { const idx = traceId.indexOf("flowpacket:"); if (idx < 0) { return null; } return traceId.slice(idx); }, []); const selectedClassifierPacketId = useMemo(() => { if (!selectedClassifierHit) { return null; } return extractPacketIdFromClassifierHitTrace(selectedClassifierHit.trace_id); }, [extractPacketIdFromClassifierHitTrace, selectedClassifierHit]); useEffect(() => { if (!selectedClassifierPacketId || mode !== "live") { return; } if (!mergedFlowPacketMap.has(selectedClassifierPacketId)) { void fetch(buildApiUrl(`/flow/packets/${encodeURIComponent(selectedClassifierPacketId)}`)) .then((response) => response.json()) .then((payload: { data?: FlowPacket | null }) => { if (!payload.data) { return; } setFetchedFlowPacketMap((prev) => new Map(prev).set(payload.data!.id, payload.data!)); }) .catch((error) => console.warn("Failed to fetch classifier flow packet", error)); } }, [selectedClassifierPacketId, mode, mergedFlowPacketMap]); const selectedClassifierFlowPacket = useMemo(() => { if (!selectedClassifierPacketId) { return null; } return mergedFlowPacketMap.get(selectedClassifierPacketId) ?? null; }, [mergedFlowPacketMap, selectedClassifierPacketId]); const selectedClassifierEvidence = useMemo((): EvidenceItem[] => { if (!selectedClassifierHit) { return []; } if (!selectedClassifierPacketId) { return []; } const packet = mergedFlowPacketMap.get(selectedClassifierPacketId); if (!packet) { return []; } return packet.members.map((id) => { const print = mergedOptionPrintMap.get(id); if (print) { return { kind: "print", id, print }; } return { kind: "unknown", id }; }); }, [mergedFlowPacketMap, mergedOptionPrintMap, selectedClassifierHit, selectedClassifierPacketId]); const inferAlertUnderlying = useCallback( (alert: AlertEvent): string | null => { const fromTrace = extractUnderlyingFromTrace(alert.trace_id); if (fromTrace) { return fromTrace; } const packetId = alert.evidence_refs[0]; if (packetId) { const packet = mergedFlowPacketMap.get(packetId); if (packet) { return extractUnderlying(extractPacketContract(packet)); } } for (const ref of alert.evidence_refs) { const print = mergedOptionPrintMap.get(ref); if (print) { return extractUnderlying(print.option_contract_id); } } return null; }, [extractPacketContract, extractUnderlyingFromTrace, mergedFlowPacketMap, mergedOptionPrintMap] ); const matchesTicker = useCallback( (value: string | null) => { if (tickerSet.size === 0) { return true; } if (!value) { return false; } return tickerSet.has(value.toUpperCase()); }, [tickerSet] ); const filteredOptions = useMemo(() => { if (tickerSet.size === 0) { return optionsFeed.items; } return optionsFeed.items.filter((print) => matchesTicker(extractUnderlying(normalizeContractId(print.option_contract_id))) ); }, [optionsFeed.items, matchesTicker, tickerSet]); const filteredEquities = useMemo(() => { if (tickerSet.size === 0) { return equitiesFeed.items; } return equitiesFeed.items.filter((print) => matchesTicker(print.underlying_id)); }, [equitiesFeed.items, matchesTicker, tickerSet]); const filteredInferredDark = useMemo(() => { if (tickerSet.size === 0) { return inferredDarkFeed.items; } return inferredDarkFeed.items.filter((event) => { const underlying = inferDarkUnderlying(event, equityPrintMap, mergedEquityJoinMap); return matchesTicker(underlying); }); }, [mergedEquityJoinMap, equityPrintMap, inferredDarkFeed.items, matchesTicker, tickerSet]); const filteredFlow = useMemo(() => { if (tickerSet.size === 0) { return flowFeed.items; } return flowFeed.items.filter((packet) => matchesTicker(extractUnderlying(extractPacketContract(packet))) ); }, [flowFeed.items, extractPacketContract, matchesTicker, tickerSet]); const filteredAlerts = useMemo(() => { if (tickerSet.size === 0) { return alertsFeed.items; } return alertsFeed.items.filter((alert) => matchesTicker(inferAlertUnderlying(alert))); }, [alertsFeed.items, inferAlertUnderlying, matchesTicker, tickerSet]); const filteredClassifierHits = useMemo(() => { if (tickerSet.size === 0) { return classifierHitsFeed.items; } return classifierHitsFeed.items.filter((hit) => { const underlying = extractUnderlyingFromTrace(hit.trace_id); return matchesTicker(underlying); }); }, [classifierHitsFeed.items, extractUnderlyingFromTrace, matchesTicker, tickerSet]); const chartClassifierHits = useMemo(() => { const desired = chartTicker.toUpperCase(); return classifierHitsFeed.items .filter((hit) => extractUnderlyingFromTrace(hit.trace_id) === desired) .sort((a, b) => { const delta = a.source_ts - b.source_ts; if (delta !== 0) { return delta; } return a.seq - b.seq; }); }, [chartTicker, classifierHitsFeed.items, extractUnderlyingFromTrace]); const chartInferredDark = useMemo(() => { const desired = chartTicker.toUpperCase(); return inferredDarkFeed.items .filter((event) => inferDarkUnderlying(event, equityPrintMap, mergedEquityJoinMap) === desired) .sort((a, b) => { const delta = a.source_ts - b.source_ts; if (delta !== 0) { return delta; } return a.seq - b.seq; }); }, [chartTicker, inferredDarkFeed.items, mergedEquityJoinMap, equityPrintMap]); const findAlertForClassifierHit = useCallback( (hit: ClassifierHitEvent): AlertEvent | null => { const packetId = extractPacketIdFromClassifierHitTrace(hit.trace_id); if (!packetId) { return null; } const desiredTrace = `alert:${packetId}`; return ( alertsFeed.items.find( (item) => item.trace_id === desiredTrace || item.evidence_refs[0] === packetId ) ?? null ); }, [alertsFeed.items, extractPacketIdFromClassifierHitTrace] ); const openFromClassifierHit = useCallback( (hit: ClassifierHitEvent) => { const alert = findAlertForClassifierHit(hit); if (alert) { setSelectedClassifierHit(null); setSelectedDarkEvent(null); setSelectedAlert(alert); return; } setSelectedAlert(null); setSelectedDarkEvent(null); setSelectedClassifierHit(hit); }, [findAlertForClassifierHit] ); const handleClassifierMarkerClick = useCallback( (hit: ClassifierHitEvent) => { openFromClassifierHit(hit); }, [openFromClassifierHit] ); const handleDarkMarkerClick = useCallback((event: InferredDarkEvent) => { setSelectedAlert(null); setSelectedClassifierHit(null); setSelectedDarkEvent(event); }, []); const lastSeen = useMemo(() => { return [ optionsFeed.lastUpdate, equitiesFeed.lastUpdate, inferredDarkFeed.lastUpdate, flowFeed.lastUpdate, alertsFeed.lastUpdate, classifierHitsFeed.lastUpdate ] .filter((value): value is number => value !== null) .sort((a, b) => b - a)[0] ?? null; }, [ optionsFeed.lastUpdate, equitiesFeed.lastUpdate, inferredDarkFeed.lastUpdate, flowFeed.lastUpdate, alertsFeed.lastUpdate, classifierHitsFeed.lastUpdate ]); return { mode, setMode, replaySource, setReplaySource, selectedAlert, setSelectedAlert, selectedDarkEvent, setSelectedDarkEvent, selectedClassifierHit, setSelectedClassifierHit, filterInput, setFilterInput, chartIntervalMs, setChartIntervalMs, optionsScroll, equitiesScroll, flowScroll, darkScroll, alertsScroll, classifierScroll, options: optionsFeed, equities: equitiesFeed, equityJoins: equityJoinsFeed, nbbo: nbboFeed, inferredDark: inferredDarkFeed, flow: flowFeed, alerts: alertsFeed, classifierHits: classifierHitsFeed, liveSession, activeTickers, tickerSet, chartTicker, nbboMap, optionPrintMap: mergedOptionPrintMap, equityPrintMap, equityJoinMap: mergedEquityJoinMap, flowPacketMap: mergedFlowPacketMap, selectedEvidence, selectedFlowPacket, selectedDarkEvidence, selectedDarkUnderlying, selectedClassifierPacketId, selectedClassifierFlowPacket, selectedClassifierEvidence, filteredOptions, filteredEquities, filteredInferredDark, filteredFlow, filteredAlerts, filteredClassifierHits, chartClassifierHits, chartInferredDark, openFromClassifierHit, handleClassifierMarkerClick, handleDarkMarkerClick, lastSeen, toggleMode: () => { setMode((prev) => (prev === "live" ? "replay" : "live")); } }; }; type TerminalState = ReturnType; const TerminalContext = createContext(null); const useTerminal = (): TerminalState => { const value = useContext(TerminalContext); if (!value) { throw new Error("Terminal context missing"); } return value; }; const NAV_ITEMS = [ { href: "/", label: "Overview" }, { href: "/tape", label: "Tape" }, { href: "/signals", label: "Signals" }, { href: "/charts", label: "Charts" }, { href: "/replay", label: "Replay" } ]; type PageFrameProps = { title: string; actions?: ReactNode; children: ReactNode; }; const PageFrame = ({ title, actions, children }: PageFrameProps) => { return (

{title}

{actions ?
{actions}
: null}
{children}
); }; type PaneProps = { title: string; status?: ReactNode; actions?: ReactNode; className?: string; children: ReactNode; }; const Pane = ({ title, status, actions, className = "", children }: PaneProps) => { const classes = ["terminal-pane", className].filter(Boolean).join(" "); return (

{title}

{status ?
{status}
: null}
{actions ?
{actions}
: null}
{children}
); }; const ShellMetricStrip = () => { const state = useTerminal(); const focus = state.activeTickers.length > 0 ? state.activeTickers.join(", ") : "ALL"; const replay = state.replaySource ? state.replaySource.toUpperCase() : "AUTO"; return (
Mode {state.mode === "live" ? "LIVE" : "REPLAY"}
Focus {focus}
Source {replay}
Last {state.lastSeen ? formatTime(state.lastSeen) : "WAITING"}
); }; const FeedStatusBar = () => { const state = useTerminal(); const feeds = [ { label: "Opt", feed: state.options }, { label: "Eq", feed: state.equities }, { label: "Flow", feed: state.flow }, { label: "Alert", feed: state.alerts }, { label: "Rule", feed: state.classifierHits }, { label: "Dark", feed: state.inferredDark } ]; return (
{feeds.map(({ label, feed }) => (
{label}
))}
); }; const OverviewBrief = () => { const state = useTerminal(); return (
Options {formatFlowMetric(state.filteredOptions.length)}
Equities {formatFlowMetric(state.filteredEquities.length)}
Flow {formatFlowMetric(state.filteredFlow.length)}
Alerts {formatFlowMetric(state.filteredAlerts.length)}
Rules {formatFlowMetric(state.filteredClassifierHits.length)}
Dark {formatFlowMetric(state.filteredInferredDark.length)}
); }; type OptionsPaneProps = { limit?: number; }; const OptionsPane = ({ limit }: OptionsPaneProps) => { const state = useTerminal(); const items = limit ? state.filteredOptions.slice(0, limit) : state.filteredOptions; return ( } actions={ } >
{items.length === 0 ? (
{state.tickerSet.size > 0 ? "No option prints match the current filter." : state.mode === "live" ? "No option prints yet. Start ingest-options." : "Replay queue empty. Ensure ClickHouse has data."}
) : ( items.map((print) => { const contractId = normalizeContractId(print.option_contract_id); const quote = state.nbboMap.get(contractId); const nbboAge = quote ? Math.abs(print.ts - quote.ts) : null; const nbboStale = nbboAge !== null && nbboAge > NBBO_MAX_AGE_MS_SAFE; const nbboMid = quote ? (quote.bid + quote.ask) / 2 : null; const nbboSide = classifyNbboSide(print.price, quote); const notional = print.price * print.size * 100; return (
{formatContractLabel(contractId)}
${formatPrice(print.price)} {formatSize(print.size)}x {print.exchange} Notional ${formatUsd(notional)} {print.conditions?.length ? {print.conditions.join(", ")} : null}
{quote ? (
Bid ${formatPrice(quote.bid)} Ask ${formatPrice(quote.ask)} Mid ${formatPrice(nbboMid ?? 0)} {Math.round(nbboAge ?? 0)}ms {nbboSide ? ( {nbboSide} A Ask AA Above Ask B Bid BB Below Bid ) : null} {nbboStale ? Stale : null}
) : (
NBBO missing
)}
{formatTime(print.ts)}
); }) )}
); }; type EquitiesPaneProps = { limit?: number; }; const EquitiesPane = ({ limit }: EquitiesPaneProps) => { const state = useTerminal(); const items = limit ? state.filteredEquities.slice(0, limit) : state.filteredEquities; return ( } actions={ } >
{items.length === 0 ? (
{state.tickerSet.size > 0 ? "No equity prints match the current filter." : state.mode === "live" ? "No equity prints yet. Start ingest-equities." : "Replay queue empty. Ensure ClickHouse has data."}
) : ( items.map((print) => (
{print.underlying_id}
${formatPrice(print.price)} {formatSize(print.size)}x {print.exchange} {print.offExchangeFlag ? ( Off-Ex ) : ( Lit )}
{formatTime(print.ts)}
)) )}
); }; type FlowPaneProps = { limit?: number; title?: string; }; const FlowPane = ({ limit, title = "Flow" }: FlowPaneProps) => { const state = useTerminal(); const items = limit ? state.filteredFlow.slice(0, limit) : state.filteredFlow; return ( } actions={ } >
{items.length === 0 ? (
{state.tickerSet.size > 0 ? "No flow packets match the current filter." : state.mode === "live" ? "No flow packets yet. Start compute." : "Replay queue empty. Ensure ClickHouse has data."}
) : ( items.map((packet) => { const features = packet.features ?? {}; const contract = String(features.option_contract_id ?? packet.id ?? "unknown"); const count = parseNumber(features.count, packet.members.length); const totalSize = parseNumber(features.total_size, 0); const totalNotional = parseNumber(features.total_notional, Number.NaN); const notional = Number.isFinite(totalNotional) ? totalNotional : parseNumber(features.total_premium, 0) * 100; const startTs = parseNumber(features.start_ts, packet.source_ts); const endTs = parseNumber(features.end_ts, startTs); const windowMs = parseNumber(features.window_ms, 0); const structureType = typeof features.structure_type === "string" ? features.structure_type : ""; const structureLegs = parseNumber(features.structure_legs, 0); const structureRights = typeof features.structure_rights === "string" ? features.structure_rights : ""; const structureStrikes = parseNumber(features.structure_strikes, 0); const nbboBid = parseNumber(features.nbbo_bid, Number.NaN); const nbboAsk = parseNumber(features.nbbo_ask, Number.NaN); const nbboMid = parseNumber(features.nbbo_mid, Number.NaN); const nbboSpread = parseNumber(features.nbbo_spread, Number.NaN); const aggressiveBuyRatio = parseNumber(features.nbbo_aggressive_buy_ratio, Number.NaN); const aggressiveSellRatio = parseNumber( features.nbbo_aggressive_sell_ratio, Number.NaN ); const aggressiveCoverage = parseNumber(features.nbbo_coverage_ratio, Number.NaN); const insideRatio = parseNumber(features.nbbo_inside_ratio, Number.NaN); const nbboAge = parseNumber(packet.join_quality.nbbo_age_ms, Number.NaN); const nbboStale = parseNumber(packet.join_quality.nbbo_stale, 0) > 0; const nbboMissing = parseNumber(packet.join_quality.nbbo_missing, 0) > 0; return (
{contract}
{formatFlowMetric(count)} prints {formatFlowMetric(totalSize)} size Notional ${formatUsd(notional)} {windowMs > 0 ? {formatFlowMetric(windowMs, "ms")} : null} {structureType ? ( {structureType.replace(/_/g, " ")} {structureRights ? ` ${structureRights}` : ""} {structureLegs > 0 ? ` ${structureLegs}L` : ""} {structureStrikes > 0 ? ` ${structureStrikes}K` : ""} ) : null} {Number.isFinite(aggressiveCoverage) && aggressiveCoverage > 0 ? ( Agg {formatPct(aggressiveBuyRatio)} / {formatPct(aggressiveSellRatio)} {Number.isFinite(insideRatio) && insideRatio > 0 ? ` · In ${formatPct(insideRatio)}` : ""} {` · ${formatPct(aggressiveCoverage)} cov`} ) : null} {Number.isFinite(nbboBid) && Number.isFinite(nbboAsk) ? ( NBBO ${formatPrice(nbboBid)} x ${formatPrice(nbboAsk)} ) : null} {Number.isFinite(nbboMid) ? Mid ${formatPrice(nbboMid)} : null} {Number.isFinite(nbboSpread) ? ( Spread ${formatPrice(nbboSpread)} ) : null} {Number.isFinite(nbboAge) ? {Math.round(nbboAge)}ms : null} {nbboStale ? NBBO stale : null} {nbboMissing ? NBBO missing : null}
{formatTime(startTs)} → {formatTime(endTs)}
); }) )}
); }; type AlertsPaneProps = { limit?: number; withStrip?: boolean; }; const AlertsPane = ({ limit, withStrip = false }: AlertsPaneProps) => { const state = useTerminal(); const items = limit ? state.filteredAlerts.slice(0, limit) : state.filteredAlerts; return ( } actions={ } > {withStrip ? : null}
{items.length === 0 ? (
{state.tickerSet.size > 0 ? "No alerts match the current filter." : state.mode === "live" ? "No alerts yet. Start compute." : "Replay queue empty. Ensure ClickHouse has data."}
) : ( items.map((alert) => { const primary = alert.hits[0]; const direction = primary ? normalizeDirection(primary.direction) : "neutral"; return ( ); }) )}
); }; type ClassifierPaneProps = { limit?: number; }; const ClassifierPane = ({ limit }: ClassifierPaneProps) => { const state = useTerminal(); const items = limit ? state.filteredClassifierHits.slice(0, limit) : state.filteredClassifierHits; return ( } actions={ } >
{items.length === 0 ? (
{state.tickerSet.size > 0 ? "No classifier hits match the current filter." : state.mode === "live" ? "No classifier hits yet. Start compute." : "Replay queue empty. Ensure ClickHouse has data."}
) : ( items.map((hit) => { const direction = normalizeDirection(hit.direction); return ( ); }) )}
); }; type DarkPaneProps = { limit?: number; }; const DarkPane = ({ limit }: DarkPaneProps) => { const state = useTerminal(); const items = limit ? state.filteredInferredDark.slice(0, limit) : state.filteredInferredDark; return ( } actions={ } >
{items.length === 0 ? (
{state.tickerSet.size > 0 ? "No inferred dark events match the current filter." : state.mode === "live" ? "No inferred dark events yet. Start compute." : "Replay queue empty. Ensure ClickHouse has data."}
) : ( items.map((event) => { const underlying = inferDarkUnderlying(event, state.equityPrintMap, state.equityJoinMap); const evidenceCount = event.evidence_refs.length; return ( ); }) )}
); }; type ChartPaneProps = { title?: string; }; const ChartPane = ({ title = "Chart" }: ChartPaneProps) => { const state = useTerminal(); return (
{CANDLE_INTERVALS.map((interval) => ( ))}
{state.chartTicker}
} > ); }; const FocusPane = () => { const state = useTerminal(); const hits = state.chartClassifierHits.slice(-10).reverse(); const dark = state.chartInferredDark.slice(-10).reverse(); return (
Ticker
{state.chartTicker}
Rules
{hits.length === 0 ? (
No rule hits for {state.chartTicker}.
) : (
{hits.map((hit) => ( ))}
)}
Dark
{dark.length === 0 ? (
No inferred dark events for {state.chartTicker}.
) : (
{dark.map((event) => ( ))}
)}
); }; const ReplayConsole = () => { const state = useTerminal(); const replayActive = state.mode === "replay"; return ( {replayActive ? "Switch Live" : "Switch Replay"} } >
Mode {replayActive ? "Replay" : "Live"}
Source {state.replaySource ? state.replaySource.toUpperCase() : "Auto"}
Replay Clock {state.options.replayTime ? formatTime(state.options.replayTime) : "—"}
Packets {formatFlowMetric(state.filteredFlow.length)}
); }; export function TerminalAppShell({ children }: { children: ReactNode }) { const state = useTerminalState(); const pathname = usePathname(); return (
{children}
{state.selectedAlert ? ( state.setSelectedAlert(null)} /> ) : null} {state.selectedClassifierHit ? ( state.setSelectedClassifierHit(null)} /> ) : null} {state.selectedDarkEvent ? ( state.setSelectedDarkEvent(null)} /> ) : null}
); } export function OverviewRoute() { return (
); } export function TapeRoute() { return (
); } export function SignalsRoute() { return (
); } export function ChartsRoute() { return (
); } export function ReplayRoute() { return (
); }