From d0ebdf52109eb00a4c23daa0116a6ba5a29c1a6a Mon Sep 17 00:00:00 2001 From: Brummel Date: Wed, 3 Jun 2026 08:58:06 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20C10=20=E2=80=94=20strategy=20result=20i?= =?UTF-8?q?s=20a=20broker-independent=20position=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supersede 'broker is part of the strategy'. A strategy outputs a time-ordered table of position events (event_ts, action[buy/sell/close], position_id, instrument_id, volume; open time = opening event's event_ts, so no open_ts; unsigned volume, direction in action). Brokers become downstream swappable plugins: a deterministic frictionless sim-optimal broker yields synthetic pip-equity for neutral comparison/optimization; realistic broker plugins add currency friction/constraints for viability/deploy. Stays within C7 (scalar columns). Updates CLAUDE.md invariant 7, the walking-skeleton line, provenance, and the day-in-the-life doc. Co-Authored-By: Claude Opus 4.8 (1M context) --- CLAUDE.md | 14 ++++++---- docs/design/INDEX.md | 63 +++++++++++++++++++++++++++--------------- docs/project-layout.md | 9 ++++-- 3 files changed, 56 insertions(+), 30 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0bd125e..1981631 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -72,11 +72,15 @@ design decision, not a refactor, and belongs in the ledger. makes a live external call mid-replay. (See `~/.claude/CLAUDE.md` for the IONOS consent rule: external LLM calls happen at the recording/live-source edge, with explicit per-session consent, never inside a backtest.) -7. **The broker is part of the strategy.** The broker node is fixed in a - strategy and identical in sim and live (one accounting path → one equity - curve shape). "Same strategy, different broker" = swap the broker *profile* - it is parameterized with. Live order-routing/reconciliation is an external - adapter outside the strategy graph. +7. **Strategy result is a broker-independent position table.** A strategy + outputs not an equity curve but a time-ordered table of position events + (`event_ts, action[buy/sell/close], position_id, instrument_id, volume`; a + position's open time is its opening event's `event_ts`); the set of open + positions at t is its state. Brokers are downstream, swappable plugins that + turn this table into an equity curve — never part of the strategy. Neutral + evaluation uses a deterministic, frictionless sim-optimal broker producing + synthetic equity in **pips**; realistic broker plugins add real + friction/constraints in currency for viability and deploy. 8. **Deploy artifacts are frozen.** Hot-reload (cdylib) is an authoring-loop tool only. The live bot is a statically-linked, versioned, frozen artifact — never hot-swapped (audit trail: this bot = this commit). diff --git a/docs/design/INDEX.md b/docs/design/INDEX.md index ae5f46b..f62be01 100644 --- a/docs/design/INDEX.md +++ b/docs/design/INDEX.md @@ -5,16 +5,18 @@ contract states what it **guarantees**, what it **forbids**, and **why**. A change that breaks a contract is a design decision (amend the contract here with its new rationale), never a silent refactor. -Provenance: contracts C1–C15 were settled in the initial rough-sketch design -interview (2026-06-03), walking the design tree root-to-leaf. The `CLAUDE.md` +Provenance: contracts C1–C18 were settled in the initial rough-sketch design +interview (2026-06-03), walking the design tree root-to-leaf (C16–C18 and the +C10 refinement to a broker-independent position table came in follow-up turns). +The `CLAUDE.md` **Domain invariants** section is the compressed, always-loaded summary of the subset that agents must never violate; this file is the fuller form with rationale. Vocabulary: a *contract* is one ledger entry. A *cycle* is one pipeline round; a *milestone* is a tracker container spanning many cycles (the first milestone is -the **walking skeleton**: ingest → one signal → deterministic backtest → equity -metric). +the **walking skeleton**: ingest → one signal → deterministic backtest → +position table → sim-optimal broker → synthetic pip-equity metric). --- @@ -136,24 +138,41 @@ forcing feedback through a visible delay node keeps the per-cycle determinism intact and the one legitimate feedback path explicit. Graph-as-data enables visualization, freezing, and re-parameterization for sweeps. -### C10 — The execution chain; broker is part of the strategy -**Guarantee.** Execution is in-graph: `signals (scores) → decision node (score → -target position, sizing, risk) → broker node (intent → fills → position / trade -/ equity streams)`. The broker node is a fixed part of the strategy and -identical in sim and live (one accounting path → one equity-curve shape). It is -parameterized by a swappable **broker profile** (fees, spread/fill rules, order -types, lot/margin); "same strategy, different broker" = swap the profile. The -portfolio is multi-instrument (positions per symbol-key). Fills cross the real -spread from `data-server` (buy @ ask, sell @ bid; M1 carries `spread`, tick -carries ask/bid), causally (only prices that occurred ≤ now). -**Forbids.** A `SimBroker`/`LiveBroker` swap; computing equity by two different -code paths; synthetic spread when real bid/ask is available; in-graph live order -routing. -**Why.** Realistic P&L does not exist without committing to a broker model, so -the broker is part of the testable artifact. One accounting path guarantees the -equity curve looks the same in both modes by construction. Live order routing / -reconciliation is an external adapter outside the strategy graph; the displayed -equity stays the modeled one. +### C10 — Strategy result is a broker-independent position table; brokers are downstream plugins +**Guarantee.** A strategy's result is **not** an equity curve but a +**broker-independent, time-ordered table of position events**. The chain is +`signals (scores) → decision/sizing node → position-event output`. An event is +pure scalar columns (C7): `event_ts: timestamp`, `action: i64` (buy / sell / +close), `position_id: i64`, `instrument_id: i64`, `volume: f64` (unsigned — +direction is the `action`). A position's open time is the `event_ts` of its +opening event (there is no separate `open_ts`); a `close` references a +`position_id` and may be partial via its own `volume`. The **set of open +positions at time t** (opens minus closes with `event_ts ≤ t`) is the strategy's +*state* at t; the ordered sequence of these states is the result. Position +sizing and risk live here (they set `volume`); the portfolio is multi-instrument. + +A **broker is a downstream, swappable plugin** that consumes the position table +and produces an equity curve — never part of the strategy. Two classes: +**(a) the sim-optimal broker** — deterministic, frictionless, perfect-fill +execution producing a **synthetic equity curve in pips** (no real currency, no +real-broker constraints); the neutral yardstick for comparing and optimizing +strategy *logic*. **(b) realistic broker plugins** (Pepperstone, …) — apply real +spread / commission / slippage / lot / margin, may reject or modify positions, +and produce a currency equity curve for viability and deployment. Pip PnL uses +per-instrument pip metadata (reference data beside the hot path, C7). Live: a +realistic broker plugin consumes the position events in real time and routes +orders; reconciliation with the real account is an external adapter. +**Forbids.** Treating an equity curve as the strategy's output; baking a broker +into the strategy; storing `open_ts` (derive it from the opening event); a +signed-volume direction trick (use `action`); broker-specific assumptions +leaking into the strategy logic. +**Why.** A strategy can be judged neutrally only if its result is independent of +any real broker's frictions. The position table is that broker-independent +invariant: one table feeds many brokers, each yielding its own equity — so "same +strategy, different broker" and "same decisions sim vs live" both fall out. The +sim-optimal pip curve is a level, currency-free playing field for comparison; +realistic plugins then test real-world viability. This supersedes the earlier +"broker is part of the strategy" framing. ### C11 — Generalized sources; record-then-replay determinism boundary **Guarantee.** A source is anything that produces timestamped scalar streams — diff --git a/docs/project-layout.md b/docs/project-layout.md index 22e3854..1ae948d 100644 --- a/docs/project-layout.md +++ b/docs/project-layout.md @@ -69,9 +69,12 @@ recorded before it enters a backtest, and only with your per-session consent. 2. **Claude** (via the skills pipeline) writes `nodes/third-candle-long/`, implements `schema` + `eval` against `aura-core`. 3. **Backtest:** `aura backtest nodes/third-candle-long --symbol GER40 - --from 2020 --to 2024 --broker pepperstone` → a metrics table (trades, - hit-rate, P&L, max-DD, Sharpe) + a run record (manifest + metrics) under - `runs/`. + --from 2020 --to 2024` → the strategy produces a broker-independent + **position table** (open/close events); the default **sim-optimal broker** + projects it into a synthetic **pip**-equity, yielding a metrics table + (trades, hit-rate, pip-P&L, max-DD, Sharpe) + a run record (manifest + + metrics) under `runs/`. Add `--broker pepperstone` to project the same table + through a realistic broker (currency, friction) for viability. (Contract C10.) 4. **Sweep + Monte-Carlo:** `aura sweep … --grid lookback=5..50,thresh=0.5..0.9 --mc-seeds 1000` → a distribution of metrics, best params, robustness bands. 5. **Compose:** "combine it with `momentum-filter` as a weighted sum" → Claude