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 (151 loc) 9.11 kB
# Kestrel examples A handful of things you can run in a minute, all on generic tickers (SPX / SPY). Each is a real invocation of the shipped CLI or library — copy, paste, run. Every command is self-documenting: `kestrel --help` lists the commands, the global flags, and the [exit-code taxonomy](#exit-codes); `kestrel <command> --help` (e.g. `kestrel run --help`, `kestrel runs list --help`) prints that one command's own usage and flags. ## Setup **From a clone of this repo** (build once, then use `node dist/cli.js` as the CLI): ```bash bun install && bun run build ``` **As an installed dependency**`npm i kestrel.markets` gives you the `kestrel` binary (swap `node dist/cli.js` for `kestrel` / `npx kestrel` below) and the `kestrel.markets` / `kestrel.markets/lang` library imports. --- ## 1. Round-trip a plan — `momentum-breakout.kestrel` Parse a plan into the typed object model and print it back in canonical form. The output is byte-stable: `print(parse(text))` is a fixed point (ADR-0004). ```bash node dist/cli.js print examples/momentum-breakout.kestrel ``` Prints the same plan back, canonicalized. `parse` (or its alias `validate`) checks it without printing. ## 2. Watch it fail closed — `reject-exit-on-mark.kestrel` An active `EXIT` may not condition on the mark — a quote is a health signal, never a value (ADR-0005). The parser refuses this at parse time and exits nonzero, instead of arming a strategy that trusts a lying mark. This holds whether the mark is spelled with the exchange jargon (`mid`/`bid`/`ask`/`last`) or with the plain-English words a written playbook uses for the option quote (`mark`/`premium`, as in "close at 2x premium") — condition the exit on `fair`, `spot`, or a structural level instead. ```bash node dist/cli.js parse examples/reject-exit-on-mark.kestrel; echo "exit=$?" ``` ``` error code=PARSE message=EXIT may not condition on the mark `mid`: marks lie ... (line 9, col 19) exit=2 ``` ## 3. See the market as text — `briefing.json` + `wake.json` Render a **Frame** — the same market picture you'd see on a terminal, laid out for a context window instead of a screen. The tape is vertical (one row per candle, newest last) so an agent that watches all day pays only for the new bar. ```bash node dist/cli.js frame examples/briefing.json ``` ``` KESTREL · OPEN briefing · T-92m to close · regular · 13:24 ET ... tape 5m · axis 505.90→521.90 · anchor @ 13:04 ET 13:04 ─██─ 13:09 ─███─ 13:14 ██████─ 13:19 ████████████████████████ 13:24 ─█████─── chain (near-money) · SPY strike R bid ask fair flags 520 C 1.70 2.05 1.84 b76 nLiq=6 — ``` Add `--json` to get the ASCII transported under a `kestrel.frame/v1` schema. ### The two frame-input kinds `frame` reads a single JSON object (a path argument, `--input <p>`, or `-` / stdin) and infers its **kind** from the shape, then renders it. `--kind briefing|wake` overrides the inference. - **`briefing`** — the OPEN keyframe, a full market picture (`examples/briefing.json`). - **`wake`** — a WAKE delta: everything **since the last vantage** — the tape buckets since last, the current levels/kernel, and the wake's own identity. It is inferred whenever the object carries a `wakeIndex` (`examples/wake.json`): ```bash node dist/cli.js frame examples/wake.json --json | jq .kind # "wake" ``` The two shapes share a `market` pane (`instrument`, `levels`, `tape`, `tapeBucketMin`, `chain`) and an acting `kernel` (`positions`, `resting`, `fillsSinceLast`, optional `budget`, `plans`). A `briefing` adds `timeToCloseMin` + `instruments`; a `wake` adds `wakeIndex`, `minutesSinceLast`, and an optional `wakeReason`. The full field list is `src/frame/types.ts` (`BriefingInput` / `WakeDeltaInput`). Two invariants hold across both kinds: - **Date-blind by construction.** There is no `session_date`, day-of-week, or epoch-millisecond field anywhere. Time is carried only as `timeToCloseMin` (relative), `wakeIndex` + `minutesSinceLast`, and HH:MM `clockET` strings — never an absolute date. - **Unknowns are explicit.** A genuinely unavailable number is `null` (or an omitted optional) and renders as an explicit `—`; the renderer never guesses or defaults a value. A non-object input, or malformed JSON, is a loud `USAGE` (exit 2); a missing input file is `NOT_FOUND` (exit 3) — see [exit codes](#exit-codes). ## 4. Use the library — `roundtrip.mjs` The text DSL and the typed object model are the same language: `parse` text to objects, `print` objects back to byte-identical text. ```bash node examples/roundtrip.mjs ``` ```js import { parse, print } from "kestrel.markets/lang"; const doc = parse(source); print(parse(print(doc))) === print(doc); // true ``` ## 5. Drive the managed API — `client-mint-sim-proof.mjs` The SDK face (`kestrel.markets/client`, one of the four equal faces — ADR-0004). Walks the day-one flow — **mint** a trial capability, **validate**, **sim** (streaming the operation to a certified Blotter), **grade** it, then hit the **paid boundary** and watch the 402/Offer arrive as data with the free proof already earned. Runs offline against the in-process contract server the client ships — no secrets. ```bash node examples/client-mint-sim-proof.mjs # in-process contract mock (default) KESTREL_API=https://api.kestrel.markets node examples/client-mint-sim-proof.mjs # live ``` ``` 1. minted trial capability kind=trial scopes=[data, sim, grade] 3. sim operation=op_sim_1 state=open events=[operation.started → operation.artifact → operation.completed] blotter (receipt) sessionId=bl_mock_1 4. grade subject=dh_mock metrics: bankable_ev=27.3 fill_support=0.61 5. paid boundary 402/Offer (surfaced as data, not a redirect) amount=25.00 USDC proof: https://kestrel.markets/proof/op_sim_1 ``` ## 6. Adopt a held leg across a wake — `plan-acquire-open.kestrel` + `plan-manage-adopt.kestrel` + `adopt-cross.jsonl` The exit escape hatch, end to end. `adopt-cross.jsonl` is a short SPY bus whose spot crosses **100** in the morning (so `acquire` fills a 100C) then **114** in the afternoon (so the adopted `EXIT` rests). The opener stages as the day's plan; the adopter is the wake reply that supersedes it and carries the held leg over with `ARM … foreach held leg`, so its covered sell can close the position the replacement plan never opened. `kestrel card adopt` walks the whole loop. ```bash mkdir -p ./run cp examples/plan-acquire-open.kestrel ./run/plans-0.kestrel cp examples/plan-manage-adopt.kestrel ./run/revision-0.kestrel bun dist/cli.js day --bus examples/adopt-cross.jsonl --dir ./run --fill strict-cross-v1 --r-usd 1000 --wakes 12:00 ``` Ends `plans_armed=2 … orders_filled=2``acquire` buys the 100C, `manage` adopts it at basis 0.90 and its covered `EXIT` sells it: fill → adopt → exit, with no naked hole at any step. --- ## Exit codes Every command fails **closed and loud**: on error it writes a structured line to **stderr** (stdout stays a pure payload channel) and returns a nonzero exit code. The error carries a stable `code` string — agents match on the `code`, not on prose, and the codes are stable across releases. This taxonomy mirrors `EXIT` in `src/cli/errors.ts`; `kestrel --help` prints it too. | exit | `code` | when | | ---: | --------------------- | ------------------------------------------------------------------------------------- | | 0 | `OK` | the command succeeded | | 1 | `GENERIC` | an unexpected/uncaught error (`KESTREL_DEBUG=1` also dumps the stack) | | 2 | `USAGE` | bad/unknown flag, missing required flag, bad `--format`, or a parse failure (`PARSE`) | | 3 | `NOT_FOUND` | `runs show <id>` / `lineage <name>` / an input file is absent | | 4 | `RUNTIME_UNAVAILABLE` | bun/chdb is needed but could not be loaded | | 5 | `PAYMENT_REQUIRED` | a remote verb returned a 402/Offer (surfaced as data) | | 130 | `SIGINT` | interrupted (Ctrl-C) | A parse failure raised by the language layer surfaces the `code: "PARSE"` inside the `USAGE` exit bucket (exit 2) — see example 2 above. Under `--json`, the error is a `{ "error": { "code", "message", "hint"? } }` object on stderr; under `text`, a `error\tcode=…\tmessage=…` line. --- What each feature actually does today — graded per capability across **syntax · runtime · evidence · access**, with source and test receipts — is tracked honestly in [`../docs/public/status.md`](../docs/public/status.md).