6.0 KiB
Portfolio-capacity bracket — frozen specification
Date frozen: 2026-08-05 Branch: research/portfolio-capacity-rebalancing Runner: scripts/run_portfolio_construction_matrix.py
Question and motivation
The daily Phase A production control (a0_control: close fill, 30-session maximum hold, 1% fixed-fractional risk, no correlation or volatility overlay) recorded 472 trades and 519 otherwise qualified entries rejected because the ten-position book was full. The blocked share is 519 / (519 + 472) = 52.4%. The book is therefore materially arrival-order constrained.
This supersedes the older statement that the ten-slot cap never bound. That statement came from a shorter, weekly, pre-gate-reset replay and is not evidence about the current daily strategy.
The study brackets the value of capacity before tuning replacement details. It does not contain a formal promotion rule or automatically change production. Because the current ~505-name production membership is projected backward, paired arm-versus-control differences are the primary evidence. Absolute profitability is descriptive and survivorship-biased.
Frozen arms
- cap10_incumbent: exact production-style cap-10 control, no displacement.
- cash_unbounded: no position-count cap; cash/no leverage and the existing 20% per-position notional ceiling remain. Reject an entry if actual initial stop-risk after cash/notional sizing is below 0.5% of marked equity.
- cap10_weekly_top10: on the final trading session of each ISO week, rank holdings plus fresh same-day qualified entrants and retain the top ten.
- cap15_incumbent: cap 15, no displacement.
All arms use the frozen Phase A control configuration: daily candidate replay, live-like full-universe residual-momentum/low-volatility 80/20 rank, activation threshold 80, normal gate-reset re-entry, close fill, 3×ATR trail, 30-session maximum hold, 1% risk, and costs of 0.10% and 0.20% per fill.
The daily replay uses zero outcome horizon: setup and rank observations continue through the snapshot's last session because portfolio simulation, unlike outcome grading, does not require 30 future bars.
Weekly-selection mechanics
- Ordinary exits run before entries/rebalancing.
- Open slots may still fill from daily qualified entries during the week.
- On the final ISO-week session, current holdings and that day's fresh qualified entrants use the full-universe strategy_rank for that same date.
- Stored entry-day rank is never used.
- Holdings with missing current rank/data are protected and consume a slot; entrants missing rank are ineligible.
- Incumbents win exact rank ties; symbol is the deterministic final tie-breaker.
- Rebalance exits pay costs and bypass cooldown/post-stop state.
- Report entrant-pool sizes, replacements, turnover, and same-symbol re-entry within 5/10/20 sessions.
Frozen cohorts
research.sqlite is expected to cover 2016-01-04 through 2026-07-17. Residual momentum requires 252 benchmark sessions. Empty-book starts additionally require 504 prior scoring sessions and 252 forward measurement sessions.
- Empty book: first eligible session of each month, approximately January 2019 through July 2025; start with no positions and measure 252 sessions.
- Warm book: first session of each year 2019–2025 is the measurement anchor. Seed the portfolio on the first session of every ISO week falling 63–126 trading sessions before the anchor, carry all positions and gate-reset state forward, and measure the same 252-session anchor window.
Warm portfolio returns reset to marked equity immediately before the anchor session. P&L after the anchor from carried positions belongs to portfolio returns, while trade EV includes only entries on or after the anchor. Remaining positions liquidate at the last measurement close with costs.
The validate-only mode must print realized cohort counts and fail unless both protocols contain the seven annual clusters 2019–2025 and every warm anchor has at least 12 seeds.
Reporting
Primary reported measures:
- net EV per trade in R, with costs and actual initial stop-risk dollars;
- Calmar (CAGR / max drawdown);
- profit factor on net trade R;
- Gain-to-Pain (sum of all monthly returns / absolute sum of negative months);
- Sortino using daily returns and zero target.
Also report total return/CAGR, maximum drawdown, Sharpe, win rate, time underwater, exposure, cash, average/peak positions, sessions at capacity, turnover, costs, qualified/admitted/blocked opportunities, and minimum-risk rejections.
For each arm/protocol/cost/metric, pair identical paths with cap10_incumbent, take the median paired delta within each start year or annual anchor, show all seven cluster values, and headline their median.
Initialization dispersion is reported separately for EV and Calmar: calculate the seed-path IQR within each warm anchor, divide by the paired control IQR, show all seven ratios, and headline their median. Do not combine them into a composite.
For context only, run a deterministic 10,000-replicate cluster bootstrap over the seven paired annual summaries and report the central 90% percentile interval for median EV and Calmar deltas and warm IQR ratios. These intervals are not promotion gates, independent-population confidence claims, or formal inference.
Reproducibility and execution
Candidate replay/ranks cache under reports/.cache; each matrix cell checkpoints atomically and resume verifies a fingerprint over the implementation commit, this specification hash, snapshot SHA-256, cache key, arm definitions, costs, and cohort manifest. An authoritative run refuses a dirty worktree.
Preflight:
python scripts/run_portfolio_construction_matrix.py backtest_snapshots/research.sqlite --run-id prod505-capacity-bracket-daily-v1 --validate-only
Authoritative run:
python scripts/run_portfolio_construction_matrix.py backtest_snapshots/research.sqlite --run-id prod505-capacity-bracket-daily-v1 --workers auto --resume
Commit only the compact final JSON and Markdown reports. Raw curves, trades, candidate caches, and checkpoints remain ignored.