"use client"; import { formatBps, formatCompact, formatDuration, formatQuoteValue } from "@/lib/format"; import { useLiveQuote } from "@/lib/stream"; import type { Instrument, Quote } from "@/lib/types"; /** Key stats grid; bid/ask/volume follow the live stream, session values from the canonical quote. */ export function InstrumentStats({ instrument: i, quote: q }: { instrument: Instrument; quote: Quote | null }) { const live = useLiveQuote(i.id); const fresh = live && q && live.received >= Date.parse(q.updated_at); const f = (v: number | null | undefined) => formatQuoteValue(v, i.asset_class, q?.currency); const bid = fresh ? live!.bid : q?.bid; const ask = fresh ? live!.ask : q?.ask; const spreadBps = bid != null && ask != null && ask > 0 ? ((ask - bid) / ((ask + bid) / 2)) * 10_000 : null; const items: Array<[string, string]> = [ ["Open", f(q?.open)], ["High", f(q?.high)], ["Low", f(q?.low)], ["Previous close", f(q?.previous_close)], ["Bid", f(bid)], ["Ask", f(ask)], ["Spread", formatBps(spreadBps)], ["Volume", formatCompact(fresh ? live!.volume : q?.volume)], ["Session high", f(q?.session_high)], ["Session low", f(q?.session_low)], ["Source dispersion", formatBps(q?.dispersion_bps)], ["Newest observation", q?.freshness_ms == null ? "—" : `${formatDuration(q.freshness_ms)} old at compute`], ]; return (
{items.map(([k, v]) => (
{k}
{v}
))}
); }