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
JavaScript
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
};