import { median, round } from '@rareindex/shared'; /** * Market depth (§25) and RareIndex spread (§26): how the current asks of ONE variant sit around its * valuation. Pure arithmetic on observed asking prices — asks are not transactions and never feed RIV. */ export interface MarketDepth { asks: number; /** asks within ±5 % / ±10 % / ±20 % of RIV (cumulative bands) */ within5: number; within10: number; within20: number; belowRiv: number; aboveRiv: number; lowestAsk: number | null; medianAsk: number | null; /** (best ask − RIV) / RIV — negative = the cheapest ask is below the valuation; null without asks */ askRivSpread: number | null; } export function marketDepth(asks: Array, riv: number | null | undefined): MarketDepth | null { const a = asks.filter((x): x is number => typeof x === 'number' && Number.isFinite(x) && x > 0); if (riv === null || riv === undefined || !(riv > 0)) return null; const rel = a.map((x) => (x - riv) / riv); const within = (b: number) => rel.filter((r) => Math.abs(r) <= b).length; const lowest = a.length ? Math.min(...a) : null; return { asks: a.length, within5: within(0.05), within10: within(0.1), within20: within(0.2), belowRiv: rel.filter((r) => r < 0).length, aboveRiv: rel.filter((r) => r > 0).length, lowestAsk: lowest, medianAsk: median(a), askRivSpread: lowest === null ? null : round((lowest - riv) / riv, 4), }; }