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.

200 lines (198 loc) 8.87 kB
// src/cli/render/sim.ts function money(n) { const r = Math.round(n * 1e6) / 1e6; return `$${r}`; } function simStoryLines(story) { const lines = []; lines.push(`kestrel sim · ${story.canonicalSlug}`); if (story.viaAlias) lines.push(`alias ${story.requestedSlug}${story.canonicalSlug}`); lines.push(`scenario ${story.title}${story.instrument} · ${story.period.start}..${story.period.end} · ${story.frameCount} frames`); lines.push(`briefing ${story.instrument} percept context available (${story.frameCount} frames)`); lines.push(`strategy ${story.strategyLabel}`); lines.push(`operation ${story.operation.id} · ${story.operation.status}`); const budget = story.receipts.budgetRemaining === null ? "—" : money(story.receipts.budgetRemaining); lines.push(`receipts ${story.receipts.meteredCount} metered · budget remaining ${budget}`); lines.push(`evidence manifest=${story.evidence.manifestRef.artifact_id} bus=${story.evidence.busRef.artifact_id} grade=${story.evidence.gradeRef.artifact_id} calls=${story.evidence.callRefs.length}`); if (story.metrics === null) { lines.push(`grade settling — retry shortly`); lines.push(`proof ${story.proofUrl} — settling — retry shortly`); } else { lines.push(`grade order_count=${story.metrics.orderCount} fill_count=${story.metrics.fillCount} realized_pnl=${story.metrics.realizedPnl}`); lines.push(`proof ${story.proofUrl}`); } return lines; } function renderSimStory(story, _ctx) { process.stdout.write(simStoryLines(story).join(` `) + ` `); } function neverFiredLine(reason) { return `why no orders fired — ${reason}`; } function planNeverFiredReason(plan, orders) { const fired = plan.lifecycle.some((s) => s.state === "fired" || s.state === "managing"); if (fired) return; const placed = orders.some((o) => o.plan_instance === plan.plan_instance); if (placed) return; for (let i = plan.lifecycle.length - 1;i >= 0; i--) { const reason = plan.lifecycle[i].reason; if (reason !== undefined) return reason.startsWith("ttl — ") ? reason.slice("ttl — ".length) : reason; } return; } function neverFiredDigestLines(report) { const lines = []; for (const p of report.plans) { const why = planNeverFiredReason(p, report.orders); if (why !== undefined) lines.push(`why ${p.plan} placed no orders — ${why}`); } return lines; } function demoLessonObject(lesson) { return { bundled_example: true, plan_label: lesson.planLabel, plan_source: lesson.planSource, swap_in: lesson.swapInCli }; } function demoLessonLines(lesson) { const lines = []; lines.push(""); lines.push(`demo plan ${lesson.planLabel}`); lines.push(" this run used a BUNDLED EXAMPLE plan, not your strategy — it is what makes the"); lines.push(" grade above a real, filled trade instead of an empty ($0, no-orders) session:"); for (const l of lesson.planSource.split(` `)) lines.push(` ${l}`); lines.push(" the `grade` line above is exactly what this example did (a naive one-share"); lines.push(" buy-and-hold — an honest result, never dressed up as alpha). author your own:"); lines.push(` ${lesson.swapInCli}`); return lines; } function renderDemoLesson(lesson) { return demoLessonLines(lesson).join(` `); } function standDownLessonObject(lesson) { return { stand_down_demo: true, reason: "no --plans supplied — this run used the labeled stand-down baseline, which arms no orders", example_plan_source: lesson.examplePlanSource, swap_in: lesson.swapInCli, prove: lesson.proveCli }; } function standDownLessonLines(lesson) { const lines = []; lines.push(""); lines.push("stand-down no --plans supplied — this run used the labeled stand-down baseline BY DESIGN"); lines.push(" nothing is broken: with no strategy authored there is nothing to arm, so the grade"); lines.push(" above is an honest $0 / no-orders result — a plan-less stand-down, not a failure."); lines.push(" author a strategy and pass it with --plans to place real orders. a minimal example:"); for (const l of lesson.examplePlanSource.split(` `)) lines.push(` ${l}`); lines.push(" save that to a file, then run it on this scenario:"); lines.push(` ${lesson.swapInCli}`); lines.push(" or run a bundled example that trades out of the box, no plan to write:"); lines.push(` ${lesson.proveCli}`); return lines; } function renderStandDownLesson(lesson) { return standDownLessonLines(lesson).join(` `); } function paidBoundaryNudgeObject(nudge) { return { free_run: true, reason: "this run was free — nothing was owed; the paid boundary (a signed, settleable Offer) is opt-in via --budget", reach_paid_boundary: `npx kestrel.markets sim ${nudge.scenarioSlug} --budget 0` }; } function paidBoundaryNudgeLines(nudge) { const lines = []; lines.push(""); lines.push("paid path this run was FREE — metered against the anonymous trial, nothing owed, no card"); lines.push(" the paid boundary certifies a run against real spend: pass --budget <amount> and a metered"); lines.push(" run pauses at the Spend boundary and returns the price as a signed, settleable Offer (data,"); lines.push(" never auto-charged) — the same 402 an agent reaches. see it on this scenario:"); lines.push(` npx kestrel.markets sim ${nudge.scenarioSlug} --budget 0`); return lines; } function renderPaidBoundaryNudge(nudge) { return paidBoundaryNudgeLines(nudge).join(` `); } function renderSimMenu(menu, _ctx) { const lines = []; const allFree = menu.scenarios.length > 0 && menu.scenarios.every((s) => s.free); lines.push(`kestrel sim — run a curated market scenario${allFree ? ", free (anonymous trial)" : ""}`); lines.push("usage: kestrel sim <scenario-slug> [--plans <file>]"); lines.push(""); lines.push("scenarios:"); const wSlug = Math.max(8, ...menu.scenarios.map((s) => s.slug.length)); const wTitle = Math.max(5, ...menu.scenarios.map((s) => s.title.length)); for (const s of menu.scenarios) { const price = s.free ? "free" : "paid"; lines.push(` ${s.slug.padEnd(wSlug)} ${s.title.padEnd(wTitle)} ${s.instrument.padEnd(5)} ${price} ${s.artifactId}`); } const aliasKeys = Object.keys(menu.aliases); if (aliasKeys.length > 0) { lines.push(""); lines.push("aliases:"); for (const k of aliasKeys) lines.push(` ${k}${menu.aliases[k]}`); } lines.push(""); const first = menu.scenarios[0]?.slug ?? "meme-stock-short-squeeze"; lines.push(`run one: kestrel sim ${first}`); process.stdout.write(lines.join(` `) + ` `); } function sessionReportLines(report) { const s = report.session; const t = report.totals; const lines = []; lines.push(`kestrel session · ${s.date} · ${s.instruments.join(",")} · ${s.mode} · ${s.fill_model}`); const h = t.headline; const chans = [`floor=${money(t.realized_floor_usd)}`, `expected=${money(t.expected_usd)}`]; if (t.sampled_usd !== undefined) chans.push(`sampled=${money(t.sampled_usd)}`); lines.push(`grade headline=${h.channel} ${money(h.usd)} · ${chans.join(" ")}`); if (h.tainted) lines.push(` tainted ${h.reasons.join("; ")}`); const m = t.settle_mark; const staleTag = m.stale ? " STALE" : ""; const uncertainTag = m.mark_uncertain ? " mark_uncertain" : ""; lines.push(`settle mark=${money(m.px)} source=${m.source}${staleTag}${uncertainTag} · premium_spent=${money(t.premium_spent)}`); lines.push(`plans ${report.plans.length}`); for (const p of report.plans) { const arc = p.lifecycle.map((step) => step.outcome !== undefined ? `${step.state}(${step.outcome}${step.reason !== undefined ? `:${step.reason}` : ""})` : step.state).join(" → "); lines.push(` ${p.plan} ${arc === "" ? "—" : arc}${p.final_state}`); const why = planNeverFiredReason(p, report.orders); if (why !== undefined) lines.push(` why ${why}`); } lines.push(`orders ${report.orders.length}`); for (const o of report.orders) { const leg = o.strike !== undefined && o.right !== undefined ? `${o.strike}${o.right}` : "SPOT"; const outcome = o.filled ? `filled@${o.fillPx} floorPnl=${money(o.floorPnl)}` : o.cancelled === true ? "cancelled" : `resting pFill=${o.pFill}`; lines.push(` ${o.side} ${o.qty} ${o.instrument} ${leg} @ ${o.px} ${o.role} ${outcome} (${o.annotation})`); } return lines; } function renderEpisodeReport(report, _ctx) { process.stdout.write(sessionReportLines(report).join(` `) + ` `); } export { renderSimStory, neverFiredLine, neverFiredDigestLines, demoLessonObject, renderDemoLesson, standDownLessonObject, renderStandDownLesson, paidBoundaryNudgeObject, renderPaidBoundaryNudge, renderSimMenu, renderEpisodeReport };