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.

188 lines (186 loc) 6.06 kB
import { loadHeavy } from "./bin-6676fry4.js"; import { parseArgs, required } from "./bin-1gc4zavq.js"; import { CliError, EXIT } from "./bin-t9ggwnv5.js"; import"./bin-xx1qrxe3.js"; import"./bin-t58k60v9.js"; import"./bin-p5jfg5b0.js"; import"./bin-29be75ss.js"; import"./bin-n77kea5n.js"; import"./bin-mn7wcxhv.js"; import"./bin-8pchjxn2.js"; import"./bin-am3351y2.js"; import"./bin-73jr945c.js"; import"./bin-df1tn094.js"; import"./bin-3ft0k1jq.js"; import { exports_lang } from "./bin-2ywrx58g.js"; import { __require } from "./bin-wckvcay0.js"; // src/cli/commands/paper.ts import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { dirname } from "node:path"; function usageErr(message) { return new CliError({ code: "USAGE", exit: EXIT.USAGE, message }); } function positive(flags, name) { const raw = required(flags, name); const n = Number(raw); if (!Number.isFinite(n) || n <= 0) { throw usageErr(`--${name} must be a positive, finite number, got ${JSON.stringify(raw)}`); } return n; } function optionalPositive(flags, name) { const raw = flags.get(name); if (raw === undefined) return; const n = Number(raw); if (!Number.isFinite(n) || n <= 0) { throw usageErr(`--${name} must be a positive, finite number, got ${JSON.stringify(raw)}`); } return n; } function optionalInt(flags, name) { const raw = flags.get(name); if (raw === undefined) return; const n = Number(raw); if (!Number.isInteger(n)) throw usageErr(`--${name} must be an integer, got ${JSON.stringify(raw)}`); return n; } function loadDocuments(plansPath) { const text = readFileSync(plansPath, "utf8"); return plansPath.endsWith(".md") ? exports_lang.parseMarkdown(text) : [exports_lang.parse(text)]; } var PAPER_FAILURES = new Set([ "mode-gate", "live-port", "live-account", "limits-absent", "documents-absent", "connect-failed", "contract-unresolved", "feed-absent", "feed-stale", "transport-degraded", "killed", "order-refused" ]); async function paperTyped(run) { try { return await run(); } catch (err) { if (err instanceof Error && err.name === "PaperSessionRefused") { const failure = err.failure; if (typeof failure === "string" && PAPER_FAILURES.has(failure)) { throw new CliError({ code: "PAPER_REFUSED", exit: EXIT.PAPER_REFUSED, message: err.message, hint: `paper session refusal kind: ${failure} — fail-closed; nothing was transmitted after this point` }); } } throw err; } } async function paperCommand(argv, ctx) { const { flags, bools } = parseArgs(argv, new Set(["live-market-data"]), new Set([ "instrument", "plans", "session-date", "r-usd", "max-order-qty", "max-position-qty", "max-notional-usd", "budget-usd", "expiry", "spot", "half-width", "tau-hours", "tolerance", "cycles", "pump-ms", "host", "port", "client-id", "account", "out" ])); const instrument = required(flags, "instrument"); const plansPath = required(flags, "plans"); const sessionDate = required(flags, "session-date"); const rUsd = positive(flags, "r-usd"); const limits = { maxOrderQty: positive(flags, "max-order-qty"), maxPositionQty: positive(flags, "max-position-qty"), maxNotionalUsd: positive(flags, "max-notional-usd") }; const expiry = flags.get("expiry"); const spot = optionalPositive(flags, "spot"); if (expiry !== undefined && spot === undefined) { throw usageErr("--expiry needs --spot: a strike window must be centred on an OBSERVED underlier price, and a centre is never guessed (kestrel-7o2.19)"); } const tauHours = optionalPositive(flags, "tau-hours"); const paper = await loadHeavy(() => import("./heavy-v95qw596.js")); const result = await paperTyped(() => paper.runPaperSession({ instrument, sessionDate, documents: loadDocuments(plansPath), limits, rUsd, cycles: optionalInt(flags, "cycles") ?? 60, ...optionalInt(flags, "pump-ms") !== undefined ? { pumpMs: optionalInt(flags, "pump-ms") } : {}, ...optionalPositive(flags, "budget-usd") !== undefined ? { budgetUsd: optionalPositive(flags, "budget-usd") } : {}, ...expiry !== undefined ? { expiry } : {}, ...spot !== undefined ? { spot } : {}, ...optionalPositive(flags, "half-width") !== undefined ? { halfWidth: optionalPositive(flags, "half-width") } : {}, ...tauHours !== undefined ? { tauYears: tauHours / (365 * 24) } : {}, ...optionalPositive(flags, "tolerance") !== undefined ? { tolerance: optionalPositive(flags, "tolerance") } : {}, ...bools.has("live-market-data") ? { liveMarketData: true } : {}, gateway: { ...flags.get("host") !== undefined ? { host: flags.get("host") } : {}, ...optionalInt(flags, "port") !== undefined ? { port: optionalInt(flags, "port") } : {}, ...optionalInt(flags, "client-id") !== undefined ? { clientId: optionalInt(flags, "client-id") } : {}, ...flags.get("account") !== undefined ? { account: flags.get("account") } : {} } })); const out = flags.get("out"); if (out !== undefined) { mkdirSync(dirname(out), { recursive: true }); writeFileSync(out, JSON.stringify({ events: result.events, ledger: result.ledger, positions: result.positions }, null, 2)); } const fills = result.events.filter((e) => e.stream === "ORDER" && e.type === "fill").length; const summary = { schema: "paper-session/v1", mode: "paper", instrument, cycles: result.cycles, events: result.events.length, orders: result.ledger.length, fills, positions: result.positions, feed_health: result.watermark.health }; if (ctx.mode === "json") process.stdout.write(JSON.stringify(summary) + ` `); else process.stdout.write(`paper ${instrument} cycles=${result.cycles} events=${result.events.length} orders=${result.ledger.length} fills=${fills} feed=${result.watermark.health} `); return 0; } export { paperCommand };