Realistic broker on the position-event table + Broker/Account composite (Stage 2) #116

Closed
opened 2026-06-22 16:30:50 +02:00 by Brummel · 5 comments
Owner

Reframed from 'single-pass broker on exposure (Architecture A)' per #117. Stage-2 scope: a realistic broker node consuming the derived position-event table (real spread / commission / slippage / lot / margin), emitting a currency equity stream; plus a Broker/Account composite aggregating per-symbol P&L into account equity, with the equity->Sizer feedback cut by a z^-1 register on the fill edge (compounding). Account mode (netting/hedging) = composition constraint.

Reframed from 'single-pass broker on exposure (Architecture A)' per #117. Stage-2 scope: a realistic broker node consuming the derived position-event table (real spread / commission / slippage / lot / margin), emitting a currency equity stream; plus a Broker/Account composite aggregating per-symbol P&L into account equity, with the equity->Sizer feedback cut by a z^-1 register on the fill edge (compounding). Account mode (netting/hedging) = composition constraint.
Brummel added this to the Realistic broker & position-event table (C10 A-side) milestone 2026-06-22 16:30:50 +02:00
Brummel added the feature label 2026-06-22 16:30:50 +02:00
Brummel changed title from RealisticBroker node: single-pass on exposure+price, currency equity (Architecture A) to Realistic broker on the position-event table + Broker/Account composite (Stage 2) 2026-06-23 13:17:49 +02:00
Author
Owner

/boss run — reference issue for the Realistic-broker (C10 A-side, Stage 2) milestone. Fork decisions for this run are logged here.

Ordering decision: broker milestone before #147 (domain-agnostic results substrate)

At the close of #136 the registry was deliberately kept domain-aware, with the RunReport<M> / domain-agnostic-World abstraction deferred to #147. The open sequencing fork was: do #147 (abstract the registry metric/deflation vocabulary) first, or this broker milestone first?

Decided: this broker milestone first; #147 after. A sound abstraction of the metric vocabulary needs ≥2 concrete metric families to generalize over. Today there is only one (R-metrics, from the Stage-1 work). This milestone introduces the second — currency metrics + net_currency_pnl (#121). Abstracting before that second family exists means generalizing from a single example: premature, and likely to encode R-specific shape as if it were general. The #136-close deferral ("registry stays domain-aware by design") was exactly that signal. So #147 builds on the broker, not the reverse → broker first.

Provenance: the user delegated this sequencing call to the orchestrator and asked it to proceed.

**`/boss` run — reference issue for the Realistic-broker (C10 A-side, Stage 2) milestone.** Fork decisions for this run are logged here. ### Ordering decision: broker milestone *before* #147 (domain-agnostic results substrate) At the close of #136 the registry was deliberately kept domain-aware, with the `RunReport<M>` / domain-agnostic-World abstraction deferred to #147. The open sequencing fork was: do #147 (abstract the registry metric/deflation vocabulary) first, or this broker milestone first? **Decided: this broker milestone first; #147 after.** A sound abstraction of the metric vocabulary needs ≥2 concrete metric *families* to generalize over. Today there is only one (R-metrics, from the Stage-1 work). This milestone introduces the second — currency metrics + `net_currency_pnl` (#121). Abstracting before that second family exists means generalizing from a single example: premature, and likely to encode R-specific shape as if it were general. The #136-close deferral ("registry stays domain-aware by design") was exactly that signal. So #147 builds *on* the broker, not the reverse → broker first. Provenance: the user delegated this sequencing call to the orchestrator and asked it to proceed.
Author
Owner

Cycle-1 scope (orchestrator decision)

The milestone spans several cycles. Cycle 1 = the RealisticBroker node + its friction model (#120), feed-forward only. A streaming broker node that produces a currency-denominated equity curve for one symbol (marking the sized position — volume from the Sizer's size, carried in the PositionManagement record — distinct from the existing SimBroker, which marks the unsized exposure to a pip curve). Frictions: spread + commission as authored, tunable, overridable params (sweepable per C12); slippage a declared no-op hook; authored cost values win over any recorded per-bar spread.

Explicitly deferred to later cycles: registry currency metrics + net_currency_pnl ranking (#121); persistence of the currency-equity tap + dup-ts-safe position-event table (#122); CLI aura run --real attaching both brokers (#123); the Broker/Account composite + equity→Sizer compounding feedback with the z⁻¹ register on the fill edge (later in #116). Cycle 1 stays flat-1R feed-forward — no equity feedback.

A load-bearing design fork for cycle 1 — the broker's input model (stream the PM record vs. consume the derived event table, which can hold >1 row per timestamp against C8) — is handed to specify to derive and record here.

### Cycle-1 scope (orchestrator decision) The milestone spans several cycles. **Cycle 1 = the `RealisticBroker` node + its friction model (#120), feed-forward only.** A streaming broker node that produces a currency-denominated equity curve for one symbol (marking the *sized* position — volume from the Sizer's `size`, carried in the PositionManagement record — distinct from the existing `SimBroker`, which marks the *unsized* exposure to a pip curve). Frictions: spread + commission as authored, tunable, overridable params (sweepable per C12); slippage a declared no-op hook; authored cost values win over any recorded per-bar spread. Explicitly **deferred to later cycles**: registry currency metrics + `net_currency_pnl` ranking (#121); persistence of the currency-equity tap + dup-ts-safe position-event table (#122); CLI `aura run --real` attaching both brokers (#123); the Broker/Account composite + equity→Sizer compounding feedback with the `z⁻¹` register on the fill edge (later in #116). Cycle 1 stays flat-1R feed-forward — no equity feedback. A load-bearing design fork for cycle 1 — the broker's **input model** (stream the PM record vs. consume the derived event table, which can hold >1 row per timestamp against C8) — is handed to `specify` to derive and record here.
Author
Owner

Design reconciliation (specify) — cycle 0081, spec 0081-realistic-broker-node

Resolving the load-bearing input-model fork for the RealisticBroker before writing the spec.

Fork: how does the RealisticBroker consume the executed book? C10's prose says the broker "consumes the derived position-event table" / sits "on the derived position-event table." But that table is a post-run computed artifact that can hold >1 row per event_ts (a reversal = Close + opposite open), so a streaming node (C8: ≤1 record per eval) cannot take it as a per-eval input stream. Two readings: (A) the broker is a post-run fold over the table (sibling of derive_position_events); (B) the broker is an in-graph streaming node reading the executor's per-eval output (the PositionManagement dense record) + price, with the position-event table kept as the decoupled audit layer.

Decided: (B), an in-graph streaming node. Basis: derived

  1. C10 itself calls realistic brokers "ordinary downstream nodes (C8/C9)" that "emit a currency equity stream," several attachable at once — the in-graph-node framing, parallel to the existing in-graph SimBroker (pip).
  2. #122 specifies currency_equity as "a normal ColumnarTrace tap … existing persist_traces path" — a tap is the recorder-sink output of an in-graph node (exactly like SimBroker's equity tap), not a post-run series.
  3. C8 forces the per-eval input to be the PositionManagement record (≤1 record/eval), since the position-event table (>1 row/instant) is structurally ineligible as a per-eval stream. The record carries the same fills the table materializes (direction, size, open, prices), so the broker reads the executor's per-eval output and the table remains the decoupled, derived audit sibling (C10 "decoupled, derived"; persisted separately per #122).

C10 clarification (not a break). "The table feeds the realistic broker nodes" is read as conceptual — the broker operates on the executed book, whose per-eval form is the executor's record and whose materialized form is the table. The mechanical streaming input is the record; the table is the audit layer. The R-evaluator (summarize_r, post-run) vs. realistic broker (in-graph) split is preserved in spirit; the ledger note will be synced at cycle close if the wording needs tightening.

Reviewable + vetoable here and via the spec's auto-sign notify.

## Design reconciliation (specify) — cycle 0081, spec `0081-realistic-broker-node` Resolving the load-bearing **input-model fork** for the `RealisticBroker` before writing the spec. **Fork: how does the RealisticBroker consume the executed book?** C10's prose says the broker "consumes the derived position-event table" / sits "on the derived position-event table." But that table is a *post-run computed* artifact that can hold >1 row per `event_ts` (a reversal = Close + opposite open), so a streaming node (C8: ≤1 record per `eval`) cannot take it as a per-`eval` input stream. Two readings: (A) the broker is a post-run fold over the table (sibling of `derive_position_events`); (B) the broker is an in-graph streaming node reading the executor's per-`eval` output (the `PositionManagement` dense record) + price, with the position-event table kept as the decoupled audit layer. → **Decided: (B), an in-graph streaming node.** Basis: **derived** — 1. C10 itself calls realistic brokers "ordinary downstream **nodes** (C8/C9)" that "emit a currency equity **stream**," several attachable at once — the in-graph-node framing, parallel to the existing in-graph `SimBroker` (pip). 2. #122 specifies `currency_equity` as "a normal ColumnarTrace **tap** … existing `persist_traces` path" — a tap is the recorder-sink output of an in-graph node (exactly like `SimBroker`'s `equity` tap), not a post-run series. 3. C8 *forces* the per-`eval` input to be the `PositionManagement` record (≤1 record/eval), since the position-event table (>1 row/instant) is structurally ineligible as a per-`eval` stream. The record carries the same fills the table materializes (`direction`, `size`, `open`, prices), so the broker reads the executor's per-`eval` output and the table remains the *decoupled, derived* audit sibling (C10 "decoupled, derived"; persisted separately per #122). **C10 clarification (not a break).** "The table feeds the realistic broker nodes" is read as conceptual — the broker operates on the *executed book*, whose per-`eval` form is the executor's record and whose materialized form is the table. The mechanical streaming input is the record; the table is the audit layer. The R-evaluator (`summarize_r`, post-run) vs. realistic broker (in-graph) split is preserved in spirit; the ledger note will be synced at cycle close if the wording needs tightening. Reviewable + vetoable here and via the spec's auto-sign notify.
Author
Owner

Reconsideration → bounce (specify, cycle 0081 — spec draft discarded)

Before committing the spec I pulled two spec-skeptic bias-breakers on it (the fork was foundational + cascading). Both returned BLOCK; both are correct.

  • scope-fork (architecture): BLOCK. The in-graph-node decision (B, recorded above) contradicts the C10 contract, which says — explicitly, in deliberate contrast to the R-evaluator — "realistic brokers on the derived position-event table," "this table feeds the realistic broker nodes," "a realistic broker node consumes the events." That is reading (A): a post-run fold over the position-event table (sibling of derive_position_events), not an in-graph node. My (B) signals were weak: "ordinary downstream nodes (C8/C9)" also names summarize_r, which IS a post-run fold — so "node" ≠ in-graph; "C8 forces the PM record" is circular (C8 bars the table only as a per-eval stream; a post-run fold over it is C8-clean and matches C10). Only #122's "tap" leans (B), and that is an open issue, not a contract. Shipping (B) would break a contract and amend the ledger after the fact — backwards (CLAUDE.md: amend the contract first).

  • ambiguity (currency model): BLOCK — a 1e5× unit error. The Sizer's size = risk_budget / stop_distance (stop_distance in price units) is dimensionally a base-unit position (its stop-loss = risk_budget currency), not lots. The spec calls size both "lots" and "a base-unit position," then multiplies by lot_size — double-counting. The two readings diverge by lot_size; the frictionless-parity acceptance inherits the undefined unit.

Deeper concern. C10 ties currency P&L to the compounding Stage 2 (fixed-fractional sizing that reads equity, with the z⁻¹ fill-edge register). A feed-forward currency curve over flat-1R sizing is ≈ the R-curve scaled by risk_budget — so the cycle-1 scoping ("feed-forward currency broker first, defer the feedback") may itself be incoherent with how C10 defines currency.

Conclusion. The broker milestone's first cycle is not settled enough for specify. Three unresolved load-bearing forks: (1) architecture A (post-run fold, contract-faithful) vs B (in-graph node, matches #122's "tap") — and the C10/#122 wording reconciliation either way; (2) the currency unit/volume model; (3) whether a feed-forward currency cycle is coherent at all vs. C10's compounding-tied currency. This needs brainstorm (discovery) — a new cycle — so it is bounced to the user. Spec draft discarded (was uncommitted).

## Reconsideration → bounce (specify, cycle 0081 — spec draft discarded) Before committing the spec I pulled two `spec-skeptic` bias-breakers on it (the fork was foundational + cascading). **Both returned BLOCK; both are correct.** - **scope-fork (architecture): BLOCK.** The in-graph-node decision (B, recorded above) contradicts the **C10 contract**, which says — explicitly, in deliberate contrast to the R-evaluator — "realistic brokers **on the derived position-event table**," "this table **feeds** the realistic broker nodes," "a realistic broker node **consumes the events**." That is reading **(A)**: a post-run fold over the position-event table (sibling of `derive_position_events`), *not* an in-graph node. My (B) signals were weak: "ordinary downstream nodes (C8/C9)" also names `summarize_r`, which IS a post-run fold — so "node" ≠ in-graph; "C8 forces the PM record" is circular (C8 bars the table only as a per-`eval` *stream*; a post-run **fold** over it is C8-clean and matches C10). Only #122's "tap" leans (B), and that is an open issue, not a contract. Shipping (B) would break a contract and amend the ledger after the fact — backwards (CLAUDE.md: amend the contract *first*). - **ambiguity (currency model): BLOCK — a 1e5× unit error.** The Sizer's `size = risk_budget / stop_distance` (stop_distance in price units) is dimensionally a **base-unit** position (its stop-loss = `risk_budget` currency), **not lots**. The spec calls `size` both "lots" and "a base-unit position," then multiplies by `lot_size` — double-counting. The two readings diverge by `lot_size`; the frictionless-parity acceptance inherits the undefined unit. **Deeper concern.** C10 ties currency P&L to the **compounding** Stage 2 (fixed-fractional sizing that *reads equity*, with the z⁻¹ fill-edge register). A *feed-forward* currency curve over flat-1R sizing is ≈ the R-curve scaled by `risk_budget` — so the cycle-1 scoping ("feed-forward currency broker first, defer the feedback") may itself be incoherent with how C10 defines currency. **Conclusion.** The broker milestone's first cycle is **not settled enough for `specify`**. Three unresolved load-bearing forks: (1) architecture A (post-run fold, contract-faithful) vs B (in-graph node, matches #122's "tap") — and the C10/#122 wording reconciliation either way; (2) the currency unit/volume model; (3) whether a feed-forward currency cycle is coherent at all vs. C10's compounding-tied currency. This needs `brainstorm` (discovery) — a new cycle — so it is **bounced to the user**. Spec draft discarded (was uncommitted).
Author
Owner

Milestone retired — C10 reworked (run close)

The realistic-broker model this milestone was built on is retired. The design discussion in this thread concluded that modelling real broker friction (slippage, swaps, spread) over historical data is horseshoe-throwing — real friction is not historically knowable; the only reliable friction is measured forward against a real broker. C10 was reworked accordingly and committed (29cdc8c):

  • Cost in research is a composable C9 cost-model graph, in R (gross R → net R), generalizing/subsuming the scalar round_trip_cost; the headline artifact is the net-R curve (net_r_equity). No money in research.
  • The Sizer and compounding leave the research loop (pure feed-forward); the z⁻¹ fill-edge register and the flat-1R-vs-compounding structural axis are gone. Executor = stop-rule → position-management in R. Conviction-based risk allocation survives as an R-aggregation axis.
  • Money / a real broker / cTrader Open API are a separate, later live/deploy edge — the only ground truth, measured not modelled.

Re-scope:

  • This milestone and its sub-issues (#120, #121, #122, #123) are closed as superseded.
  • New forward queue: #148 (the cost-model graph, research) and #149 (the live/deploy edge, cTrader).
  • #124 (instrument-ref resolution) survives — the cost model reads reference geometry at ingestion.
  • #118 (currency-equity chart) is closed — the net-R curve charts via existing tooling.

Closing this issue.

## Milestone retired — C10 reworked (run close) The realistic-broker model this milestone was built on is **retired**. The design discussion in this thread concluded that modelling real broker friction (slippage, swaps, spread) over historical data is horseshoe-throwing — real friction is not historically knowable; the only reliable friction is measured forward against a real broker. C10 was reworked accordingly and committed (`29cdc8c`): - Cost in research is a **composable C9 cost-model graph, in R** (gross R → net R), generalizing/subsuming the scalar `round_trip_cost`; the headline artifact is the **net-R curve** (`net_r_equity`). No money in research. - The **Sizer and compounding leave the research loop** (pure feed-forward); the `z⁻¹` fill-edge register and the flat-1R-vs-compounding structural axis are gone. Executor = stop-rule → position-management in R. Conviction-based risk allocation survives as an R-aggregation axis. - Money / a real broker / cTrader Open API are a **separate, later live/deploy edge** — the only ground truth, measured not modelled. Re-scope: - This milestone and its sub-issues (#120, #121, #122, #123) are **closed as superseded**. - New forward queue: **#148** (the cost-model graph, research) and **#149** (the live/deploy edge, cTrader). - #124 (instrument-ref resolution) **survives** — the cost model reads reference geometry at ingestion. - #118 (currency-equity chart) is closed — the net-R curve charts via existing tooling. Closing this issue.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#116