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,222 lines (1,209 loc) 102 kB
import { sha256 } from "./bin-8pchjxn2.js"; import { MS_PER_YEAR, greeks, impliedVol } from "./bin-df1tn094.js"; // src/session/specs.ts function specFromMeta(si) { const isOption = si.assetClass === "option-underlier"; return { symbol: si.symbol, tickSize: 0.01, strikeStep: 1, multiplier: isOption ? 100 : 1, ...si.role !== undefined ? { role: si.role } : {} }; } function resolveSpecs(meta, override) { const specs = override !== undefined ? [...override] : meta.instruments.map(specFromMeta); if (specs.length === 0) throw new Error("session: no instruments (fail-closed, RUNTIME §8)"); return specs; } function requireMeta(events) { const meta = events.find((e) => e.stream === "META"); if (meta === undefined) { throw new Error("session: no META header in bus (a well-formed bus opens with exactly one META, RUNTIME §8)"); } return meta; } // src/frame/types.ts class KernelHonestyError extends Error { constructor(message) { super(message); this.name = "KernelHonestyError"; } } function isHonestConfidence(confidence) { return confidence >= 0 && confidence <= 1; } function makeField(spec) { const { attribution } = spec; if (attribution === "MODEL") { if (spec.source === undefined || spec.source === "") { throw new KernelHonestyError("MODEL Field carries no source watermark — a MODEL value MUST carry source(modelVer) + confidence"); } if (spec.modelVer === undefined || spec.modelVer === "") { throw new KernelHonestyError("MODEL Field carries no modelVer receipt — a MODEL value MUST carry source(modelVer) + confidence"); } if (spec.confidence === undefined) { throw new KernelHonestyError("MODEL Field carries no confidence — a MODEL value MUST carry source(modelVer) + confidence"); } if (!isHonestConfidence(spec.confidence)) { throw new KernelHonestyError(`MODEL Field carries an out-of-range confidence ${spec.confidence} — MODEL confidence MUST be in [0,1]`); } } else { if (spec.confidence !== undefined) { throw new KernelHonestyError(`${attribution} Field must carry no confidence`); } if (spec.modelVer !== undefined) { throw new KernelHonestyError(`${attribution} Field must carry no modelVer receipt`); } } const out = { value: spec.value, attribution }; if (spec.source !== undefined) out.source = spec.source; if (spec.modelVer !== undefined) out.modelVer = spec.modelVer; if (spec.confidence !== undefined) out.confidence = spec.confidence; if (spec.asOfSeq !== undefined) out.asOfSeq = spec.asOfSeq; return out; } function assertClaimHonest(claim) { const label = claim.claimType; const f = claim.field; if (f === undefined || f === null) { throw new KernelHonestyError(`claim ${label} carries no Field`); } if (f.attribution !== "MODEL") { throw new KernelHonestyError(`claim ${label} is attributed ${f.attribution}, not MODEL — a predictor/regime claim MUST be ` + "a MODEL Field with source + confidence (honesty guard)"); } if (f.source === undefined || f.source === "") { throw new KernelHonestyError(`MODEL claim ${label} carries no source watermark`); } if (f.modelVer === undefined || f.modelVer === "") { throw new KernelHonestyError(`MODEL claim ${label} carries no modelVer receipt`); } if (f.confidence === undefined) { throw new KernelHonestyError(`MODEL claim ${label} carries no confidence`); } if (!isHonestConfidence(f.confidence)) { throw new KernelHonestyError(`MODEL claim ${label} carries an out-of-range confidence ${f.confidence} — MODEL confidence MUST be in [0,1]`); } } var KERNEL_LEAD_SENTINEL = "==== SAFETY / CONTROL KERNEL (non-configurable; leads every frame) ===="; var KERNEL_DELTA_SENTINEL = "==== KERNEL DELTA (unlisted fields unchanged) ===="; var KERNEL_SECTION_LABELS = [ "-- WAKE --", "-- DATA-HEALTH --", "-- POSITIONS / INVENTORY-CLAIMS --", "-- RESTING ORDERS --", "-- BUDGET / REMAINING-R --", "-- OWNER ENVELOPE + ACTS --", "-- L0/L1 ENGINE LOG --", "-- PREDICTOR / REGIME CLAIMS --" ]; var RESERVED_PANE_IDS = ["kernel", "cockpit"]; function assertPaneIdAllowed(paneId) { const id = paneId.trim().toLowerCase(); if (RESERVED_PANE_IDS.includes(id)) { throw new KernelHonestyError(`pane id "${paneId}" is reserved for the non-configurable cockpit kernel and cannot be ` + "claimed by a configured pane (fail-closed)"); } } // src/frame/brief.ts function briefHashOf(text) { return `sha256:${sha256(text)}`; } function assertBriefHashHonest(brief) { const expected = briefHashOf(brief.text); if (brief.hash !== expected) { throw new KernelHonestyError(`brief hash mismatch — supplied ${JSON.stringify(brief.hash)} but text hashes to ${JSON.stringify(expected)} ` + "(fail-closed: a Brief's content hash must match its text so provenance attributes the real thesis)."); } } // src/frame/format.ts var DASH = "—"; function px(x) { if (x === null || x === undefined || !Number.isFinite(x)) return DASH; return Number.isInteger(x) ? String(x) : x.toFixed(2); } function num(x, prec = 2) { if (x === null || x === undefined || !Number.isFinite(x)) return DASH; return x.toFixed(prec); } function money(x) { if (x === null || x === undefined || !Number.isFinite(x)) return DASH; return (x >= 0 ? "+" : "") + x.toFixed(2); } function usd(x) { if (x === null || x === undefined || !Number.isFinite(x)) return DASH; return (x < 0 ? "-$" : "+$") + Math.abs(x).toFixed(2); } function signedInt(n) { return (n >= 0 ? "+" : "") + String(n); } function isSpotLeg(leg) { return leg.strike === undefined || leg.right === undefined || leg.strike === null || leg.right === null; } function toClose(min) { return `T-${min === null || !Number.isFinite(min) ? DASH : String(Math.round(min))}m to close`; } function pad(s, w) { return s.length >= w ? s : s + " ".repeat(w - s.length); } // src/frame/kernel-walk.ts function walkKernel(kernel, frameKind, sink) { sink.sentinel(frameKind); sink.frameLine(frameKind); if (kernel.mandate !== undefined && kernel.mandate !== null) sink.mandate(kernel.mandate); if (kernel.brief !== undefined && kernel.brief !== null) sink.brief(kernel.brief); sink.wake(kernel.wake); sink.dataHealth(kernel.dataHealth ?? [], kernel.unavailable ?? []); sink.positions(kernel.positions ?? []); sink.resting(kernel.resting ?? []); sink.budget(kernel.budgetEnvelope); sink.ownerActs(kernel.budgetEnvelope, kernel.ownerActs ?? []); sink.engineLog(kernel.engineLog ?? []); sink.claims(kernel.claims ?? []); } var ENGINE_LOG_BUCKETS = ["fired", "cancelled", "rejected", "clamped"]; function bucketEngineLog(elog) { const known = new Set(ENGINE_LOG_BUCKETS); return { buckets: ENGINE_LOG_BUCKETS.map((kind) => ({ kind, items: elog.filter((e) => e.kind === kind) })), other: elog.filter((e) => !known.has(e.kind)) }; } function restingLabel(o, labels) { const live = o.live === true; const clamped = o.clamped === true; if (live) return clamped ? labels.liveClamped : labels.live; return clamped ? labels.clamped : labels.off; } function actionToken(e, style) { const reason = e.reason != null && e.reason !== "" ? `${style.reasonSpace ? " " : ""}(${e.reason})` : ""; return `${e.id}@${style.seqPrefix}${e.asofSeq}${reason}`; } function collapseActionTokens(items, style) { const out = []; for (let i = 0;i < items.length; ) { const e = items[i]; let j = i + 1; while (j < items.length && items[j].id === e.id && (items[j].reason ?? null) === (e.reason ?? null)) j++; const count = j - i; const token = actionToken(e, style); out.push(count > 1 ? `${count}x ${token}` : token); i = j; } return out; } // src/frame/founder-views.generated.ts var FOUNDER_VIEWS = { "strategist-open": { name: "strategist-open", panes: [{ name: "instruments" }, { name: "levels" }, { name: "tape" }, { name: "chain" }, { name: "acting" }] }, "watcher-wake": { name: "watcher-wake", panes: [{ name: "delta" }, { name: "tape" }, { name: "levels" }, { name: "chain" }, { name: "acting" }] }, "scan-fire": { name: "scan-fire", panes: [{ name: "levels" }, { name: "series-summary" }, { name: "prior-context" }, { name: "tape", args: [{ kind: "arg-window", window: { value: 5, unit: "m" } }] }] } }; // src/frame/founder-registry.ts var FOUNDER_VIEW_NAME_BY_SEAT_FRAME = { "strategist/OPEN": "strategist-open", "watcher/WAKE": "watcher-wake", "pm/WAKE": "scan-fire" }; function founderViewFor(seat, frameKind) { const name = FOUNDER_VIEW_NAME_BY_SEAT_FRAME[`${seat}/${frameKind}`]; if (name === undefined) return; const view = FOUNDER_VIEWS[name]; if (view === undefined) { throw new Error(`founder registry maps ${seat}/${frameKind} → "${name}", but that View is absent from the generated FOUNDER_VIEWS ` + `(regenerate: bun scripts/gen-founder-view-registry.ts). This is registry/golden drift, never honest absence.`); } return view; } var FOUNDER_SEED_KEYS = Object.keys(FOUNDER_VIEW_NAME_BY_SEAT_FRAME); // src/frame/session-scheme.ts var SESSION_SCHEMES = [ "equity-rth", "cme-rth-overnight", "perp-utc-day", "fx-week" ]; var DEFAULT_SESSION_SCHEME = "equity-rth"; var SESSION_SCHEME_BOUNDARIES = { "equity-rth": { open: true, close: true, sessionSplit: false }, "cme-rth-overnight": { open: true, close: true, sessionSplit: true }, "perp-utc-day": { open: false, close: false, sessionSplit: false, rollingAnchor: "rolling 24h extreme" }, "fx-week": { open: true, close: true, sessionSplit: true } }; function isSessionScheme(s) { return SESSION_SCHEMES.includes(s); } function unknownSchemeRefusal(served) { return new KernelHonestyError(`SessionScheme "${served}" is not in the closed roster {${SESSION_SCHEMES.join(", ")}} — a ` + `series-registry attribute must name a DECLARED scheme (each carries its own boundary stream). ` + `Judgment REFUSED (fail-closed, never a silent equity-rth default): an undeclared scheme has no ` + `open/close/session-split declaration, so its boundaries cannot be honestly rendered. Add the ` + `scheme + its SchemeBoundaries to open the roster, or correct the value (ADR-0041 §1/§3).`); } function resolveScheme(scheme) { if (scheme === undefined || scheme === null) return DEFAULT_SESSION_SCHEME; if (!isSessionScheme(scheme)) throw unknownSchemeRefusal(scheme); return scheme; } function schemeHasBoundary(scheme, kind) { const b = SESSION_SCHEME_BOUNDARIES[scheme]; return kind === "open" ? b.open : kind === "close" ? b.close : b.sessionSplit; } function rollingAnchorOf(scheme) { return SESSION_SCHEME_BOUNDARIES[scheme].rollingAnchor; } // src/frame/paradigm-ledger.ts var ATTESTED_CELLS = [ { pane: "levels", sort: "LevelName", state: "ATTESTED", form: "levels vwap hod" }, { pane: "tape", sort: "Window", state: "ATTESTED", form: "tape 5m" }, { pane: "chain", sort: "Count", state: "ATTESTED", form: "chain 12" }, { pane: "tape", sort: "SessionOrdinal", state: "ATTESTED", form: "tape d-0" }, { pane: "chain", sort: "ExpiryOrdinal", state: "ATTESTED", form: "chain e-0" } ]; var LATENT_CELLS = [ { pane: "chain", sort: "Instrument", state: "LATENT", train: "chain Instrument (cross-instrument leg source) — a LATER train, NOT Train 1B (ADR-0041 §1: the " + "marked cross-instrument read is a CALC lexeme taking two Instrument args; Train 1A added Count, " + "Train 1B added ExpiryOrdinal, neither added Instrument)" } ]; var DEFECTIVE_CELLS = []; var DEFECTIVE_FIRST_ARRIVAL = "No UNCONDITIONED (pane × sort) cell is DEFECTIVE. The first production DEFECTIVE is " + "SCHEME-CONDITIONED and LANDED with kestrel-wa0j.44 (prior-context × SessionOrdinal under a " + "SessionScheme with no close boundary: reason 'no close under this SessionScheme', periphrasis " + "'anchor to the declared rolling extreme' — see schemeConditioned). A further DEFECTIVE source " + "arrives with kestrel-wa0j.42 (social/sentiment — no as-of source, determinism unachievable at the feed)."; var PANE_BOUNDARY_REQUIREMENT = { "prior-context": "close", tape: "close" }; var CONDITIONED_SORT = "SessionOrdinal"; function conditionedReason(kind) { return `no ${kind} under this SessionScheme`; } var CONDITIONED_PERIPHRASIS = "anchor to the declared rolling extreme"; function schemeConditionedDefect(pane, scheme) { const req = PANE_BOUNDARY_REQUIREMENT[pane]; if (req === undefined) return; if (schemeHasBoundary(scheme, req)) return; return { pane, sort: CONDITIONED_SORT, state: "DEFECTIVE", reason: conditionedReason(req), periphrasis: CONDITIONED_PERIPHRASIS, conditioning: { sessionScheme: scheme } }; } var SCHEME_CONDITIONED_CELLS = Object.keys(PANE_BOUNDARY_REQUIREMENT).flatMap((pane) => SESSION_SCHEMES.map((scheme) => schemeConditionedDefect(pane, scheme))).filter((c) => c !== undefined); var PARADIGM_CELLS = [...ATTESTED_CELLS, ...LATENT_CELLS, ...DEFECTIVE_CELLS]; var DESTINATION_BACKLOG = { macro: "macro period/context sub-selectors (a later train — outside Train 1A/1B)", vol: "vol straddle / expected-move sub-selectors (a later train)", tape: "multi-arg tape — style/overlay/strip position classes (deferred; Train 1A migrated the Window arg, Train 1B added the SessionOrdinal address — style/overlay/strip remain LATENT)", levels: "named / band-keyed level-set idents (e.g. `registry`) — deferred; Train 1A serves individual-level idents only", chain: "chain sub-selectors (fair/realness) + Instrument — deferred to a LATER train (Train 1A added Count, Train 1B added ExpiryOrdinal; Instrument + fair/realness sub-selectors remain LATENT)" }; function latentTrainFor(pane) { return DESTINATION_BACKLOG[pane]; } // src/frame/refusals.ts function argStr(a) { if (a.kind === "arg-ident") return a.name; if (a.kind === "arg-count") return `${a.count}`; if (a.kind === "arg-ordinal") return `d-${a.ordinal}`; if (a.kind === "arg-expiry") return `e-${a.expiry}`; return `${a.window.value}${a.window.unit}`; } function argsStr(args) { return args.map(argStr).join(" "); } function syntacticSupersort(a) { switch (a.kind) { case "arg-ident": return "ident"; case "arg-count": return "numeral"; case "arg-ordinal": return "ordinal"; case "arg-expiry": return "expiry"; case "arg-window": return "window"; } } var SORT_ACCEPTS = { Instrument: "ident", LevelName: "ident", Window: "window", Count: "numeral", SessionOrdinal: "ordinal", ExpiryOrdinal: "expiry" }; function supersortPhrase(s) { switch (s) { case "ident": return "a bare ident"; case "window": return "a window (a numeral + a time unit, e.g. `5m`)"; case "numeral": return "a bare count numeral (e.g. `12`)"; case "ordinal": return "a session ordinal (`d-<n>`, e.g. `d-0`)"; case "expiry": return "an expiry ordinal (`e-<n>`, e.g. `e-0`)"; } } function slotSpecOf(slots) { return slots.map((s) => `${s.name}:${s.sort}${s.arity === "many" ? "…" : ""}`).join(", "); } function paneRefusal(r) { return new KernelHonestyError(refusalMessage(r)); } function refusalMessage(r) { switch (r.kind) { case "unknown-pane": return `View "${r.view}" names pane "${r.pane}", which is NOT in the pane catalog — the pane cannot ` + `render (single-source: a View selects only catalogued panes). Known panes: ` + `${r.known.join(", ")} (fail-closed, never a silent drop).`; case "takes-no-args": return `View "${r.view}" passes arg${r.args.length === 1 ? "" : "s"} "${argsStr(r.args)}" ` + `to pane "${r.pane}", which takes no arguments — an arg a pane does not understand is refused ` + `(fail-closed, never silently ignored).`; case "no-signature": return `pane "${r.pane}" received arg${r.args.length === 1 ? "" : "s"} "${argsStr(r.args)}" but declares ` + `no ParamSlot signature — cannot elaborate its args (fail-closed at the \`mat\` rung; a catalog defect).`; case "arity": return `pane "${r.pane}" was served ${r.args.length} arg${r.args.length === 1 ? "" : "s"} "${argsStr(r.args)}" ` + `but its signature binds only [${slotSpecOf(r.slots)}] — too many arguments. Judgment REFUSED at the \`mat\` rung ` + `(materializable: an argument must elaborate at its slot's sort and arity, ADR-0041 §3; fail-closed, never silently dropped).`; case "sort-mismatch": { const want = SORT_ACCEPTS[r.slot.sort]; const got = syntacticSupersort(r.arg); return `pane "${r.pane}" · slot "${r.slot.name}" (sort ${r.slot.sort}): expected ${supersortPhrase(want)} but was served ` + `${supersortPhrase(got)} "${argStr(r.arg)}". Judgment REFUSED at the \`mat\` rung (materializable: an argument ` + `must elaborate at its slot's sort, ADR-0041 §3; fail-closed, never a silent coercion).`; } case "count-not-integer": return `pane "${r.pane}" · slot "${r.slot.name}" (sort Count): a Count must be a positive integer, got "${argStr(r.arg)}". ` + `Judgment REFUSED at the \`mat\` rung (a cardinal names N nearest-money addresses; fail-closed, never a fractional count).`; case "ordinal-not-nonneg-integer": return `pane "${r.pane}" · slot "${r.slot.name}" (sort ${r.slot.sort}): an ordinal must be a non-negative integer, got "${argStr(r.arg)}". ` + `Judgment REFUSED at the \`mat\` rung (a ${r.slot.sort} names an ordinal into the declared boundary stream — ` + `${r.slot.sort === "SessionOrdinal" ? "d-0 the current session, d-1 the prior" : "e-0 the nearest expiry, e-1 the next out"}; ` + `fail-closed, never a fractional or negative ordinal).`; case "window-not-servable": return `pane "${r.pane}" cannot serve window arg "${argStr(r.arg)}": the tape rows are served pre-bucketed at ` + `${r.servedBucketMin}m — a renderable window must be a multiple of the served ${r.servedBucketMin}m (a sub-resolution or ` + `non-multiple window cannot be expressed by these rows; rendering them under a "${argStr(r.arg)}" ` + `label would misstate the scale — fail-closed, never mismatched data as if requested).`; case "unknown-value-naming-roster": return `pane "${r.pane}" · slot "${r.slot}": "${r.value}" is not a known level — the levels roster is ` + `{${r.roster.join(", ")}}. Judgment REFUSED (an ident must name a cell the LevelSet carries; ` + `fail-closed, never a silent drop). Cell state: a named level-SET like \`registry\` is LATENT — the ` + `band-keyed level-set inflection (a later train), NOT Train 1A's individual-level idents (ADR-0041 §3).`; case "latent-cell": { const train = latentTrainFor(r.pane); const named = train ?? "(no train recorded in the ledger — a ledger gap, fail-closed)"; return `pane "${r.pane}" · form "${r.form}": the paradigm cell is LATENT — not yet materializable, but a ` + `named train will land it. Deferred train: ${named}. Judgment REFUSED at the \`mat\` rung ` + `(ADR-0041 §3: a LATENT refusal names its train; fail-closed, never a silent drop).`; } case "defective-cell": { const cond = r.conditioning !== undefined ? ` (conditioned on ${r.conditioning})` : ""; return `pane "${r.pane}" · form "${r.form}": the paradigm cell is DEFECTIVE — it never materializes${cond}. ` + `Written reason: ${r.reason}. Sanctioned periphrasis: ${r.periphrasis}. Judgment REFUSED ` + `(ADR-0041 §3: a DEFECTIVE refusal names its written reason + its sanctioned periphrasis; ` + `absent-not-hidden — the address has no value under this paradigm, and the periphrasis renders in its place, never an invented one).`; } case "over-budget": return `View "${r.view}" is over its token budget: the selected panes (${r.paneIds.join(", ")}) ` + `measured ${r.spent} tokens under tokenizer "${r.tokenizer}", exceeding the budget of ${r.budget}. ` + "Refusing to render a View that does not fit its own budget (fail-closed — trim the View or raise its budget; " + "the non-configurable kernel is not counted against the View budget)."; case "unreachable-bound-arg": return `pane "${r.pane}" expected ${r.expected} but was reached with "${argsStr(r.args)}" (fail-closed).`; case "unreachable-not-catalogued": return `pane "${r.pane}" is not in the catalog — cannot materialize (fail-closed).`; case "unreachable-args-on-argless": return `pane "${r.pane}" takes no arguments but was resolved with "${argsStr(r.args)}" ` + `— cannot materialize (fail-closed).`; } } // src/frame/options-analytics.ts function legKey(expiry, strike, right) { return `${expiry}:${strike}:${right}`; } function reduceOptionSurface(events, throughSeq) { const legs = new Map; let underlier = ""; let spot = null; let spotTs = null; let cutoffSeq = -1; let cutoffTs = 0; for (const e of events) { if (throughSeq !== undefined && e.seq > throughSeq) break; cutoffSeq = e.seq; cutoffTs = e.ts; if (e.stream !== "TICK") continue; if (e.type === "SPOT") { spot = e.px; spotTs = e.ts; if (underlier === "") underlier = e.instrument; } else if (e.type === "BOOK") { const expiry = e.expiry ?? ""; if (underlier === "") underlier = e.instrument; for (const l of e.legs) { legs.set(legKey(expiry, l.strike, l.right), { expiry, strike: l.strike, right: l.right, bid: l.bid, ask: l.ask }); } } } return { underlier, legs, spot, spotTs, cutoffSeq, cutoffTs }; } var SPOT_STALE_AFTER_MS = 120000; function liquidMid(bid, ask) { if (bid === null || ask === null) return null; const mid = 0.5 * (bid + ask); return mid > 0 ? mid : null; } function daysBetween(fromTs, toTs) { return Math.max(0, Math.floor((toTs - fromTs) / 86400000)); } function buildOptionsAnalytics(surface, config) { const multiplier = config.multiplier ?? 100; const rate = config.rate ?? 0; const staleMs = config.staleMs ?? SPOT_STALE_AFTER_MS; const { spot, spotTs, cutoffTs, cutoffSeq } = surface; const spotStaleSeconds = spotTs === null ? null : Math.max(0, Math.floor((cutoffTs - spotTs) / 1000)); const spotStale = spot === null || spotTs === null || cutoffTs - spotTs > staleMs; const byExpiry = new Map; for (const l of surface.legs.values()) { const arr = byExpiry.get(l.expiry) ?? []; arr.push({ strike: l.strike, right: l.right, bid: l.bid, ask: l.ask }); byExpiry.set(l.expiry, arr); } const expiries = []; for (const [expiry, rawLegs] of byExpiry) { const closeTs = config.expiryCloseTs.get(expiry); const tauYears = closeTs === undefined ? null : Math.max(0, (closeTs - cutoffTs) / MS_PER_YEAR); const daysToExpiry = closeTs === undefined ? null : daysBetween(cutoffTs, closeTs); const legs = rawLegs.map((r) => { const mid = liquidMid(r.bid, r.ask); const oi = config.openInterest?.get(legKey(expiry, r.strike, r.right)) ?? null; let iv = null; let delta = null; let gamma = null; let vega = null; if (mid !== null && spot !== null && !spotStale && tauYears !== null && tauYears > 0) { iv = impliedVol({ forward: spot, strike: r.strike, tauYears, price: mid, right: r.right }); if (iv !== null) { const g = greeks({ forward: spot, strike: r.strike, tauYears, sigma: iv, right: r.right }); delta = g.delta; gamma = g.gamma; vega = g.vega; } } return { strike: r.strike, right: r.right, bid: r.bid, ask: r.ask, mid, iv, delta, gamma, vega, oi }; }).sort((a, b) => a.strike - b.strike || (a.right < b.right ? -1 : a.right > b.right ? 1 : 0)); expiries.push({ expiry, tauYears, daysToExpiry, legs }); } expiries.sort((a, b) => { const ca = config.expiryCloseTs.get(a.expiry); const cb = config.expiryCloseTs.get(b.expiry); if (ca === undefined && cb === undefined) return a.expiry < b.expiry ? -1 : 1; if (ca === undefined) return 1; if (cb === undefined) return -1; return ca - cb; }); const method = `Black-Scholes r=${rate}; tau=(expiry-close − cutoff)/365d; IV from two-sided NBBO mid`; return { underlier: surface.underlier, spot, spotStale, spotStaleSeconds, asOfSeq: cutoffSeq, multiplier, rate, method, expiries }; } // src/frame/tape-geometry.ts var TAPE_WIDTH = 40; function tapeBounds(rows) { let lo = Infinity; let hi = -Infinity; for (const r of rows) { lo = Math.min(lo, r.low, r.open, r.close); hi = Math.max(hi, r.high, r.open, r.close); } return { lo, hi }; } function candleLine(row, lo, hi, width = TAPE_WIDTH) { const span = hi - lo; const colOf = (p) => { if (span <= 0) return 0; const c = Math.round((p - lo) / span * (width - 1)); return c < 0 ? 0 : c > width - 1 ? width - 1 : c; }; const loCol = colOf(row.low); const hiCol = colOf(row.high); const bodyLo = Math.min(colOf(row.open), colOf(row.close)); const bodyHi = Math.max(colOf(row.open), colOf(row.close)); const cells = Array(width).fill(" "); for (let c = loCol;c <= hiCol; c += 1) cells[c] = "─"; for (let c = bodyLo;c <= bodyHi; c += 1) cells[c] = "█"; cells[bodyLo] = "█"; return cells.join("").replace(/\s+$/, ""); } // src/frame/pane-catalog.ts function renderInstruments(instruments) { if (instruments.length === 0) return `instruments: (none)`; const lines = instruments.map((i) => { const role = i.role !== undefined ? ` ${i.role}` : ""; const mult = `mult ${px(i.multiplier)}`; const tick = `tick ${px(i.tick)}`; return ` ${i.symbol} ${i.assetClass}${role} ${mult} ${tick}`; }); return ["instruments:", ...lines].join(` `); } function spotCell(lv) { const age = lv.spotStaleSeconds; if (age === undefined || age === null || age * 1000 <= SPOT_STALE_AFTER_MS) return `spot ${px(lv.spot)}`; return `spot ${px(lv.spot)} [STALE ${Math.round(age / 60)}m]`; } function renderLevels(instrument, lv) { const parts = [ spotCell(lv), `prior_close ${px(lv.priorClose)}`, `hod ${px(lv.hod)}`, `lod ${px(lv.lod)}`, `vwap ${px(lv.vwap)}`, `or ${px(lv.orLow)}–${px(lv.orHigh)}` ]; return `levels · ${instrument} ${parts.join(" · ")}`; } var LEVEL_ROSTER = ["spot", "prior_close", "hod", "lod", "vwap", "or"]; function levelCell(name, lv) { switch (name) { case "spot": return spotCell(lv); case "prior_close": return `prior_close ${px(lv.priorClose)}`; case "hod": return `hod ${px(lv.hod)}`; case "lod": return `lod ${px(lv.lod)}`; case "vwap": return `vwap ${px(lv.vwap)}`; case "or": return `or ${px(lv.orLow)}–${px(lv.orHigh)}`; } } function renderLevelsWithArgs(ctx, args) { const lv = ctx.market.levels; const cells = []; for (const a of args) { if (a.kind !== "arg-ident") { throw paneRefusal({ kind: "unreachable-bound-arg", pane: "levels", expected: "bound LevelName idents", args: [a] }); } if (!LEVEL_ROSTER.includes(a.name)) { throw paneRefusal({ kind: "unknown-value-naming-roster", pane: "levels", slot: "level-set", value: a.name, roster: LEVEL_ROSTER }); } cells.push(levelCell(a.name, lv)); } return `levels · ${ctx.market.instrument} ${cells.join(" · ")}`; } function renderTape(rows, bucketMin, sessionTag) { const tag = sessionTag !== undefined ? ` · ${sessionTag}` : ""; if (rows.length === 0) { return [`tape ${bucketMin}m${tag} · (no prints this window)`].join(` `); } const { lo, hi } = tapeBounds(rows); const anchorClock = rows[0].clock; const header = `tape ${bucketMin}m${tag} · axis ${px(lo)}→${px(hi)} · anchor @ ${anchorClock} ET`; const body = rows.map((r) => `${r.clock} ${candleLine(r, lo, hi)}`); return [header, ...body].join(` `); } function rebucketTape(rows, factor) { const out = []; for (let i = 0;i < rows.length; i += factor) { const run = rows.slice(i, i + factor); const first = run[0]; const last = run[run.length - 1]; let high = -Infinity; let low = Infinity; let volume = 0; let allVolumed = true; for (const r of run) { if (r.high > high) high = r.high; if (r.low < low) low = r.low; if (r.volume === null || r.volume === undefined || !Number.isFinite(r.volume)) allVolumed = false; else volume += r.volume; } out.push({ clock: last.clock, open: first.open, high, low, close: last.close, ...allVolumed ? { volume } : {} }); } return out; } function windowServableBy(bucketMin, window) { const w = windowMinutes(window); if (w === null || !Number.isFinite(bucketMin) || bucketMin <= 0) return false; const factor = w / bucketMin; return Number.isInteger(factor) && factor >= 1; } function renderTapeWithArgs(ctx, args) { const windowArg = args.find((a) => a.kind === "arg-window"); const sessionArg = args.find((a) => a.kind === "arg-ordinal"); if (sessionArg !== undefined && sessionArg.ordinal >= 1) { return renderTapePriorSession(ctx, sessionArg.ordinal, windowArg); } const available = ctx.market.tapeBucketMin; if (windowArg === undefined) return renderTape(ctx.market.tape, available); const requested = windowMinutes(windowArg.window); if (requested === null || !windowServableBy(available, windowArg.window)) { throw paneRefusal({ kind: "window-not-servable", pane: "tape", arg: windowArg, servedBucketMin: available }); } const factor = requested / available; if (factor === 1) return renderTape(ctx.market.tape, requested); return renderTape(rebucketTape(ctx.market.tape, factor), requested); } function renderTapePriorSession(ctx, ordinal, windowArg) { const scheme = schemeOfMarket(ctx); const label = windowArg !== undefined ? ` ${windowArg.window.value}${windowArg.window.unit}` : ""; const addr = `d-${ordinal}`; const head = `tape${label} · ${ctx.market.instrument} · ${addr}`; const defect = schemeConditionedDefect("tape", scheme); if (defect !== undefined) { const anchor = rollingAnchorOf(scheme) ?? "rolling extreme"; return `${head} · (${defect.reason} (${scheme}) — no prior-session address under this SessionScheme; ${defect.periphrasis}: ${anchor})`; } const carried = ctx.priorSessions?.find((s) => s.ordinal === ordinal); if (carried !== undefined && carried.rows.length > 0) { return renderPriorSessionBlock(ctx, carried, ordinal, windowArg); } return `${head} · (frozen frame carries no session at ${addr} — the current session only; multiday tape data threads with kestrel-wa0j.20)`; } function renderPriorSessionBlock(ctx, session, ordinal, windowArg) { const available = ctx.market.tapeBucketMin; const tag = session.sessionLabel !== undefined ? `d-${ordinal} (${session.sessionLabel})` : `d-${ordinal}`; if (windowArg === undefined) return renderTape(session.rows, available, tag); const requested = windowMinutes(windowArg.window); if (requested === null || !windowServableBy(available, windowArg.window)) { throw paneRefusal({ kind: "window-not-servable", pane: "tape", arg: windowArg, servedBucketMin: available }); } const factor = requested / available; if (factor === 1) return renderTape(session.rows, requested, tag); return renderTape(rebucketTape(session.rows, factor), requested, tag); } function darkFlag(row) { const bidDark = row.bid === null; const askDark = row.ask === null; if (bidDark && askDark) return "dark"; if (bidDark) return "bid dark"; if (askDark) return "ask dark"; return DASH; } function renderChain(underlier, chain, dte) { const dteTag = dte !== undefined && dte !== null && Number.isFinite(dte) ? ` · ${dte}dte` : ""; if (chain.length === 0) { return `chain (near-money) · ${underlier}${dteTag} (no legs)`; } const head = ` ${pad("strike", 8)}${pad("R", 3)}${pad("bid", 8)}${pad("ask", 8)}${pad("fair", 10)}flags`; const lines = chain.map((r) => { const fair = r.fairNote !== undefined && r.fair !== null && r.fair !== undefined ? `${px(r.fair)} ${r.fairNote}` : px(r.fair); const fairCell = pad(fair, Math.max(10, fair.length + 2)); return ` ${pad(px(r.strike), 8)}${pad(r.right, 3)}${pad(px(r.bid), 8)}${pad(px(r.ask), 8)}${fairCell}${darkFlag(r)}`; }); return [`chain (near-money) · ${underlier}${dteTag}`, head, ...lines].join(` `); } function selectNearMoneyStrikes(chain, n) { const seen = new Set; const ordered = []; for (const r of chain) { if (!seen.has(r.strike)) { seen.add(r.strike); ordered.push(r.strike); } } const keep = new Set(ordered.slice(0, n)); return { rows: chain.filter((r) => keep.has(r.strike)), availableStrikes: ordered.length }; } function renderChainWithArgs(ctx, args) { const countArg = args.find((a) => a.kind === "arg-count"); const expiryArg = args.find((a) => a.kind === "arg-expiry"); if (expiryArg !== undefined && expiryArg.expiry >= 1) { return renderChainFurtherExpiry(ctx, expiryArg.expiry, countArg); } if (countArg === undefined) { return renderChain(ctx.market.instrument, ctx.market.chain, ctx.market.chainDte); } const n = countArg.count; const { rows, availableStrikes } = selectNearMoneyStrikes(ctx.market.chain, n); const base = renderChain(ctx.market.instrument, rows, ctx.market.chainDte); if (availableStrikes >= n) return base; const missing = n - availableStrikes; const shortfall = ` (requested ${n} near-money strikes; ${availableStrikes} available — ${missing} absent with reason: ` + `the frozen near-money chain carries only ${availableStrikes} strike${availableStrikes === 1 ? "" : "s"})`; return `${base} ${shortfall}`; } function renderChainFurtherExpiry(ctx, expiry, countArg) { const countLabel = countArg !== undefined ? ` ${countArg.count}` : ""; const addr = `e-${expiry}`; return `chain (near-money)${countLabel} · ${ctx.market.instrument} · ${addr} · ` + `(frozen chain carries no expiry at ${addr} — the nearest expiry only; multi-expiry chain data threads with kestrel-wa0j.20)`; } var MACRO_PANE = "macro: unavailable (v1 harness)"; function planTag(plan) { return plan === undefined ? "" : ` (${plan})`; } function renderPositions(positions) { if (positions.length === 0) return `positions: (none)`; const lines = positions.map((p) => isSpotLeg(p) ? ` ${signedInt(p.qty)} ${p.instrument} shares basis ${px(p.basis)}${unrealTag(p)}${planTag(p.plan)}` : ` ${signedInt(p.qty)} ${p.instrument} ${px(p.strike)}${p.right} basis ${px(p.basis)} fair ${px(p.fair)}${unrealTag(p)}${planTag(p.plan)}`); return ["positions:", ...lines].join(` `); } function unrealTag(p) { return p.unrealUsd === undefined ? "" : ` unreal ${usd(p.unrealUsd)}`; } function renderResting(resting) { if (resting.length === 0) return `resting: (none)`; const lines = resting.map((o) => { const note = o.note !== undefined ? ` [${o.note}]` : ""; const leg = isSpotLeg(o) ? "shares" : `${px(o.strike)}${o.right}`; return ` ${o.side.toUpperCase()} ${o.qty} ${o.instrument} ${leg} @ ${px(o.px)}${note}${planTag(o.plan)}`; }); return ["resting:", ...lines].join(` `); } function renderFills(fills) { if (fills.length === 0) return `fills since last: (none)`; const lines = fills.map((f) => { const at = f.clock !== undefined ? ` @${f.clock}` : ""; const leg = isSpotLeg(f) ? "shares" : `${px(f.strike)}${f.right}`; return ` ${f.side.toUpperCase()} ${f.qty} ${f.instrument} ${leg} @ ${px(f.px)}${at}${planTag(f.plan)}`; }); return ["fills since last:", ...lines].join(` `); } function renderBudget(budget) { if (budget === null || budget === undefined) return `premium budget: ${DASH}`; const total = budget.total !== undefined ? ` (total ${px(budget.total)}` : ""; const maxR = budget.maxConcurrentR !== undefined ? `, maxR ${px(budget.maxConcurrentR)})` : total !== "" ? ")" : ""; return `premium budget: used ${money(budget.used)} / remaining ${money(budget.remaining)}${total}${maxR}`; } function renderPlans(kernel) { if (kernel.plans.length === 0) return `plans: (none)`; const lines = kernel.plans.map((p) => { const outcome = p.outcome !== undefined ? `(${p.outcome})` : ""; const blocked = p.blockedReason !== undefined ? ` (blocked: ${p.blockedReason})` : ""; const note = p.note !== undefined ? ` — ${p.note}` : ""; return ` ${p.name}: ${p.state}${outcome === "" ? "" : " " + outcome}${blocked}${note}`; }); return ["plans:", ...lines].join(` `); } function renderActingDetail(kernel) { return [ "KERNEL (acting)", renderPositions(kernel.positions), renderResting(kernel.resting), renderFills(kernel.fillsSinceLast), renderBudget(kernel.budget), renderPlans(kernel) ].join(` `); } function pokesForLevel(rows, level, dir) { const pierces = (r) => dir === "up" ? r.high > level : r.low < level; const pastBy = (p) => dir === "up" ? p - level : level - p; const pokes = []; const n = rows.length; let i = 0; while (i < n) { if (!pierces(rows[i])) { i += 1; continue; } let j = i; let excursion = 0; while (j < n && pierces(rows[j])) { const r = rows[j]; const ex = pastBy(dir === "up" ? r.high : r.low); if (ex > excursion) excursion = ex; j += 1; } const reachesEnd = j === n; const lastClose = rows[j - 1].close; const held = reachesEnd && (dir === "up" ? lastClose > level : lastClose < level); pokes.push({ excursion, bars: j - i, held }); i = j; } return pokes; } function failedBreakLine(rows, lv) { if (lv.value === null || lv.value === undefined || !Number.isFinite(lv.value)) { return { line: ` ${lv.label} ${DASH}: level unknown`, failed: 0 }; } const pokes = pokesForLevel(rows, lv.value, lv.dir); const probed = pokes.length; const held = pokes.filter((p) => p.held).length; const failedPokes = pokes.filter((p) => !p.held); const failed = failedPokes.length; const head = ` ${lv.label} ${px(lv.value)}: probed ${probed}×`; if (probed === 0) return { line: head, failed: 0 }; const detail = `${head} · held ${held}× · failed ${failed}×`; if (failed === 0) return { line: `${detail} · clean break (held)`, failed: 0 }; const maxExc = failedPokes.reduce((m, p) => p.excursion > m ? p.excursion : m, 0); const longest = failedPokes.reduce((m, p) => p.bars > m ? p.bars : m, 0); return { line: `${detail} · max excursion ${num(maxExc, 2)} · longest ${longest} bar${longest === 1 ? "" : "s"}`, failed }; } function renderFailedBreaks(market) { const lv = market.levels; const levels = [ { label: "hod", value: lv.hod, dir: "up" }, { label: "lod", value: lv.lod, dir: "down" }, { label: "orHigh", value: lv.orHigh, dir: "up" }, { label: "orLow", value: lv.orLow, dir: "down" } ]; if (market.tape.length === 0) { return `failed-breaks · ${market.instrument} · (no prints this window)`; } const built = levels.map((l) => failedBreakLine(market.tape, l)); const total = built.reduce((s, b) => s + b.failed, 0); const header = `failed-breaks · ${market.instrument} · ${total} failed this session`; return [header, ...built.map((b) => b.line)].join(` `); } function pastSplit(r, level, dir) { const bodyHi = Math.max(r.open, r.close); const bodyLo = Math.min(r.open, r.close); if (dir === "up") { const body2 = Math.max(0, bodyHi - level); const wick2 = r.high - Math.max(bodyHi, level); return { wick: wick2, body: body2 }; } const body = Math.max(0, level - bodyLo); const wick = Math.min(bodyLo, level) - r.low; return { wick, body }; } function breakInteractionLine(rows, lv) { if (lv.value === null || lv.value === undefined || !Number.isFinite(lv.value)) { return ` ${lv.label} ${DASH}: level unknown`; } const level = lv.value; const touches = rows.filter((r) => r.low <= level && level <= r.high); if (touches.length === 0) return ` ${lv.label} ${px(level)}: no touch`; const last = touches[touches.length - 1]; const broke = lv.dir === "up" ? last.close > level : last.close < level; const verb = broke ? "broke (body through)" : "rejected (wick, closed inside)"; const { wick, body } = pastSplit(last, level, lv.dir); const wickLabel = lv.dir === "up" ? "upper-wick" : "lower-wick"; return ` ${lv.label} ${px(level)}: ${touches.length} touch${touches.length === 1 ? "" : "es"} · ${verb} · ${wickLabel} ${num(wick, 2)} / body ${num(body, 2)}`; } function vwapInteractionLine(rows, vwap) { if (vwap === null || vwap === undefined || !Number.isFinite(vwap)) { return ` vwap ${DASH}: level unknown`; } const touches = rows.filter((r) => r.low <= vwap && vwap <= r.high); if (touches.length === 0) return ` vwap ${px(vwap)}: no touch`; const last = touches[touches.length - 1]; const side = last.close > vwap ? "closed above (reclaim)" : last.close < vwap ? "closed below (loss)" : "closed at vwap"; const up = last.high - vwap; const down = vwap - last.low; return ` vwap ${px(vwap)}: ${touches.length} touch${touches.length === 1 ? "" : "es"} · ${side} · reach up ${num(up, 2)} / down ${num(down, 2)}`; } function renderLevelInteraction(market) { const lv = market.levels; if (market.tape.length === 0) { return `level-interaction · ${market.instrument} · (no prints this window)`; } const breakLevels = [ { label: "hod", value: lv.hod, dir: "up" }, { label: "lod", value: lv.lod, dir: "down" }, { label: "orHigh", value: lv.orHigh, dir: "up" }, { label: "orLow", value: lv.orLow, dir: "down" } ]; const lines = [ breakInteractionLine(market.tape, breakLevels[0]), breakInteractionLine(market.tape, breakLevels[1]), vwapInteractionLine(market.tape, lv.vwap), breakInteractionLine(market.tape, breakLevels[2]), breakInteractionLine(market.tape, breakLevels[3]) ]; return [`level-interaction · ${market.instrument}`, ...lines].join(` `); } function p99NearestRank(sample) { const sorted = [...sample].sort((a, b) => a - b); const rank = Math.max(1, Math.ceil(0.99 * sorted.length)); return sorted[rank - 1]; } function renderRangeVelocity(market) { const rows = market.tape; const head = `range-velocity · ${market.instrument}`; if (rows.length === 0) { return `${head} · (no prints this window)`; } let hi = -Infinity; let lo = Infinity; for (const r of rows) { if (r.high > hi) hi = r.high; if (r.low < lo) lo = r.low; } const range = hi - lo; if (rows.length < 2) { return [ head, ` range ${num(range, 2)} · velocity ${DASH} · 1 bar (insufficient for a close-to-close velocity)`, " read: UNKNOWN — need ≥2 bars for a velocity read (insufficient)" ].join(` `); } const deltas = []; for (let i = 1;i < rows.length; i += 1) deltas.push(rows[i].close - rows[i - 1].close); const absDeltas = deltas.map((d) => Math.abs(d)); const net = rows[rows.length - 1].close - rows[0].close; const move = Math.abs(net); const p99 = p99NearestRank(absDeltas); const lastAbs = absDeltas[absDeltas.length - 1]; const barVel = deltas[deltas.length - 1]; const bars = rows.length; const ratioPct = p99 > 0 ? Math.round(lastAbs / p99 * 100) : null; const ratioStr = ratioPct === null ? DASH : `${ratioPct}%`; const l2 = ` range ${num(range, 2)} · velocity ${money(net)} · move ${num(move, 2)} over ${bars} bars`; const l3base = ` bar velocity now ${money(barVel)} · p99 ${num(p99, 2)} (${ratioStr} of p99)`; const prevAbs = absDeltas.length >= 2 ? absDeltas[absDeltas.length - 2] : null; if (prevAbs === null || ratioPct === null) { return [ head, l2, `${l3base} · trend UNKNOWN`, " read: UNKNOWN — need ≥3 bars to judge accelerating vs decelerating (insufficient)" ].join(` `); } const trend = lastAbs > prevAbs ? "ACCELERATING" : lastAbs < prevAbs ? "DECELERATING" : "STEADY"; const l3 = `${l3base} · ${trend}`; let read; if (trend === "ACCELERATING" && ratioPct >= 50) { read = " read: IMPULSIVE — break on accelerating velocity at/near its p99, fundable"; } else if (trend === "DECELERATING" && ratioPct < 50) { read = " read: GRINDING — poke on decelerating velocity far below p99, unfunded (EQ fakeout)"; } else { read = ` read: MIXED — velocity ${ratioStr} of p99, ${trend.toLowerCase()}; inconclusive (neither clean impulse nor clean grind)`; } return [head, l2, l3, read].join(` `); } function percentileNearestRank(sample, p) { const sorted = [...sample].sort((a, b) => a - b); const rank = Math.max(1, Math.ceil(p * sorted.length)); return sorted[rank - 1]; } function leastSquaresSlope(closes) { const n = closes.length; const xBar = (n - 1) / 2; let yBar = 0; for (const y of closes) yBar += y; yBar /= n; let num2 = 0; let den = 0; for (let i = 0;i < n; i += 1) { const dx = i - xBar; num2 += dx * (closes[i] - yBar); den += dx * dx; } return num2 / den; } function closeVsVwapLine(lastClose, vwap) { if (vwap === null || vwap === undefined || !Number.isFinite(vwap)) { return ` close-vs-vwap ${DASH} (vwap unknown)`; } const dist = lastClose - vwap; const pctCell = vwap === 0 ? DASH : `${money(dist / vwap * 100)}%`; return ` close-vs-vwap ${money(dist)} (${pctCell}) — close ${px(lastClose)} vs vwap ${px(vwap)}`; } function renderSeriesSummary(market) { const rows = market.tape; const head = `series-summary · ${market.instrument}`; if (rows.length === 0) { return `${head} · (no prints this window)`; } const closes = rows.map((r) => r.close); const lastClose = closes[closes.length - 1]; const vwap = market.levels.vwap; const bucketMin = market.tapeBucketMin; if (rows.length < 2) { return [ head, ` window 1 bucket · ${num(bucketMin, 0)}m each`, ` drift ${DASH} (need ≥2 buckets for a close-to-close read)`, closeVsVwapLine(lastClose, vwap), ` slope ${DASH} (need ≥2 buckets for a least-squares slope)`, ` velocity 1-bucket |move| ${DASH} (need ≥2 buckets; n=0)` ].join(` `); } const firstClose = closes[0]; const drift = lastClose - firstClose; const driftPct = firstClose === 0 ? DASH : `${money(drift / firstClose * 100)}%`; const absDeltas = []; for (let i = 1;i < closes.length; i += 1) absDeltas.push(Math.abs(closes[i] - closes[i - 1])); const p50 = percentileNearestRank(absDeltas, 0.5); const p90 = percentileNearestRank(absDeltas, 0.9); const max = Math.max(...absDeltas); const slope = leastSquaresSlope(closes); return [ head, ` window ${rows.length} buckets · ${num(bucketMin, 0)}m each`, ` drift ${money(drift)} (${driftPct}) — close ${px(firstClose)} → ${px(lastClose)}`, closeVsVwapLine(lastClose, vwap), ` slope ${money(slope)} pts/bucket (least-squares over ${rows.length} closes)`, ` velocity 1-bucket |move| p50=${num(p50, 2)} p90=${num(p90, 2)} max=${num(max, 2)} n=${absDeltas.length}` ].join(` `); } var GEX_ASSUMPTION = "assume dealer short calls / long puts ⇒ short γ above spot, long γ below"; function ivPct(iv) { return iv === null || iv === undefined || !Number.isFinite(iv) ? DASH : `${(iv * 100).toFixed(1)}%`; } function millions(x) { if (x === null || x === undefined || !Number.isFinite(x)) return DASH; const m = x / 1e6; return `${m >= 0 ? "+" : "−"}${Math.abs(m).toFixed(1)}M`; } function gexProfile(o) { const spot = o.spot; if (spot === null || !Number.isFinite(spot)) return null; const spot2 = spot * spot; const byStrike = new Map; let computed = 0; let withOi = 0; for (const exp of o.expiries) { for (const l of exp.legs) { if (l.oi !== null && l.oi > 0) withOi += 1; if (l.gamma === null || l.oi === null || l.oi <= 0) continue; const sign = l.right === "C" ? -1 : 1; const dealerGamma = sign * l.gamma * l.oi * o.multiplier * spot2 * 0.01; byStrike.set(l.strike, (byStrike.get(l.strike) ?? 0) + dealerGamma); computed += 1; } } if (computed === 0) return null; const perStrike = [...byStrike.entries()].map(([strike, net2]) => ({ strike, net: net2 })).sort((a, b) => a.strike - b.strike); const net = perStrike.reduce((s, p) => s + p.net, 0); let flip = null; let cum = 0; let prevK = null; let prevCum = 0; for (const p of perStrike) { const nextCum = cum + p.net; if (prevK !== null && (cum <= 0 && nextCum > 0 || cum >= 0 && nextCum < 0)) { const span = p.strike - prevK; const denom = nextCum - cum; flip = denom === 0 ? p.strike : Number((prevK + span * ((0 - cum) / denom)).toFixed(2)); } prevK = p.strike; prevCum = cum; cum = nextCum; } let posWall = null; let negWall = null; for (const p of perStrike) { if (p.net > 0 && (posWall === null || p.net > posWall.net)) posWall = p; if (p.net < 0 && (negWall === null || p.net < negWall.net)) negWall = p; } return { net, perStrike, flip, posWall, negWall, computed, withOi }; } function nearestStrike(perStrike, level) { let best = null; let bestD = Infinity; for (const p of perStrike) { const d = Math.abs(p.strike - level); if (d < bestD) { bestD = d; best = p; } } return best; } function gexConfidence(p) { if (p.withOi === 0) return 0; const c = p.computed / p.withOi; return c < 0 ? 0 : c > 1 ? 1 : c; } function renderGex(market) { const o = market.options; const head = `gex · ${market.instrument} (MODEL)`; const assume = ` ${GEX_ASSUMPTION} · gex-v1`; if (o === undefined) { return [`${head} · ${DASH} — no options-analytics projection (fail-closed)`, assume].join(` `); } if (o.spotStale || o.spot === null) { const age = o.spotStaleSeconds === null ? "never observed" : `sta