Realistic broker & position-event table (C10 A-side)

New Issue

Deliver C10's deferred A-side so a strategy can be optimized net of real cost instead of by frictionless total_pips (which today rewards churn / selects overtraders). Today the only broker is the frictionless SimBroker (pips); there is no PositionEvent, no realistic broker, no currency/account/lot/contract-size concept anywhere.

Architecture decision (settled)

The realistic broker is a single-pass node on exposure + price (peer of SimBroker), tracking position state internally and charging friction on each exposure delta. The position-event table (event_ts, action[buy/sell/close], position_id, instrument_id, volume) is a decoupled post-run AUDIT artifact derived from the recorded exposure stream's first difference — not the broker's live input.

This resolves a real over-specification in C10 as written: "ordinary node" + "attaches on the derived table" + "table is computed post-run" cannot all hold without a two-pass run and a multi-column / multi-row-per-ts Source replay (which does not exist and carries a reversal determinism hole). We pick the single-pass reading and record the clarification in the ledger (see the ledger issue). Rejected: two-pass table-as-Source (over-machinery), post-run fold (gives up "broker is an ordinary node").

Settled defaults (overridable)

  • Sizing (exposure fraction → lots): fixed-notional |exposure|·account_size/contract_value, rounded to lot_step; one shared sizing policy used by both the broker and the table-derivation.
  • Frictions (first cut): constant per-instrument spread (reproducible from the manifest) + commission (per-lot/per-trade). Slippage declared no-op; margin/liquidation deferred.
  • Account context: passed as broker constructor args (account_size, currency label) — NOT Aura.toml (that file is its own deferred cycle).
  • Single-instrument GER40 with an EUR account; quote-currency is a label only.

Stops / risk overlays

Stop-loss, take-profit, trailing-stop, time-stop are risk overlays in the decision layer, expressed in the exposure stream (C10: "risk lives in the decision/sizing node") — NOT broker features. Putting them in the broker would diverge the broker's position from exposure and break sim-optimal vs realistic comparability. The broker owns only stop-fill realism (slippage / gap-through), which is the slippage param of this milestone (no-op first cut). The protective-exit node family itself is separate strategy-authoring (aura-std), tracked as its own issue, not part of this milestone.

Deferred (named, NOT in this milestone)

Aura.toml + config loader (own cycle); FX conversion when quote ≠ account currency; slippage modeling; margin/liquidation account-state; per-bar executable spread (needs a 3rd broker input); multi-instrument. Whether the optimizer DEFAULT objective flips to net-currency is a separate, explicitly-ratified step (keep total_pips default, net-currency opt-in).

Definition of done

A single aura run --real <SYM> yields directly comparable sim-optimal (pips) and realistic (currency) equity curves plus the position-event audit table; optimize/rank_by can rank by net_currency_pnl; the C10 Architecture-A clarification is in the ledger; RunMetrics stays backward-compatible (#[serde(default)] on new fields).

No due date
100% Completed