From 98be64ebdf8fcdee497528d886c2c7e59f9a86f3 Mon Sep 17 00:00:00 2001 From: Brummel Date: Wed, 17 Jun 2026 19:06:11 +0200 Subject: [PATCH] spec(0051): strategy as a World-consumable Composite blueprint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Milestone spec: ship the GER40 session-breakout as a Composite blueprint so the same strategy runs across backtest / sweep / walk_forward / compare without re-authoring (the #94 fieldtest friction). Records the design decision that the bar period is a structural axis (a construction arg binding both Resample and Session, so a sweep cannot desync them — #96) and that the canonical shippable strategy form is a Composite (FlatGraph is its compiled substrate). Authoring- side; no engine-core change (the existing bind() + GraphBuilder + param_space() layer already suffices). refs #94, #96, #97 --- .../specs/0051-strategy-as-world-blueprint.md | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 docs/specs/0051-strategy-as-world-blueprint.md diff --git a/docs/specs/0051-strategy-as-world-blueprint.md b/docs/specs/0051-strategy-as-world-blueprint.md new file mode 100644 index 0000000..f57e232 --- /dev/null +++ b/docs/specs/0051-strategy-as-world-blueprint.md @@ -0,0 +1,138 @@ +# 0051 — Strategy as a World-consumable Composite blueprint (GER40 breakout) + +Status: draft (milestone spec) +Driver: the GER40 research deep-dive fieldtest +(`docs/specs/fieldtest-research-breakout-deepdive.md`) found that the shipped +real-data session-breakout (commit `5b5f034`) ships as a hand-wired raw +`FlatGraph`, which the World families (`sweep` / `walk_forward` / `optimize` / +compare) **cannot consume** — they need a `Composite` with `param_space()` + +`bootstrap_with_cells`. Every World task forced a researcher to re-author the +13-node graph by hand. This milestone makes the flagship strategy World-ready. + +Tracker: closes #94, #96, #97 (milestone "GER40 breakout as a World-consumable +Composite blueprint"). + +## 1. Goal & non-goal + +**Goal.** Ship the GER40 session-breakout as a reusable **`Composite` blueprint** +so the *same* strategy runs across single backtest, parameter sweep, +walk-forward and multi-instrument/timeframe comparison **without re-authoring**. +Establish (and record in the ledger) that the canonical *shippable* form of a +strategy is a `Composite` blueprint; the `FlatGraph` is its compiled substrate +(C11/C23), not the authoring or shipping form. + +**Authoring-side only — no engine-core change.** The recon +(`crates/aura-engine/src/blueprint.rs`, `builder.rs`, `node.rs`) confirms the +authoring + binding layer already supports everything this needs: +`GraphBuilder` / `Composite::new`, `param_space()` derivation, and +`PrimitiveBuilder::bind(slot, value)` (removes a param from the space). No new +engine mechanism is built here. + +**Non-goal.** +- The **generic param fan-out** feature (one blueprint param → multiple node + slots; recon §3 verdict: does not exist) is **explicitly not built**. The + bar-period is a structural axis (see §2), so fan-out is unnecessary for this + strategy. The capability stays an open idea for a future, genuinely-coupled + *sweep* param. +- The **process-residency bug** (#95) is a separate debug thread (likely + data-server chunk retention), not this milestone. +- **Per-instrument pip registry** (#22) and the **ns→ms inverse** (#80) stay as + filed; the compare demo hand-supplies `pip_size = 1.0` for both index + instruments (honest by construction — both quote in index points). +- No `aura new` / cdylib / deploy (C9 Part-B still deferred). No new nodes. + +## 2. The design decision: bar-period is a structural axis, not a sweep param + +The bar period lives in two nodes — `Resample` exposes `period_minutes` as a +tunable param; `Session` bakes its period (`session.rs:66-76`, `params: vec![]`). +Sweeping one while the other stays baked silently desyncs to 0.0 pips (#96). + +**Decision:** the blueprint takes `bar_period_minutes` as a **construction +argument** that binds *both* nodes at authoring time — +`Resample::builder().bind("period_minutes", bar_period)` **and** +`Session::new(open_hour, open_minute, tz, bar_period)` — so the two periods are +**locked equal by construction** and a desync is structurally impossible. + +Rationale (substance, not effort): +- **C34 (deform vs tune).** A 15m-breakout and a 30m-breakout are *different + strategies*, not one strategy tuned — the bar period deforms, it does not + tune. Deformers do not belong in `param_space()`. +- **C11 (structural axes = experiment matrix; tuning params = sweep).** The + timeframe is a matrix axis (instantiate the blueprint per period and compare), + the entry/exit bar indices are the sweep *within* each. + +That this also avoids the net-new fan-out feature is a tiebreaker, not the +reason. + +`delay.lag` (the "previous bar's high" register — a structural constant, the +exact C34 case) is likewise **bound out**: `Delay::builder().bind("lag", 1)`, +removing it from the space. + +**Result:** `param_space() == { entry_bar.target, exit_bar.target }` — exactly +the two genuine tuning knobs (the `EqConst` targets, named `entry_bar` / +`exit_bar`). This resolves #96 (no desync, no `delay.lag` leak) and makes #97's +`walk_forward` non-degenerate (a real, non-empty space to optimize per window). + +## 3. The blueprint + +A factory in the example's shared module (C9: pure composition of shipped +`aura-std` nodes → lives in `aura/examples`, not a separate crate): + +``` +ger40_breakout_blueprint(bar_period_minutes, open_hour, open_minute, tz) + -> Composite +``` + +- Internally the 13-node breakout (the proven wiring), authored via + `GraphBuilder` (the fluent, name-addressed form; canonical template + `crates/aura-engine/src/test_fixtures.rs` SMA-cross). +- `bar_period_minutes` binds Resample's period and constructs Session (§2). +- `delay.lag` bound to 1 (§2). +- Four OHLC **source roles** (open, high, low, close) in the C4 merge order, + fanning into Resample's `Barrier(0)` slots. +- `param_space() = { entry_bar.target, exit_bar.target }`. +- One exposed output (the SimBroker equity) + the recorder taps as today. + +## 4. Deliverables + +1. The blueprint factory (above), replacing the hand-wired `FlatGraph` as the + authoring form in `crates/aura-ingest/examples/shared/`. +2. **Single backtest** — the existing `ger40_breakout_real` example bootstraps + the blueprint (`blueprint.with("entry_bar.target", 3).with("exit_bar.target", + 5).bootstrap()`), reproducing the shipped FlatGraph's numbers **exactly** + (C23 behaviour-preserving: 2024-09 → −45.0; 2024 → +203.4 / 62 sessions). +3. **World-family demos on real GER40** (new runnable examples): + - `sweep` over the `{entry_bar, exit_bar}` grid, ranked by `total_pips`. + - `walk_forward` (IS/OOS roll) — non-degenerate, optimizing entry/exit per + window. + - `compare` across instruments (GER40 vs FRA40) **and** across the bar-period + structural axis (a 15m blueprint vs a 30m blueprint). +4. **Gated test** (mirrors `real_bars.rs` skip-guard): asserts + (a) C23 — Composite-bootstrap == FlatGraph-bootstrap on the same window, + bit-identical; (b) `param_space()` is exactly `{entry_bar.target, + exit_bar.target}` (no `delay.lag`, no period); (c) determinism. +5. A short **ledger note**: the canonical shippable strategy form is a + `Composite` blueprint; `FlatGraph` is its compiled substrate. + +## 5. Acceptance criteria + +- The breakout ships as a `Composite`; `sweep` / `walk_forward` / compare all run + on it with **no re-authoring** (the #94 friction is gone). +- `param_space()` is exactly `{ entry_bar.target, exit_bar.target }` (#96). +- The period **cannot** be swept into a desync (it is not in the space) (#96). +- `walk_forward` optimizes a non-empty space per window (#97). +- C23: the blueprint reproduces the shipped FlatGraph numbers exactly. +- `cargo test --workspace` green; `cargo clippy --workspace --all-targets -D + warnings` clean. No change under `crates/*/src` except the optional ledger + note (authoring-side milestone). + +## 6. Issue resolution + +| Issue | Resolution | +|---|---| +| #94 form (FlatGraph not World-consumable) | ship the Composite blueprint (§3, §4.1) | +| #96 period desync + `delay.lag` leak | bar-period structural-bound, `delay.lag` bound out (§2) | +| #97 walk_forward empty-space | non-degenerate via the `{entry,exit}` space (§2, §4.3) | +| #3 param fan-out | **not needed** — stays filed (non-goal §1) | +| #95 residency | separate debug thread (non-goal §1) | +| #22 pip / #80 ns→ms | adjacent, not in scope (non-goal §1) |