9eb6d6b4f6
Slice 8, closing the #319 retirement's prose and bench debt. Bench argv rides exec (fixed_cost single-run probe, campaign surfaces, and the seed helper that still called the retired sweep at runtime — caught by the bench run itself); all five fingerprints unchanged against the committed baselines, confirming record-line parity through the retirement. Live docs (README, authoring guide, project layout, glossary) describe only exec + campaign documents + graph introspect --params; three glossary entries explicitly mark their verb retired. Ledger amendments per the C29 discipline — C25's executor-verb-set re-settled (exec + the --override residue), C14's dual grammar reduced to the one dispatch, C24's document-built runs/families + gated-intake route list (lockstep with the code comment), C12's override clause and identity anchor repointed to the runner-layer hash computation, C18/C22/C27/C01 mention rewrites — superseded sentences moved verbatim to the history sidecars (c25's created). Stale dual-grammar doc comment on is_blueprint_file rewritten. refs #319
136 lines
8.0 KiB
Markdown
136 lines
8.0 KiB
Markdown
# aura
|
||
|
||
**aura is a "game engine for traders."** It is a Rust framework — and a
|
||
playground — for authoring trading strategies as composable dataflow nodes,
|
||
backtesting them deterministically and massively in parallel, validating them
|
||
(sweep / Monte-Carlo / walk-forward), and freezing a validated strategy into a
|
||
standalone bot with a broker connection. Its differentiator is the **World**: a
|
||
program that constructs and orchestrates whole *families* of backtests, not the
|
||
single run.
|
||
|
||
This README is a discoverability map of the shipped `aura` CLI — *what commands
|
||
exist and what they do*. It deliberately keeps per-flag detail light; the two
|
||
authoritative sources for exact syntax are always:
|
||
|
||
- `aura <command> --help` — the exhaustive flag grammar.
|
||
- The design ledger (`docs/design/INDEX.md`) and glossary (`docs/glossary.md`)
|
||
— the concepts and invariants behind the surface.
|
||
|
||
## Build & run
|
||
|
||
aura is a Cargo workspace. Build everything and you get one binary named `aura`:
|
||
|
||
```sh
|
||
cargo build --workspace # binary at target/debug/aura
|
||
cargo build --workspace --release # optimized, at target/release/aura
|
||
```
|
||
|
||
Invoke it as `aura <command> …` (examples below use the plain name).
|
||
|
||
## Concepts in one breath
|
||
|
||
- **Blueprint** — a serialized signal graph as data: a param-generic
|
||
`price → bias` node graph. A blueprint file (`blueprint.json`) is the unit a
|
||
downstream consumer loads and executes or varies.
|
||
- **Open vs. closed** — a blueprint with unbound (free) numeric knobs is *open*;
|
||
one with every knob bound is *closed*. A plain `exec` wants closed; naming a
|
||
knob as a campaign axis (or an `exec --override`) is what reopens it.
|
||
- **Family** — the set of runs a campaign document's process pipeline produces
|
||
over a blueprint (a sweep grid, a Monte-Carlo seed set, a walk-forward window
|
||
sequence). Families are persisted in a content-addressed store and can be
|
||
listed, ranked, and reproduced.
|
||
- **Axis** — one named, sweepable knob of a blueprint (e.g. `fast.length`),
|
||
bound with a comma-separated value list. A **gang** fuses several sibling
|
||
knobs into one axis (one value drives all members).
|
||
|
||
## Executing a document
|
||
|
||
`aura exec <target>` (#319) is the one executor verb: `<target>` is either a
|
||
loaded **blueprint** file (a single synthetic run) or a **campaign document**
|
||
(a `.json` file or its registered 64-hex content id — instruments × windows ×
|
||
strategy × param axes × process, see `docs/authoring-guide.md` §3). `graph`
|
||
renders a blueprint's structure so a mis-wire is visible before a run.
|
||
|
||
| Command | Purpose |
|
||
|---|---|
|
||
| `aura exec <bp.json>` | Run one backtest of a **closed** blueprint and print its report. An open (free-knob) blueprint is refused with a clean error — bind it, or vary it as a campaign axis. |
|
||
| `aura exec <bp.json> --override <node.param>=<value>` | Reopen one **bound** param for this single execution only (the value is recorded raw in the manifest); repeatable. |
|
||
| `aura exec <bp.json> --tap <name>=<fold>` | Subscribe a declared tap to a fold (`record`/`count`/`sum`/`mean`/`min`/`max`/`first`/`last`) for this run; repeatable. |
|
||
| `aura graph <bp.json>` | **Render** the blueprint's structure as an interactive HTML DAG so a mis-wire is visible before a run. Omit the file to render the built-in sample; a named-but-unreadable file is a usage error. |
|
||
| `aura graph introspect --params <bp.json\|id>` | **Discover** a blueprint's open, sweepable knobs. Prints each as `<name>:<kind>`, bound params trailing `default=<value>`. Run this first to learn the axis names for a campaign document. |
|
||
| `aura exec <campaign.json\|id> [--parallel-instruments <n>]` | Execute a **campaign document** — a grid family, gates, walk-forward, Monte-Carlo, and cross-instrument generalization per its process document, one cell per (strategy, instrument, window). |
|
||
| `aura runs families` | List every persisted family (id, kind, member count). |
|
||
| `aura runs family <id> [rank <metric>]` | List one family's members, optionally ranked best-first by an R metric (e.g. `sqn_normalized`, `expectancy_r`). |
|
||
| `aura reproduce <family-id>` | Re-derive every member of a persisted family from the content-addressed store and check it is bit-identical (the C18/C1 determinism guarantee). |
|
||
|
||
**Important contract — every open knob is mandatory on a campaign axis.** A
|
||
campaign document's `strategies[].axes` must name every open knob
|
||
`graph introspect --params` lists: there is no default value — pin a knob you
|
||
don't want to vary with a single-value axis (`{"kind":"I64","values":[4]}`).
|
||
Omitting one is a clean error naming the missing knob, not a silent default.
|
||
|
||
Use `aura exec --help` for the exact flag grammar, `docs/authoring-guide.md`
|
||
for the campaign document shape (data window, naming, presentation/emit).
|
||
|
||
## Authoring & introspecting topology
|
||
|
||
A blueprint is authored declaratively from a **JSON op-list**: an ordered list
|
||
of by-identifier construction ops replayed against the standard node vocabulary.
|
||
Adding a new node *type* to that vocabulary is Rust, not JSON — see
|
||
`docs/authoring-guide.md`, §0, "Authoring a new node in Rust".
|
||
|
||
| Command | Purpose |
|
||
|---|---|
|
||
| `aura graph build` | Read an op-list document on **stdin**, build the blueprint, and print its canonical JSON to **stdout** (no trailing newline — it is the content-addressed artifact). |
|
||
| `aura graph introspect --vocabulary` | List every node type available to op-lists. |
|
||
| `aura graph introspect --node <T>` | Show one type's input ports, output fields, and param paths (with the bind-value form). |
|
||
| `aura graph introspect --unwired` | Read a *partial* op-list on stdin and list its still-open interior slots. |
|
||
| `aura graph introspect --content-id` | Read an op-list on stdin and print the SHA-256 content id of the blueprint it would build. |
|
||
| `aura graph introspect --identity-id` | Read an op-list on stdin and print the SHA-256 **identity id** — the topology's debug-name-blind id, so two documents (or an op-script and a Rust-built blueprint) expressing the same topology print the same id. Combinable with `--content-id` (both ids, one per line, content id first). |
|
||
|
||
### Op-list shape
|
||
|
||
Each element is one op, tagged by `"op"`. Node params are bound with the **typed
|
||
`Scalar` form** — a tagged, capitalized-kind object: `{"I64": 5}`, `{"F64":
|
||
1.5}`, `{"Bool": true}`. A worked example (an SMA-crossover → bias signal):
|
||
|
||
```json
|
||
[
|
||
{"op":"source","role":"price","kind":"F64"},
|
||
{"op":"add","type":"SMA","name":"fast","bind":{"length":{"I64":2}}},
|
||
{"op":"add","type":"SMA","name":"slow","bind":{"length":{"I64":4}}},
|
||
{"op":"add","type":"Sub"},
|
||
{"op":"add","type":"Bias"},
|
||
{"op":"feed","role":"price","into":["fast.series","slow.series"]},
|
||
{"op":"connect","from":"fast.value","to":"sub.lhs"},
|
||
{"op":"connect","from":"slow.value","to":"sub.rhs"},
|
||
{"op":"connect","from":"sub.value","to":"bias.signal"},
|
||
{"op":"expose","from":"bias.bias","as":"bias"}
|
||
]
|
||
```
|
||
|
||
Piping this document into `aura graph build` emits a `blueprint.json` you can
|
||
then feed to `exec` above:
|
||
|
||
```sh
|
||
aura graph build < crossover.ops.json > crossover.bp.json
|
||
aura graph introspect --params crossover.bp.json
|
||
```
|
||
|
||
The op kinds are `source`, `input`, `add`, `feed`, `connect`, `expose`, `tap`,
|
||
`gang`, `doc`, and `use` (`tap` declares a recorded measurement point on an
|
||
interior wire; `doc` declares the composite's one-line meaning, required at
|
||
register — C29; `use` splices a registered blueprint in as a nested composite,
|
||
by content id or label; `add` additionally takes an `args` object for
|
||
**arg-bearing** types (`Session`, `LinComb`, `CostSum`) — structural,
|
||
non-scalar construction consumed before `bind`; see the authoring guide). See
|
||
`aura graph introspect --node <T>` for a type's exact ports and the op-script
|
||
grammar in the design ledger for the full semantics.
|
||
|
||
## Where to go deeper
|
||
|
||
- `aura <command> --help` — exhaustive, always-current flag grammar.
|
||
- `docs/design/INDEX.md` — the design ledger: architecture, invariants, the World.
|
||
- `docs/glossary.md` — canonical terminology (blueprint, family, axis, sweep /
|
||
Monte-Carlo / walk-forward, R, content-id, …).
|