UNPKG

kestrel.markets

Version:

A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.

1,040 lines (1,025 loc) 31.9 kB
import { black76, impliedVol, intrinsic } from "./bin-df1tn094.js"; import { assertNever } from "./bin-2ywrx58g.js"; // src/series/types.ts var UNKNOWN = Symbol("kestrel.UNKNOWN"); function isUnknown(x) { return x === UNKNOWN; } var MS = { s: 1000, m: 60000, h: 3600000, d: 86400000, w: 604800000, mo: 2592000000, q: 7776000000, y: 31536000000 }; function durationMs(value, unit) { return value * MS[unit]; } // src/series/windows.ts var DEFAULTS = { baselineCapacity: 512, minBaselineSamples: 30, maxSamples: 200000 }; class BaselineRing { cap; buf = []; head = 0; constructor(cap) { this.cap = cap; } push(v) { if (this.buf.length < this.cap) { this.buf.push(v); } else { this.buf[this.head] = v; this.head = (this.head + 1) % this.cap; } } get count() { return this.buf.length; } percentile(n) { const sorted = [...this.buf].sort((a, b) => a - b); const N = sorted.length; const rank = Math.min(N, Math.max(1, Math.ceil(n / 100 * N))); return sorted[rank - 1]; } sigmaBand(k) { const N = this.buf.length; let sum = 0; for (const v of this.buf) sum += v; const mean = sum / N; let ss = 0; for (const v of this.buf) ss += (v - mean) * (v - mean); const sd = Math.sqrt(ss / N); return mean + k * sd; } } function comboKey(metric, windowMs) { return `${metric}|${windowMs}`; } class WindowEngine { samples = []; baselines = new Map; cfg; constructor(cfg = {}) { this.cfg = { baselineCapacity: cfg.baselineCapacity ?? DEFAULTS.baselineCapacity, minBaselineSamples: cfg.minBaselineSamples ?? DEFAULTS.minBaselineSamples, maxSamples: cfg.maxSamples ?? DEFAULTS.maxSamples }; } track(metric, value, unit) { const key = comboKey(metric, durationMs(value, unit)); if (!this.baselines.has(key)) this.baselines.set(key, new BaselineRing(this.cfg.baselineCapacity)); } pushSpot(ts, px) { this.samples.push({ ts, px }); if (this.samples.length > this.cfg.maxSamples) this.samples.shift(); for (const [key, ring] of this.baselines) { const [metric, windowMsStr] = key.split("|"); const wv = this.compute(metric, Number(windowMsStr), ts); if (wv !== UNKNOWN) ring.push(wv.dollars); } } value(metric, value, unit, now) { return this.compute(metric, durationMs(value, unit), now); } baseline(metric, value, unit, stat) { const ring = this.baselines.get(comboKey(metric, durationMs(value, unit))); if (ring === undefined || ring.count < this.cfg.minBaselineSamples) return UNKNOWN; return stat.kind === "p" ? ring.percentile(stat.n) : ring.sigmaBand(stat.n); } get sampleCount() { return this.samples.length; } reset() { this.samples.length = 0; this.baselines.clear(); } compute(metric, windowMs, now) { const cutoff = now - windowMs; const startPx = this.priceAtOrBefore(cutoff); if (startPx === undefined) return UNKNOWN; const nowPx = this.latestPx(); if (nowPx === undefined) return UNKNOWN; if (metric === "range") { let hi = -Infinity; let lo = Infinity; for (let i = this.samples.length - 1;i >= 0; i--) { const s = this.samples[i]; if (s.ts < cutoff) break; if (s.px > hi) hi = s.px; if (s.px < lo) lo = s.px; } const dollars = hi - lo; return { dollars, pct: startPx !== 0 ? dollars / Math.abs(startPx) : 0 }; } const delta = nowPx - startPx; const signed = metric === "velocity" ? delta : Math.abs(delta); return { dollars: signed, pct: startPx !== 0 ? signed / Math.abs(startPx) : 0 }; } priceAtOrBefore(cutoff) { for (let i = this.samples.length - 1;i >= 0; i--) { const s = this.samples[i]; if (s.ts <= cutoff) return s.px; } return; } latestPx() { const last = this.samples[this.samples.length - 1]; return last?.px; } } // src/series/state.ts class CanonicalState { instrument; windows; orMs; priorCloseVal; spotVal; spotTs; hodVal; lodVal; hodExclVal; lodExclVal; orHiExclVal; orLoExclVal; orAnchorTs; orHi; orLo; vwapNum = 0; vwapDen = 0; lastPx; lastPxTs; phaseVal; constructor(cfg) { this.instrument = cfg.instrument; this.priorCloseVal = cfg.priorClose; this.orMs = durationMs(cfg.openingRangeMinutes ?? 5, "m"); this.windows = new WindowEngine(cfg.windows ?? {}); } applyEvent(ev) { if (ev.stream === "TICK") { if (ev.type === "SPOT") { if (ev.instrument === this.instrument) this.onSpot(ev.ts, ev.px); } else if (ev.type === "HEARTBEAT") { if (ev.phase !== undefined) this.phaseVal = ev.phase; } } } onSpot(ts, px) { this.hodExclVal = this.hodVal?.value; this.lodExclVal = this.lodVal?.value; this.orHiExclVal = this.orHi; this.orLoExclVal = this.orLo; if (this.lastPx !== undefined && this.lastPxTs !== undefined) { const dt = ts - this.lastPxTs; if (dt > 0) { this.vwapNum += this.lastPx * dt; this.vwapDen += dt; } } this.lastPx = px; this.lastPxTs = ts; this.spotVal = px; this.spotTs = ts; if (this.hodVal === undefined || px > this.hodVal.value) this.hodVal = { value: px, ts }; if (this.lodVal === undefined || px < this.lodVal.value) this.lodVal = { value: px, ts }; if (this.orAnchorTs === undefined) this.orAnchorTs = ts; if (ts <= this.orAnchorTs + this.orMs) { this.orHi = this.orHi === undefined ? px : Math.max(this.orHi, px); this.orLo = this.orLo === undefined ? px : Math.min(this.orLo, px); } this.windows.pushSpot(ts, px); } get spot() { return this.spotVal ?? UNKNOWN; } get spotStamp() { return this.spotVal !== undefined && this.spotTs !== undefined ? { value: this.spotVal, ts: this.spotTs } : UNKNOWN; } get hod() { return this.hodVal?.value ?? UNKNOWN; } get hodStamp() { return this.hodVal ?? UNKNOWN; } get lod() { return this.lodVal?.value ?? UNKNOWN; } get lodStamp() { return this.lodVal ?? UNKNOWN; } get hodTrigger() { return this.hodExclVal ?? UNKNOWN; } get lodTrigger() { return this.lodExclVal ?? UNKNOWN; } get orHighTrigger() { return this.orHiExclVal ?? UNKNOWN; } get orLowTrigger() { return this.orLoExclVal ?? UNKNOWN; } get priorClose() { return this.priorCloseVal ?? UNKNOWN; } get orHigh() { return this.orHi ?? UNKNOWN; } get orLow() { return this.orLo ?? UNKNOWN; } get phase() { return this.phaseVal ?? UNKNOWN; } get vwap() { if (this.spotVal === undefined) return UNKNOWN; if (this.vwapDen === 0) return this.spotVal; return this.vwapNum / this.vwapDen; } reset() { this.spotVal = undefined; this.spotTs = undefined; this.hodVal = undefined; this.lodVal = undefined; this.hodExclVal = undefined; this.lodExclVal = undefined; this.orHiExclVal = undefined; this.orLoExclVal = undefined; this.orAnchorTs = undefined; this.orHi = undefined; this.orLo = undefined; this.vwapNum = 0; this.vwapDen = 0; this.lastPx = undefined; this.lastPxTs = undefined; this.phaseVal = undefined; this.windows.reset(); } } // src/series/registry.ts var OBSERVABILITY_ORDER = ["tick", "second", "minute", "session"]; function observabilityRank(c) { return OBSERVABILITY_ORDER.indexOf(c); } function isRung(v) { return OBSERVABILITY_ORDER.includes(v); } function isFinerThanFidelity(cls, fidelity) { if (!isRung(cls) || !isRung(fidelity)) return true; return observabilityRank(cls) < observabilityRank(fidelity); } function marketScalarReg(name, observabilityClass) { return { name, kind: "market", scope: { space: "signal" }, windowing: "scalar", source: { kind: "platform" }, unit: "price", market: { via: "scalar", scalar: name }, observabilityClass }; } function marketWindowReg(name, metric, pct) { return { name, kind: "market", scope: { space: "signal" }, windowing: "windowed", source: { kind: "platform" }, unit: pct ? "fraction" : "price_delta", market: { via: "window", metric, pct }, observabilityClass: "tick" }; } var PLATFORM_SERIES = [ marketScalarReg("spot", "tick"), marketScalarReg("vwap", "tick"), marketScalarReg("hod", "minute"), marketScalarReg("lod", "minute"), marketScalarReg("prior_close", "session"), marketScalarReg("or_high", "minute"), marketScalarReg("or_low", "minute"), marketWindowReg("velocity", "velocity", false), marketWindowReg("velocity_pct", "velocity", true), marketWindowReg("move", "move", false), marketWindowReg("move_pct", "move", true), marketWindowReg("range", "range", false), marketWindowReg("range_pct", "range", true) ]; function headName(ref) { return ref.segments[0]?.name; } function orgScopePath(segments) { return segments.map((s) => s.name); } class SeriesRegistry { records = new Map; constructor(platform = PLATFORM_SERIES) { for (const r of platform) this.records.set(r.name, r); } lookup(name) { return this.records.get(name); } classify(ref) { const name = headName(ref); if (name === undefined) return UNKNOWN; return this.records.get(name) ?? UNKNOWN; } admitAtFidelity(ref, fidelity) { const rec = this.classify(ref); const label = headName(ref) ?? "(empty path)"; if (!isRung(fidelity)) { return { admit: false, reason: `unknown grading fidelity "${fidelity}" — not a recognized observability rung (${OBSERVABILITY_ORDER.join(", ")}); series "${label}" cannot be graded on an untrusted fidelity (fail-closed, RUNTIME §28)` }; } if (isUnknown(rec)) { return { admit: false, reason: `series "${label}" has no phonebook record — an unknown series cannot be graded at ${fidelity} fidelity (fail-closed, RUNTIME §28)` }; } if (rec.kind === "org") return { admit: true }; const cls = rec.observabilityClass; if (cls === undefined) { return { admit: false, reason: `market series "${rec.name}" carries no observability class — cannot confirm it is observable at ${fidelity} fidelity (fail-closed)` }; } if (isFinerThanFidelity(cls, fidelity)) { return { admit: false, reason: `series "${rec.name}" is ${cls}-observable but the session grades at ${fidelity} fidelity — a finer-than-fidelity series cannot be graded honestly; de-arm (fidelity-1, RUNTIME §28)` }; } return { admit: true }; } noteOrgWrite(segments, by) { const head = segments[0]; if (head === undefined) return UNKNOWN; const existing = this.records.get(head.name); if (existing !== undefined) return existing; const reg = { name: head.name, kind: "org", scope: { space: "org", path: orgScopePath(segments) }, windowing: "scalar", source: { kind: "authored", by } }; this.records.set(head.name, reg); return reg; } dump() { return [...this.records.values()].map((r) => ({ ...r })).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0); } } var defaultSeriesRegistry = new SeriesRegistry; // src/series/provider.ts function marketBinding(registry, name) { const reg = registry.lookup(name); return reg?.kind === "market" ? reg.market : undefined; } function isBareSegment(ref) { return ref.segments.length === 1 && ref.segments[0]?.selector === undefined; } class CanonicalSeriesProvider { state; org; hooks; registry; constructor(state, org, hooks = {}, registry = defaultSeriesRegistry) { this.state = state; this.org = org; this.hooks = hooks; this.registry = registry; } timeOfDayMinutes(now) { return this.hooks.timeOfDayMinutes ? this.hooks.timeOfDayMinutes(now) : UNKNOWN; } fillEvent(ev) { return this.hooks.fillEvent ? this.hooks.fillEvent(ev) : UNKNOWN; } temporalEnvelope(env, inner, now) { return this.hooks.temporalEnvelope ? this.hooks.temporalEnvelope(env, inner, now) : UNKNOWN; } resolve(ref, now) { if (ref.segments.length === 0) return UNKNOWN; const head = ref.segments[0]; if (head === undefined) return UNKNOWN; if (ref.window !== undefined) { if (!isBareSegment(ref)) return UNKNOWN; const binding = marketBinding(this.registry, head.name); if (binding?.via !== "window") return UNKNOWN; const wv = this.state.windows.value(binding.metric, ref.window.value, ref.window.unit, now); if (wv === UNKNOWN) return UNKNOWN; return binding.pct ? wv.pct : wv.dollars; } if (isBareSegment(ref)) { const binding = marketBinding(this.registry, head.name); if (binding?.via === "scalar") return this.marketScalar(binding.scalar); } return this.org.resolve(ref.segments); } quantify(ref, _now) { return this.org.quantify?.(ref.segments) ?? UNKNOWN; } baseline(ref, stat) { if (ref.window === undefined || !isBareSegment(ref)) return UNKNOWN; const head = ref.segments[0]; if (head === undefined) return UNKNOWN; const binding = marketBinding(this.registry, head.name); if (binding?.via !== "window") return UNKNOWN; const bstat = stat.stat === "p" ? { kind: "p", n: stat.n } : { kind: "sigma", n: stat.n }; return this.state.windows.baseline(binding.metric, ref.window.value, ref.window.unit, bstat); } phase() { return this.state.phase; } marketScalar(name) { switch (name) { case "spot": return this.state.spot; case "vwap": return this.state.vwap; case "hod": return this.state.hodTrigger; case "lod": return this.state.lodTrigger; case "prior_close": return this.state.priorClose; case "or_high": return this.state.orHighTrigger; case "or_low": return this.state.orLowTrigger; default: return UNKNOWN; } } } function pathKey(segments) { return segments.map((s) => { if (s.selector === undefined) return s.name; const inner = s.selector.kind === "sel-quant" ? s.selector.q : s.selector.name; return `${s.name}(${inner})`; }).join("."); } class FakeOrgFacts { facts; constructor(facts = new Map) { this.facts = facts; } static of(entries) { return new FakeOrgFacts(new Map(Object.entries(entries))); } resolve(segments) { return this.facts.get(pathKey(segments)) ?? UNKNOWN; } } // src/series/trigger.ts var ROOT = "$"; function quantOf(op) { if (op.kind !== "series" || op.window !== undefined) return null; const head = op.segments[0]; return head?.selector?.kind === "sel-quant" ? head.selector.q : null; } class TriggerEvaluator { mem = new Map; evaluate(trigger, provider, now) { return this.evalNode(trigger, ROOT, provider, now); } reset() { this.mem.clear(); } dumpState() { return [...this.mem.entries()].map(([path, m]) => [path, { ...m }]).sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0); } evalNode(node, path, provider, now) { switch (node.kind) { case "cmp": { if (quantOf(node.left) !== null || quantOf(node.right) !== null) { return this.evalQuantifiedCompare(node.op, node.left, node.right, provider, now); } const { lv, rv } = this.resolvePair(node.left, node.right, provider, now); const lU = isUnknown(lv); const rU = isUnknown(rv); if (lU && rU) return UNKNOWN; if (lU || rU) { if (node.op === "eq" || node.op === "ne") { const lFinal = lU ? this.literalOf(node.left) : lv; const rFinal = rU ? this.literalOf(node.right) : rv; if (!isUnknown(lFinal) && !isUnknown(rFinal)) { return node.op === "eq" ? lFinal === rFinal : lFinal !== rFinal; } } return UNKNOWN; } return this.compareResolved(node.op, lv, rv); } case "cross": { if (quantOf(node.left) !== null || quantOf(node.right) !== null) return UNKNOWN; const { lv, rv } = this.resolvePair(node.left, node.right, provider, now); if (isUnknown(lv) || isUnknown(rv)) return UNKNOWN; if (typeof lv !== "number" || typeof rv !== "number") return UNKNOWN; const mem = this.crossMem(path); const cur = lv > rv ? "above" : lv < rv ? "below" : "equal"; if (node.band !== undefined && !mem.armed) { const width = node.band.value; const cleared = node.dir === "above" ? lv <= rv - width : lv >= rv + width; if (cleared) mem.armed = true; } const side = cur === "equal" ? node.touch === true ? node.dir : null : cur; if (side === null) return false; const prev = mem.prevSide; mem.prevSide = side; if (prev === null) return false; if (!(prev !== side && side === node.dir)) return false; if (node.band !== undefined) { if (!mem.armed) return false; mem.armed = false; } return true; } case "break-hold": { const inner = this.evalNode(node.inner, `${path}.0`, provider, now); const mem = this.heldMem(path); const dur = durationMs(node.dur.value, node.dur.unit); if (inner === true) { if (mem.since === null) mem.since = now; return now - mem.since >= dur; } mem.since = null; return inner === UNKNOWN ? UNKNOWN : false; } case "within": { const inner = this.evalNode(node.inner, `${path}.0`, provider, now); const mem = this.withinMem(path); const dur = durationMs(node.dur.value, node.dur.unit); if (inner === true) mem.lastTrue = now; if (mem.lastTrue !== null) return now - mem.lastTrue <= dur; return inner === UNKNOWN ? UNKNOWN : false; } case "until": case "at": { const inner = this.evalNode(node.inner, `${path}.0`, provider, now); return provider.temporalEnvelope ? provider.temporalEnvelope(node, inner, now) : UNKNOWN; } case "held-stop": { return UNKNOWN; } case "clock-stop": { return UNKNOWN; } case "nth": { const inner = this.evalNode(node.event, `${path}.0`, provider, now); const mem = this.nthMem(path); if (inner === true && !mem.prevInner) mem.count += 1; mem.prevInner = inner === true; if (mem.count >= node.ordinal) return true; return inner === UNKNOWN ? UNKNOWN : false; } case "phase": { const p = provider.phase(); if (isUnknown(p)) return UNKNOWN; return p === node.phase; } case "time-window": { if (provider.timeOfDayMinutes === undefined) return UNKNOWN; const m = provider.timeOfDayMinutes(now); if (isUnknown(m)) return UNKNOWN; const from = node.from ? node.from.hour * 60 + node.from.minute : null; const to = node.to ? node.to.hour * 60 + node.to.minute : null; const afterFrom = from === null || m >= from; const beforeTo = to === null || m < to; return afterFrom && beforeTo; } case "event": { return provider.structural ? provider.structural(node, now) : UNKNOWN; } case "fill": { return provider.fillEvent ? provider.fillEvent(node) : UNKNOWN; } case "and": { const vals = node.terms.map((t, i) => this.evalNode(t, `${path}.${i}`, provider, now)); let anyUnknown = false; for (const v of vals) { if (v === false) return false; if (isUnknown(v)) anyUnknown = true; } return anyUnknown ? UNKNOWN : true; } case "or": { const vals = node.terms.map((t, i) => this.evalNode(t, `${path}.${i}`, provider, now)); let anyUnknown = false; for (const v of vals) { if (v === true) return true; if (isUnknown(v)) anyUnknown = true; } return anyUnknown ? UNKNOWN : false; } case "not": { const v = this.evalNode(node.term, `${path}.0`, provider, now); if (isUnknown(v)) return UNKNOWN; return !v; } default: return assertNever(node, "series/trigger evalNode"); } } resolvePair(left, right, provider, now) { if (right.kind === "baseline") { if (left.kind !== "series") return { lv: UNKNOWN, rv: UNKNOWN }; return { lv: provider.resolve(left, now), rv: provider.baseline(left, right) }; } if (left.kind === "baseline") { if (right.kind !== "series") return { lv: UNKNOWN, rv: UNKNOWN }; return { lv: provider.baseline(right, left), rv: provider.resolve(right, now) }; } return { lv: this.operandValue(left, provider, now), rv: this.operandValue(right, provider, now) }; } operandValue(op, provider, now) { switch (op.kind) { case "series": return provider.resolve(op, now); case "quantity": return op.value; case "baseline": return UNKNOWN; default: return assertNever(op, "series/trigger operandValue"); } } literalOf(op) { if (op.kind !== "series" || op.window !== undefined || op.segments.length !== 1) return UNKNOWN; const seg0 = op.segments[0]; if (seg0 === undefined || seg0.selector !== undefined) return UNKNOWN; return seg0.name; } evalQuantifiedCompare(op, left, right, provider, now) { const lq = quantOf(left); const rq = quantOf(right); if (lq !== null && rq !== null) return UNKNOWN; const quant = lq ?? rq; const qRef = lq !== null ? left : right; const other = lq !== null ? right : left; const values = provider.quantify?.(qRef, now) ?? UNKNOWN; if (isUnknown(values)) return UNKNOWN; const threshold = this.operandValue(other, provider, now); if (isUnknown(threshold)) return UNKNOWN; let anyTrue = false; let anyFalse = false; let anyUnknown = false; for (const v of values) { const r = lq !== null ? this.compareResolved(op, v, threshold) : this.compareResolved(op, threshold, v); if (r === true) anyTrue = true; else if (isUnknown(r)) anyUnknown = true; else anyFalse = true; } if (quant === "any") return anyTrue ? true : anyUnknown ? UNKNOWN : false; return anyFalse ? false : anyUnknown ? UNKNOWN : true; } compareResolved(op, lv, rv) { switch (op) { case "eq": return lv === rv; case "ne": return lv !== rv; case "gt": case "ge": case "lt": case "le": { if (typeof lv !== "number" || typeof rv !== "number") return UNKNOWN; return op === "gt" ? lv > rv : op === "ge" ? lv >= rv : op === "lt" ? lv < rv : lv <= rv; } default: return assertNever(op, "series/trigger compareResolved"); } } crossMem(path) { const m = this.mem.get(path); if (m !== undefined) { if (m.kind !== "cross") throw new Error(`series/trigger: memory kind clash at ${path}`); return m; } const fresh = { kind: "cross", prevSide: null, armed: true }; this.mem.set(path, fresh); return fresh; } heldMem(path) { const m = this.mem.get(path); if (m !== undefined) { if (m.kind !== "held") throw new Error(`series/trigger: memory kind clash at ${path}`); return m; } const fresh = { kind: "held", since: null }; this.mem.set(path, fresh); return fresh; } withinMem(path) { const m = this.mem.get(path); if (m !== undefined) { if (m.kind !== "within") throw new Error(`series/trigger: memory kind clash at ${path}`); return m; } const fresh = { kind: "within", lastTrue: null }; this.mem.set(path, fresh); return fresh; } nthMem(path) { const m = this.mem.get(path); if (m !== undefined) { if (m.kind !== "nth") throw new Error(`series/trigger: memory kind clash at ${path}`); return m; } const fresh = { kind: "nth", count: 0, prevInner: false }; this.mem.set(path, fresh); return fresh; } } // src/fair/spot.ts var EXEC_FAIR_QUOTE_MODEL = "exec-fair-quote-v1"; function executionFairSpot(input) { const { bid, ask, asof } = input; if (bid === null || ask === null) return null; if (!Number.isFinite(bid) || !Number.isFinite(ask)) return null; if (ask < bid) return null; const value = (bid + ask) / 2; return { value, receipt: { model: EXEC_FAIR_QUOTE_MODEL, bid, ask, ...asof !== undefined ? { asof } : {} } }; } // src/fair/surface.ts var PARITY_TOTAL_VOL_TOLERANCE = 0.001; function realSides(q, forward) { let bid = q.bid; let ask = q.ask; if (bid !== null && bid < intrinsic(forward, q.strike, q.right)) bid = null; const ceiling = q.right === "C" ? forward : q.strike; if (ask !== null && ask >= ceiling) ask = null; if (bid !== null && ask !== null && bid >= ask) { bid = null; ask = null; } return { bid, ask }; } function liquidMid(q, forward) { const { bid, ask } = realSides(q, forward); if (bid === null || ask === null) return null; const mid = 0.5 * (bid + ask); return mid > 0 ? mid : null; } var FORWARD_NO_ARB_BAND = 0.1; function impliedForward(quotes, spot) { const degraded = (reason) => ({ forward: spot, source: "spot", degraded: reason }); if (!Number.isFinite(spot) || spot <= 0) return degraded("underlying spot unusable — no ATM anchor to read parity at"); const calls = new Map; const puts = new Map; for (const q of quotes) { const mid = liquidMid(q, spot); if (mid === null) continue; (q.right === "C" ? calls : puts).set(q.strike, mid); } let bestStrike = null; let bestDist = Infinity; for (const [strike, _cmid] of calls) { if (!puts.has(strike)) continue; const dist = Math.abs(strike - spot); if (dist < bestDist || dist === bestDist && bestStrike !== null && strike < bestStrike) { bestDist = dist; bestStrike = strike; } } if (bestStrike === null) { return degraded("no strike carries a liquid two-sided CALL and PUT — put-call parity underivable"); } const cmid = calls.get(bestStrike); const pmid = puts.get(bestStrike); if (cmid === undefined || pmid === undefined) return degraded("parity pair vanished — refusing to guess"); const forward = bestStrike + (cmid - pmid); if (!Number.isFinite(forward) || forward <= 0) { return degraded(`parity-implied forward is not a price (${forward}) — refusing to price on it`); } if (Math.abs(forward / spot - 1) > FORWARD_NO_ARB_BAND) { return degraded(`parity-implied forward ${forward} is >${FORWARD_NO_ARB_BAND * 100}% from spot ${spot} — a broken quote, not a forward`); } return { forward, source: "parity", strike: bestStrike, residual: forward - spot }; } function buildSurface(quotes, forward, tauYears) { const acc = new Map; for (const q of quotes) { const mid = liquidMid(q, forward); if (mid === null) continue; const iv = impliedVol({ forward, strike: q.strike, tauYears, price: mid, right: q.right }); if (iv === null) continue; const cur = acc.get(q.strike) ?? {}; if (q.right === "C") { if (cur.call === undefined) cur.call = iv; } else if (cur.put === undefined) { cur.put = iv; } acc.set(q.strike, cur); } const points = []; const sqrtTau = Math.sqrt(tauYears); for (const [strike, { call, put }] of acc) { if (call !== undefined && put !== undefined) { const dispersion = Math.abs(call - put); if (dispersion * sqrtTau > PARITY_TOTAL_VOL_TOLERANCE) continue; points.push({ strike, iv: 0.5 * (call + put), dispersion }); continue; } const only = call ?? put; if (only !== undefined) points.push({ strike, iv: only }); } points.sort((a, b) => a.strike - b.strike); return points; } function interpIv(points, strike) { const n = points.length; const first = points[0]; const last = points[n - 1]; if (first === undefined || last === undefined) return null; if (n === 1 || strike <= first.strike) return first.iv; if (strike >= last.strike) return last.iv; for (let i = 0;i < n - 1; i++) { const a = points[i]; const b = points[i + 1]; if (a === undefined || b === undefined) break; if (strike >= a.strike && strike <= b.strike) { const span = b.strike - a.strike; if (span <= 0) return a.iv; const w = (strike - a.strike) / span; return a.iv + w * (b.iv - a.iv); } } return last.iv; } // src/fair/index.ts var EXEC_FAIR_MODEL = "exec-fair-b76-v1"; function isFairRefusal(o) { return "refused" in o; } var BOOK_BOUND_EPS = 0.000000001; function executionFairOutcome(input) { const { underlyingSpot, strike, right, tauYears, liquidQuotes, asof } = input; if (!Number.isFinite(underlyingSpot) || underlyingSpot <= 0) { return { refused: "unbuildable", reason: "no-underlying" }; } const fwd = impliedForward(liquidQuotes, underlyingSpot); const surface = buildSurface(liquidQuotes, fwd.forward, tauYears); const nLiquid = surface.length; if (nLiquid === 0) return { refused: "unbuildable", reason: "no-liquid-strikes" }; const ivAtStrike = interpIv(surface, strike); if (ivAtStrike === null) return { refused: "unbuildable", reason: "no-surface-at-strike" }; let parityResidual; for (const p of surface) { if (p.dispersion === undefined) continue; if (parityResidual === undefined || p.dispersion > parityResidual) parityResidual = p.dispersion; } const modeled = black76({ forward: fwd.forward, strike, tauYears, sigma: ivAtStrike, right }); const value = Math.max(modeled, intrinsic(underlyingSpot, strike, right)); if (input.bookFresh === true) { const bid = input.legBid ?? null; const ask = input.legAsk ?? null; if (bid !== null && ask !== null && bid < ask) { if (value > ask + BOOK_BOUND_EPS) return { refused: "tainted", reason: "fair-above-fresh-ask" }; if (value < bid - BOOK_BOUND_EPS) return { refused: "tainted", reason: "fair-below-fresh-bid" }; } } return { value, receipt: { model: EXEC_FAIR_MODEL, nLiquid, ivAtStrike, forward: fwd.forward, forwardSource: fwd.source, ...fwd.strike !== undefined ? { forwardStrike: fwd.strike } : {}, ...fwd.residual !== undefined ? { forwardResidual: fwd.residual } : {}, ...fwd.degraded !== undefined ? { forwardDegraded: fwd.degraded } : {}, ...parityResidual !== undefined ? { parityResidual } : {}, ...asof !== undefined ? { asof } : {} } }; } function executionFair(input) { const outcome = executionFairOutcome(input); return isFairRefusal(outcome) ? null : outcome; } export { UNKNOWN, isUnknown, durationMs, CanonicalState, defaultSeriesRegistry, CanonicalSeriesProvider, pathKey, FakeOrgFacts, TriggerEvaluator, impliedForward, buildSurface, interpIv, EXEC_FAIR_QUOTE_MODEL, executionFairSpot, EXEC_FAIR_MODEL, isFairRefusal, executionFairOutcome, executionFair };