Trade-Level Portfolio Ledger & Mixer — Architecture Reference
status: DRAFT (Round-2 review incorporated 2026-09-28) tier: 2 (design reference) last-verified: 2026-09-28 verified-against: 95a97b1b initiative: Projects/Trade-Level Portfolio Mixer/Trade-Level Portfolio Mixer - Revised Master Plan (2026-09-25).md supersedes: none
Purpose
This document defines the proposed data and execution contracts for the Trade-Level Portfolio Mixer. It is a design reference, not an implementation description. All decisions marked PROPOSED require Gate 0 approval before code is written.
The contract exists to keep the following three layers honest:
- the per-record trade ledger;
- the portfolio mixer/optimizer;
- the dashboard and targeted-evolution consumers.
The governing plan is Projects/Trade-Level Portfolio Mixer/Trade-Level Portfolio Mixer - Revised Master Plan (2026-09-25).md.
Scope boundary
The first release consumes replayed bar-resolution trades from the Python backtester. It does not claim exchange/tick execution, live order routing, or exact intra-bar fill timing. A later tick-data initiative may add higher-resolution fields without changing the immutable identity model.
1. Immutable record identity
1.1 Aliases versus records
genome_uid is an alias. It is retained for compatibility with existing champion JSONs, dashboards, family keys, and vault references.
record_id is the canonical key for one replay (replay identity). Proposed inputs to the identity hash are:
- normalized genome UID;
- chart/dataset identity — the exact parquet consumed, including research variants (
*_DI_EVOLVE,*_DI_*); - engine (
PAorOPT); - canonical CLI hash after neutral-parameter normalization — computed on a deep copy of the parsed params (never on the live replay dict;
canonicalize_genomemutates in place — the documented v3.20.0 gotcha), with the_INTERNAL_CLI_KEYSdenylist and the launcher token stripped; - backtest engine version;
- dataset snapshot fingerprint;
- effective data window (resolved
date_from/date_to/last_sessions— identity-relevant even though the reporting denylist strips those keys from replay CLIs); - data-preparation parameter set (frozen prep config, e.g.
atr_period,source_tz).
Schema-version exclusion (D-11): the ledger schema version and the generation id are not record_id inputs. They belong to the ledger-row identity (record_id × ledger schema version × generation id). Bumping the ledger schema must never re-key existing records.
A single UID may resolve to multiple record_id values. This is a first-class result, not an error to hide.
Identity is derived from CLI/JSON provenance, never from lossy CSV headers — the P4 case (header-only replay gives 5,301 vs 6,106 trades) is a mandatory regression fixture.
1.2 Collision rules
- Never use
genome_uidas a parquet overwrite key. - Never resolve duplicate records by highest
Net Ticksalone. - Retain conflicting records and classify them in the catalog: generation-history duplicate, CLI collision, engine/version conflict, dataset conflict,
DIVERGENT_ENGINE_CONTRACT(pre-v3.20.0 Engine-B rows — replaying under today's neutral-default engine yields different trades than their stored DB Net Ticks; the 11 EPOCH-B champion JSONs already carry the_entry_semantics_divergenceannotation as precedent), or unresolved. - The catalog must expose the alias→record mapping and the reason for every conflict.
- A portfolio candidate references
record_id;genome_uidalone is insufficient.
1.3 Population classification
Every build manifest must list the population rule and census:
- included records;
- excluded records and reason;
- charts covered;
- engines covered;
- duplicate/collision groups;
- date and dataset windows;
- per-record and aggregate trade counts.
The manifest — not a historical snapshot figure — is the authoritative census for a published generation. Reference census at Round-2 review (2026-09-28 ~20:30, live DB): 7,442 rows / 5,851 distinct UIDs with valid CLI / 805 champion-class UIDs; the "410 champions / 5,762 pool" figures of the Sept-20 snapshot are stale, the pool churns under continuous evolution, and by the same evening the parallel Genome Evaluation DB rebuild had already moved the census to 10,620 / 9,966 / 154 — tier sizes are measured at build time (F-15), never hard-coded.
Champion taxonomy pinning (D-03): a generation must name the champion definition it used. Three coexist: 410-institutional (Sept-20 artifact), live-DB institutional (Net Ticks > 30000 & Max Drawdown < 0), and multi-horizon status rows (Anchor_Net/Lifetime_Net/Net > 0). The proposed Generation-1 definition is live-DB institutional + family reps (family_reps.csv).
Anchor-coverage sparsity: only 163 champion rows carry anchor_net > 0 (pre-v3.24.1 runs lack the column). Facet-index filters must not assume anchor coverage; coverage flags are mandatory.
2. Ledger schema (proposed)
| Field | Requirement | Semantics |
|---|---|---|
record_id |
required | immutable record key |
genome_uid |
required | compatibility alias |
Chart |
required | M1, M5, D200, D500, T900, V300, or explicitly classified extension |
Engine |
required | PA or OPT |
Trade_ID |
required | zero-based ordinal within the record |
Signal_Bar |
required | bar index where the setup was detected |
Fill_Bar |
required | bar index where the limit/entry was filled |
Exit_Bar |
required | bar index where the trade closed |
Fill_Window_Bars |
required | order fill window (CLI param; engine default 1) — needed to derive unfilled pending occupancy; the Python engine never exports expired orders |
Signal_Time |
required | signal-bar stamp |
Fill_Bar_Time |
required | fill-bar stamp |
Exit_Bar_Time |
required | exit-bar stamp |
Timestamp_Precision |
required | proposed initial value BAR |
Direction |
required | Long or Short |
Volume |
required | contracts |
Entry_Price / Exit_Price |
required | executed price at declared precision |
Gross_Ticks |
required | price PnL before friction, with declared rounding |
Slippage_Ticks |
required | explicit slippage component |
Commission_Ticks |
required | engine-canonical commission component, or explicit NA reason |
Net_Ticks |
required | canonical engine net value; mixer must not reapply friction |
Tick_Size / Tick_Value |
required | instrument contract specification values |
Contract_Spec_Version |
required | versioned instrument metadata |
Net_PnL_USD |
required | conversion from the declared contract spec |
Exit_Reason |
required | canonical engine reason |
SessionDay |
required | signal-bar CME session day |
Weekday |
required | signal-bar weekday |
Intraday_Window |
required | signal-bar session bucket |
Duration_Bars |
derived | exit minus fill bar under the declared convention |
Duration_Seconds |
derived/optional | bar-stamp difference only when precision is declared |
MAE_BarRange_Ticks |
required (D-10, BarRange-first) | worst intra-bar high/low excursion from entry; no same-bar ordering ambiguity (no intra-bar sequencing claimed); engine-computed via the existing unr_best/unr_worst block (engine/simulate.py:556-563) |
MFE_BarRange_Ticks |
required (D-10, BarRange-first) | best intra-bar high/low excursion from entry |
MAE_Close_Ticks |
proposed, later | worst bar-close mark-to-market excursion from entry |
MFE_Close_Ticks |
proposed, later | best bar-close mark-to-market excursion from entry |
Excursion_Method |
required | BAR_RANGE / CLOSED_BAR / TICK; documents the method per field |
Source_* |
required | CLI, engine version, DB/run id, dataset fingerprint, schema version |
2.1 Precision rule
The first release declares Timestamp_Precision = BAR. The current backtester trade log provides signal-bar, fill-bar, and close-bar stamps; it does not prove an exact second within a bar. The ledger must never turn a bar stamp into a claim of exact execution.
A Duration_Seconds value derived from bar stamps is allowed only when labelled as bar-resolution. Exact-second or tick-resolution fields require a separate data contract and provenance.
2.2 Session rule
Attribution follows the existing signal-bar convention:
- CME session roll at 17:00 CT;
SessionDay,Weekday, andIntraday_Windoware derived from the signal bar;- the fill or exit bar never moves a trade into a different facet;
- an overnight position remains occupied after its assigned slot ends.
2.3 Excursion rule
MAE_BarRange_Ticks and MFE_BarRange_Ticks are the initial excursion fields (D-10, BarRange-first): excursion-from-entry measured on intra-bar highs/lows. Because no intra-bar sequencing is claimed, this variant carries no same-bar ordering ambiguity — and it is the variant the engine can populate with a minimal additive diff (the daily-limit check already computes the intra-bar extremes per active bar). Bar-close variants (MAE_Close_Ticks / MFE_Close_Ticks) are added later.
Semantic guard: the existing per-trade tl_max_dd is a peak-to-trough unrealized drawdown, not an excursion from entry — it must never be relabelled as MAE. Each excursion field carries Excursion_Method; an intra-bar sequencing claim (beyond H/L extremes) requires a separate method tag and provenance.
2.4 Accounting rule
Friction is stored as components and the canonical engine net value is preserved. The mixer consumes Net_Ticks; it never subtracts slippage or commission again. A tick-like commission value must never be labelled USD without an explicit unit conversion.
Multi-instrument support requires a versioned contract-spec table. A hard-coded MNQ tick value is not a universal contract.
3. Facet index contract
The facet index is a derived, disposable artifact. The trade ledgers and manifest are the source of truth.
One row per record_id may contain:
- global net, drawdown, trade count, win rate, and active-session count;
- weekday net/drawdown/trade/active-day metrics;
- intraday window metrics;
- joint weekday×window metrics;
- stored
Opp_v21,Risk_v21,Score_v21,Anchor_Net, andLifetime_Netprovenance; - minimum-activity flags and confidence/coverage flags;
- identity collision classification.
Metrics must preserve active-session denominators. Zero-filled non-trading days must not be counted as losses or as active exposure. The index must record its source generation and schema version.
Search latency is a performance target, not a correctness substitute. The index may be rebuilt from the ledgers at any time.
4. Generation and publication contract
4.1 Build stages
- Snapshot the source DB and Master datasets.
- Fingerprint sources, code revision, engine version, and schema.
- Build ledgers, index, catalog, and manifest in staging.
- Run parity, identity, coverage, and storage checks.
- Publish the generation atomically through a manifest/pointer switch.
- Retain the previous generation for rollback.
4.2 Manifest requirements
The manifest must contain:
- generation id and creation time;
- source DB/dataset fingerprints;
- code revision and engine version;
- ledger schema version;
- population census and inclusion/exclusion rules;
- record and trade counts;
- per-file checksums and index checksum;
- collision/conflict summary;
- parity results — at record granularity: exact integer match on net ticks (mandatory); the daily PnL allocation sums to the record total exactly (same association order or integer daily ticks). A ≤0.5-tick print-only tolerance (the daily-PnL extractor pattern) is not bit-perfect and is not accepted; the manifest records the per-record diff distribution;
- storage measurements;
- active consumers, parent-generation pointer (lineage), and rollback target;
- execution-environment block (python/numba/numpy versions, njit-vs-fallback mode) — platform codegen has proven material (SIGSEGV saga);
- physical fingerprint per dataset — row count + first/last bar stamp in addition to (path, mtime, size); mtime alone is Dropbox-volatile;
- pinned champion-taxonomy definition used for the generation (D-03).
4.3 Failure rule
A failed parity, integrity, identity, or storage check rejects the generation. Published artifacts are never updated in place, and a warning-only path is not a valid release mode.
5. Portfolio occupancy and account model
5.1 Interval semantics
Bar indices are the sole authority for interval math (D-13) — monotonic and DST-immune by construction; timestamps are display-only. SessionDay/Weekday/Intraday_Window remain signal-bar CT attribution and never move a trade between facets.
A trade occupies a half-open interval:
[Fill_Bar, Exit_Bar)
with one engine-verified exception: a same-bar fill→exit round-trip (explicit same-bar exit block in the simulation core) occupies a minimum of 1 bar — never an empty interval.
Pending orders are a separate occupancy state, with two sub-cases derived from Fill_Window_Bars:
- filled orders tile cleanly: pending
[Signal_Bar, Fill_Bar)+ position[Fill_Bar, Exit_Bar); - unfilled orders are never exported by the Python engine, so their occupancy is derived:
[Signal_Bar, Signal_Bar + Fill_Window_Bars + 1).
A portfolio must not claim zero collision while an unexpired order can fill over another position. Ambiguous same-timestamp overlap between different strategies on one account is treated conservatively as a collision (D-05). A mandatory roll-edge fixture covers a signal at 16:59 CT with fill at 17:01 CT: bar-index occupancy persists across the CME roll.
5.2 Single-account relay
Proposed default rules:
- at most one open position;
- no simultaneous long/short exposure;
- a lower-priority signal is rejected when admission would exceed the contract limit;
- no automatic flattening is implied by priority gating;
- a position that crosses a session, window, or weekday boundary remains occupied.
Every admitted or rejected trade records the rule and the competing assignment.
5.3 Fleet mode
Fleet mode must define:
- account assignment;
- netting versus isolated positions;
- contract quantity per account;
- partial-fill behaviour;
- peak concurrent margin;
- cross-account symbol exposure;
- capital reservation and release timing.
A display estimate such as N × $800 is not an account model and must not be presented as a certification.
6. Optimizer contract
The optimizer selects portfolios, not merely genomes. It must therefore state:
- candidate universe and record-id pinning;
- objective formula and horizon;
- capital/concurrency constraints;
- minimum trade and active-session floors;
- complexity/turnover penalty;
- train, validation, and untouched forward-anchor partitions;
- baseline comparison;
- tie-breaking and deterministic ordering.
The forward anchor must not influence candidate discovery or parameter tuning. Stored score axes are provenance and ranking inputs; they do not replace portfolio-level replay.
Search algorithm (D-14): the recommended default is a deterministic greedy + local-swap search with explicit tie-breaking. A portfolio-level genetic algorithm re-opens the Round-1 overfit surface (F-07) and must be explicitly justified if chosen.
Anchor partition reuse (D-07): the untouched forward anchor is the v3.24.1 universal forward anchor (chronological last 20% of sessions), so Anchor_Net gating in champion_vault.py stays consistent — no parallel partition scheme.
Anchor-leak canary (mandatory): a synthetic record trading only inside the anchor window with outsized returns must never be scored, ranked, or selected; the optimizer code path must physically exclude anchor sessions from objective evaluation, and the canary test proves it end-to-end.
7. Targeted expert evolution contract
The first implementation is PA-first.
A child campaign records:
- parent
record_idand parent manifest generation; - assigned weekday/session window;
- pinned entry and exit genes;
- scratch results location;
- validation and anchor verdict;
- catalog disposition and lineage id.
The parent ledger, parent CLI, and prior gate evidence remain immutable. A child is not eligible for publication until its validation protocol passes. Engine-B support requires a separate design amendment because the current Stage-2 seam is Engine-A only.
8. Known limitations (proposed release)
- Bar-resolution timestamps do not establish exact second-level execution.
- Bar-range excursions are intra-bar high/low extremes from entry, not tick-path MAE/MFE; the Close variants and any intra-bar sequencing claim require a separate method tag.
- The Python engine never exports expired orders — unfilled pending occupancy is a derivation from
Fill_Window_Bars, not an observed fact. - Anchor provenance is sparse on historical rows (only 163 champion rows carry
anchor_net > 0); anchor-based filters degrade to coverage-flagged fallbacks. - Correlation across zero-filled or non-overlapping sessions can be misleading; active-session denominators and coverage flags are mandatory.
- A day-granularity schedule map is not a concurrency proof.
- The forward-anchor split reduces leakage but does not eliminate regime/era bias.
- Multi-instrument support is blocked until contract specifications are versioned.
9. Decision state
The following are proposed defaults and are not yet approved:
| Decision | Proposed value |
|---|---|
| Timestamp precision | BAR |
| Primary key | record_id (replay identity; schema version + generation live on the row identity — D-11) |
| UID handling | alias only; retain all conflicts incl. DIVERGENT_ENGINE_CONTRACT class |
| Same-timestamp overlap | conservative collision; same-bar fill→exit = minimum 1-bar occupancy |
| Relay conflict action | reject new lower-priority entry |
| Fleet margin | explicit versioned model |
| Optimizer validation | train/validation/untouched anchor; anchor = v3.24.1 universal forward anchor |
| Optimizer algorithm | greedy + local-swap, deterministic (D-14, recommended) |
| Targeted evolution scope | PA-first; Engine-B later |
| Storage budget | measure first, then set |
| Initial population | tiered (D-03): Gen-1 ≈ 1,200 records (805 champions + 423 family reps); V300 + pre-v3.20.0 OPT excluded-with-reason; full pool = classified background backfill |
| Excursions | BarRange-first via existing intra-bar extremes (D-10); Phase 0 byte-identity-gated commit |
10. Review provenance
Round 1 (2026-09-25, commit 0d82a8fe) — Senior review against the original draft. Implementation surfaces inspected:
Backtesting/backtest.py:4079-4170— trade reconstruction and facet attribution;Backtesting/engine/simulate.py:232-235,:530-541— friction and mark-to-market excursion;Backtesting/scripts/extract_champion_daily_pnl.py:114-125,:242-245,:403-421— pool identity, parity, publication;Backtesting/Dashboard/callbacks/portfolio.py:555-603,:1096-1118— metadata collision approximation;Backtesting/scripts/backtest_exit.py:2-21— Engine-A Stage-2 scope.
Round 2 (2026-09-28, commits 95a97b1b→6141547f, engine v3.24.1→v3.30.1 across the review window) — combined Senior + jr-dev (Big Pickle) feasibility review; all figures independently re-verified on disk. Additional surfaces inspected:
- live DB census (read-only
immutable=1): 7,442 rows / 5,851 distinct UIDs / 805 champion-class at ~20:30 — 10,620 / 9,966 / 154 by ~20:45 after the parallel Genome Evaluation DB rebuild (census volatility = the F-15 argument for the manifest-as-census rule); - master parquet set (
Exports/Master/): six production masters rebuilt 2026-09-22 + DI variant datasets; Backtesting/engine/simulate.py:556-563— existing intra-barunr_best/unr_worstextremes (D-10 reuse block); same-bar exit block; pending fill/expiry window;Backtesting/backtest.py:250—BACKTEST_VERSION = "3.30.1"single source (splash banner v3.30.0 lags it — staleness class persists, P0.1);Backtesting/backtest.py:4140-4195— trade log gated onnot silent or include_trades(Phase-1 re-replay scope);extract_champion_daily_pnl.py:170-183,:289-301— preload print pattern (stdout-interleaving root cause for the "mislabel", F-14) and worker preload architecture (Phase-1 template).
Status: Draft contract. Update only through an approved Gate 0 decision or an explicit post-implementation verification refresh.