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.

340 lines (333 loc) 10.8 kB
// src/protocol/support.ts function isCalibratedSupport(support) { return support === "calibrated"; } function partition(cells) { let calGain = 0; let calLoss = 0; let extGain = 0; let extLoss = 0; for (const cell of cells) { if (isCalibratedSupport(cell.support)) { if (cell.ev >= 0) calGain += cell.ev; else calLoss += cell.ev; } else { if (cell.ev >= 0) extGain += cell.ev; else extLoss += cell.ev; } } return { calibrated: { gain: calGain, loss: calLoss }, extrapolated: { gain: extGain, loss: extLoss } }; } function bankableEvOf(p) { return p.calibrated.gain + p.calibrated.loss + p.extrapolated.loss; } // src/protocol/grade.ts var REPORT_GRID = 1e8; function roundToGrid(x) { return Math.round(x * REPORT_GRID) / REPORT_GRID; } var round = roundToGrid; function partitionOf(claims) { const cal = claims.find((c) => c.support === "calibrated"); const ext = claims.find((c) => c.support === "extrapolated"); return { calibrated: { gain: cal?.gain ?? 0, loss: cal?.loss ?? 0 }, extrapolated: { gain: ext?.gain ?? 0, loss: ext?.loss ?? 0 } }; } function hasCertifiedEvidence(b) { return b.totals !== undefined && b.fill_claim !== undefined; } function grade(blotters) { const subjectSessionId = blotters.map((b) => b.sessionId).sort().join("+"); let orderCount = 0; let fillCount = 0; let positionCount = 0; let realizedPnl = 0; let grossTradedQty = 0; let filledNotional = 0; for (const b of blotters) { orderCount += b.orders.length; fillCount += b.fills.length; positionCount += b.positions.length; realizedPnl += b.settlement.realized; for (const f of b.fills) { grossTradedQty += Math.abs(f.qty); filledNotional += Math.abs(f.qty * f.price); } } const metrics = { realized_pnl: round(realizedPnl), fill_rate: orderCount > 0 ? round(fillCount / orderCount) : 0, order_count: orderCount, fill_count: fillCount, position_count: positionCount, gross_traded_qty: round(grossTradedQty), filled_notional: round(filledNotional) }; const missing = blotters.filter((b) => !hasCertifiedEvidence(b)).map((b) => b.sessionId); if (missing.length === 0) { let floor = 0; let expected = 0; let bankable = 0; for (const b of blotters) { floor += b.totals.floor; expected += b.totals.expected; bankable += bankableEvOf(partitionOf(b.fill_claim)); } metrics.realized_floor_usd = round(floor); metrics.expected_usd = round(expected); metrics.bankable_ev = round(bankable); return { subjectSessionId, metrics }; } const named = missing.length === 0 ? "the set" : [...missing].sort().join(", "); return { subjectSessionId, metrics, labels: { risk_honest: "unknown", risk_honest_reason: `certified evidence (totals + fill_claim) absent on: ${named} — risk-honest metrics refused, never a silent zero (kestrel-h314 D6)` } }; } // src/protocol/index.ts var PROTOCOL_VERSION = "0.4"; var FACES = ["http", "sdk", "cli", "mcp"]; // src/bus/types.ts var BUS_SCHEMA = 6; var SUPPORTED_BUS_SCHEMAS = new Set([1, 2, 3, 4, 5, 6]); var STREAMS = new Set([ "META", "TICK", "DETECTOR", "PLAN", "ORDER", "WAKE", "CONTROL", "REGIME", "JOURNAL", "TELEMETRY" ]); var CORPUS_TIERS = ["public", "semi-private", "private"]; var JOURNAL_KINDS = new Set(["author", "debrief", "note"]); var TELEMETRY_TYPES = new Set(["order", "settle", "guard", "hazard", "settle_mark", "theta_bleed"]); function foldBook(ev, prior) { let legs; if (prior === undefined || prior.instrument !== ev.instrument || prior.legs.length === 0) { legs = ev.legs; } else { const merged = new Map; for (const l of prior.legs) merged.set(`${l.strike}:${l.right}`, l); for (const l of ev.legs) merged.set(`${l.strike}:${l.right}`, l); legs = [...merged.values()]; } return { instrument: ev.instrument, underlier_px: ev.underlier_px, ...ev.expiry !== undefined ? { expiry: ev.expiry } : {}, legs, asof_seq: ev.seq, asof_ts: ev.ts }; } function isStream(s) { return typeof s === "string" && STREAMS.has(s); } var TICK_TYPES = new Set([ "SPOT", "BOOK", "HEARTBEAT" ]); var ORDER_ACTIONS = new Set([ "place", "cancel", "fill", "reject" ]); var WAKE_KINDS = new Set([ "wake", "coalesced", "downgraded" ]); var DELIBERATION_TYPE = "deliberation"; function isValidStreamType(stream, type) { switch (stream) { case "META": return type === "session"; case "TICK": return TICK_TYPES.has(type); case "DETECTOR": return type.length > 0; case "PLAN": return type === "lifecycle"; case "ORDER": return ORDER_ACTIONS.has(type); case "WAKE": return WAKE_KINDS.has(type) || type === DELIBERATION_TYPE; case "CONTROL": return type.length > 0; case "REGIME": return type === "tag"; case "TELEMETRY": return type.length > 0; default: return false; } } // src/bus/read.ts import { readFileSync } from "node:fs"; var defaultSkipSink = (reason, line) => { console.warn(`bus skip at line ${line}: ${reason}`); }; class BusReadError extends Error { line; constructor(message, line) { super(`bus corruption at line ${line}: ${message}`); this.name = "BusReadError"; this.line = line; } } function looksLikeContent(source) { return source.trimStart().startsWith("{"); } function validate(obj, lineNo) { if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { throw new BusReadError("event is not a JSON object", lineNo); } const e = obj; if (typeof e["seq"] !== "number" || !Number.isFinite(e["seq"])) { throw new BusReadError("missing/invalid numeric `seq`", lineNo); } if (typeof e["ts"] !== "number" || !Number.isFinite(e["ts"])) { throw new BusReadError("missing/invalid numeric `ts`", lineNo); } const stream = e["stream"]; if (!isStream(stream)) { throw new BusReadError(`unknown stream ${JSON.stringify(stream)}`, lineNo); } if (stream === "JOURNAL") { if (typeof e["kind"] !== "string") { throw new BusReadError("JOURNAL missing/invalid string `kind`", lineNo); } if (typeof e["body"] !== "string") { throw new BusReadError("JOURNAL missing/invalid string `body`", lineNo); } return obj; } const type = e["type"]; if (typeof type !== "string") { throw new BusReadError("missing/invalid string `type`", lineNo); } if (!isValidStreamType(stream, type)) { throw new BusReadError(`invalid (stream,type) pairing ${JSON.stringify(stream)}/${JSON.stringify(type)}`, lineNo); } if (stream === "META" && !SUPPORTED_BUS_SCHEMAS.has(e["bus_schema"])) { throw new BusReadError(`unsupported bus_schema ${JSON.stringify(e["bus_schema"])} (reader supports ${[...SUPPORTED_BUS_SCHEMAS].join(", ")})`, lineNo); } if (stream === "WAKE" && type === DELIBERATION_TYPE) { const intField = (name) => { const v = e[name]; if (typeof v !== "number" || !Number.isInteger(v)) { throw new BusReadError(`deliberation record: missing/non-integer \`${name}\``, lineNo); } return v; }; if (intField("wake_seq") < 0) throw new BusReadError("deliberation record: negative `wake_seq`", lineNo); if (intField("measured_ms") < 0) throw new BusReadError("deliberation record: negative `measured_ms`", lineNo); if (intField("buffer_ms") < 0) throw new BusReadError("deliberation record: negative `buffer_ms`", lineNo); } return obj; } function* readBusText(text, opts) { if (text.length === 0) return; const onSkip = opts?.onSkip ?? defaultSkipSink; const endsWithNewline = text.endsWith(` `); const segments = text.split(` `); if (endsWithNewline) segments.pop(); let expectedSeq = 0; let sawMeta = false; let metaSchema = 0; const checkpointTsBySeq = new Map; for (let i = 0;i < segments.length; i++) { const raw = segments[i] ?? ""; const lineNo = i + 1; const isFinalSegment = i === segments.length - 1; const torniable = isFinalSegment && !endsWithNewline; if (raw.trim() === "") { if (torniable) return; throw new BusReadError("unexpected blank line", lineNo); } let parsed; try { parsed = JSON.parse(raw); } catch { if (torniable) return; throw new BusReadError("unparseable JSON", lineNo); } const event = validate(parsed, lineNo); if (!sawMeta) { if (event.stream !== "META") { throw new BusReadError("first event must be the META session header (a well-formed bus opens with exactly one META)", lineNo); } sawMeta = true; metaSchema = event.bus_schema; } else if (event.stream === "META") { throw new BusReadError("a second META event (a well-formed bus opens with exactly one META)", lineNo); } if (event.seq !== expectedSeq) { throw new BusReadError(`non-monotonic seq: expected ${expectedSeq}, got ${event.seq} (seq must be gap-free from 0)`, lineNo); } expectedSeq += 1; if (event.stream === "WAKE") { if (event.type === "wake" && event.wake === "checkpoint") { checkpointTsBySeq.set(event.seq, event.ts); } else if (event.type === DELIBERATION_TYPE) { if (metaSchema < 6) { throw new BusReadError(`a deliberation record on a bus stamped bus_schema ${metaSchema} — a clocked record on a pre-clocked (<6) bus is corrupt (clock-honest wakes §1)`, lineNo); } const d = event; const cpTs = checkpointTsBySeq.get(d.wake_seq); if (cpTs === undefined) { throw new BusReadError(`deliberation record: dangling wake_seq ${d.wake_seq} — it must name an EARLIER WAKE checkpoint on this bus`, lineNo); } if (d.ts !== cpTs + d.measured_ms + d.buffer_ms) { throw new BusReadError(`deliberation record: ts ${d.ts} breaks the return-time identity checkpoint.ts + measured_ms + buffer_ms = ${cpTs + d.measured_ms + d.buffer_ms}`, lineNo); } } } if (event.stream === "JOURNAL" && !JOURNAL_KINDS.has(event.kind)) { onSkip(`unknown JOURNAL kind ${JSON.stringify(event.kind)}`, lineNo); continue; } yield event; } } function* readBusFile(path, opts) { yield* readBusText(readFileSync(path, "utf8"), opts); } function* readBus(source, opts) { if (looksLikeContent(source)) { yield* readBusText(source, opts); } else { yield* readBusFile(source, opts); } } export { isCalibratedSupport, partition, roundToGrid, grade, PROTOCOL_VERSION, FACES, BUS_SCHEMA, CORPUS_TIERS, foldBook, readBusText, readBus };