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.

42 lines (41 loc) 2.28 kB
/** * # session/specs — the driver's PURE session-header helpers (META → instruments), factored light * * `requireMeta` / `resolveSpecs` / `specFromMeta` are the sim driver's own header rules: find the * one META a well-formed bus opens with, and resolve the session's {@link InstrumentSpec}s from it. * They were born inside `src/session/sim.ts` and are re-exported from there unchanged; this module * exists so a consumer that must stay OFF the heavy session graph — the CLI's `validate --arm` * context derivation (kestrel-elu9), which is LIGHT/node-runnable — can reuse the EXACT derivation * a real run performs instead of re-implementing a copy that could drift (AGENTS.md: validation * must match execution). Type-only imports; zero runtime dependencies; no `bun:*`, no IO, no clock. */ /** A default {@link InstrumentSpec} from a bus META instrument. Contract facts are not on the v1 * META, so they are DERIVED from the asset class: an `option-underlier` (SPX, SPY, …) gets the * standard listed-option shape — tick `0.01`, strike step `1`, **multiplier `100`** (premiums and * P&L are per-contract-×-100 dollars) — while a non-option underlier keeps multiplier `1`. An * explicit `instruments` override on `RunSimOptions` still wins. */ export 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 } : {}), }; } /** Resolve the session's instrument specs from options + META, failing closed on none. */ export 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; } /** Find the single META header, failing closed on its absence (RUNTIME §8). */ export 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; }