import type { EquityPrint, EquityPrintJoin, EquityQuote } from "@islandflow/types"; export type EquityQuoteJoin = { quote: EquityQuote | null; ageMs: number; stale: boolean; }; export type QuotePlacement = "AA" | "A" | "B" | "BB" | "MID" | "MISSING" | "STALE"; const roundTo = (value: number, digits = 4): number => { if (!Number.isFinite(value)) { return 0; } return Number(value.toFixed(digits)); }; export const classifyQuotePlacement = ( price: number, join: EquityQuoteJoin ): QuotePlacement => { if (!Number.isFinite(price)) { return "MISSING"; } if (!join.quote) { return "MISSING"; } if (join.stale) { return "STALE"; } const bid = join.quote.bid; const ask = join.quote.ask; if (!Number.isFinite(bid) || !Number.isFinite(ask) || ask <= 0) { return "MISSING"; } 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"; } return "MID"; }; export const buildEquityPrintJoin = ( print: EquityPrint, join: EquityQuoteJoin ): EquityPrintJoin => { const joinQuality: Record = {}; const placement = classifyQuotePlacement(print.price, join); const features: Record = { underlying_id: print.underlying_id, price: print.price, size: print.size, off_exchange_flag: print.offExchangeFlag, print_ts: print.ts, quote_placement: placement }; if (!join.quote) { joinQuality.quote_missing = 1; } else { joinQuality.quote_age_ms = join.ageMs; if (join.stale) { joinQuality.quote_stale = 1; } else { const bid = join.quote.bid; const ask = join.quote.ask; const mid = (bid + ask) / 2; const spread = ask - bid; features.quote_ts = join.quote.ts; features.quote_bid = bid; features.quote_ask = ask; features.quote_mid = roundTo(mid); features.quote_spread = roundTo(spread); } } const joinId = `equityjoin:${print.trace_id}`; return { source_ts: print.source_ts, ingest_ts: print.ingest_ts, seq: print.seq, trace_id: joinId, id: joinId, print_trace_id: print.trace_id, quote_trace_id: join.quote?.trace_id ?? "", features, join_quality: joinQuality }; };