# Effective initial-risk floor A/B - frozen specification

> ## ⛔ CLOSED 2026-08-05 — NEGATIVE. DO NOT RUN.
>
> This A/B was never executed because the capacity-bracket run already contains
> it. `cap15_incumbent` (peak 12, zero blocked, no floor) and `cash_unbounded`
> (peak 12, floor) have the same effective capacity and differ essentially only
> by `min_initial_risk_fraction`. Paired over 175 paths, the 0.5% floor gives
> **EV/trade +0.032 and profit factor +0.073, but CAGR −0.753pp, total return
> −0.765pp, Sharpe −0.047, Calmar −0.051**, and it removes 11.4 trades per path
> while never adding one (174 worse / 0 better).
>
> **The pass rule below is unsafe.** It promotes on paired EV, and the floor
> raises EV per trade *precisely by deleting trades* that were net positive
> contributors — so this specification would have shipped a change costing
> 0.75pp of CAGR. Any successor study must decide on CAGR/total return and treat
> EV per trade as a diagnostic.
>
> See [portfolio-capacity-bracket-findings.md](portfolio-capacity-bracket-findings.md#correction-2026-08-05-ev-per-trade-was-the-wrong-lens).
> Retained as a record of what was specified and why it was withdrawn.

Date frozen: 2026-08-05
Branch: research/portfolio-capacity-rebalancing (deleted; tag `research/portfolio-capacity-final`)
Runner: scripts/run_portfolio_construction_matrix.py (not on main; see tag)
Study ID: risk-floor-ab

## Question

Does rejecting an otherwise qualified cap-10 entry when its actual initial
stop-risk after cash and notional sizing is below 0.5% of marked equity improve
trade selection?

The completed capacity bracket cannot answer this. Its cash_unbounded arm
removed the count cap and applied the 0.5% floor simultaneously. In the 70 paths
where the control cap never bound, that arm still raised mean EV from 0.328 to
0.399 R and profit factor from 1.60 to 1.75 while trades fell about 8% and
exposure stayed nearly flat. Capacity was a no-op in those paths, so the floor
is the plausible cause, but the prior arm remains confounded.

This A/B changes only the floor. It has no formal promotion gate and does not
automatically change production.

## Frozen arms

1. cap10_incumbent: current production-style cap-10 control, with no minimum
   effective-risk floor.
2. cap10_min_risk_005: the same cap-10 strategy, rejecting an entry only when
   actual initial stop-risk after cash/notional sizing is below 0.5% of marked
   equity.

Both arms have max_positions=10, weekly replacement disabled, 1% target risk
per trade, and identical admission ordering. The only differing simulator
argument is min_initial_risk_fraction: None versus 0.005.

All other settings remain the frozen daily Phase A control: current production
construction universe, full-universe residual-momentum/low-volatility 80/20
rank, threshold 80, normal gate-reset re-entry, close fills, 3x ATR trail,
30-session maximum hold, 20% per-position notional ceiling, no leverage, and
costs of 0.10% and 0.20% per fill.

Every priced symbol contributes to the daily cross-sectional rank. Rank-only
symbols cannot submit trades. Validation retains the 450-600-symbol production
construction guardrail and the legacy-snapshot column-scoped loader.

## Frozen cohorts

Reuse the completed bracket's point-in-time daily candidate/rank cache and
cohort manifest:

- Empty book: first eligible session of each month in 2019-2025, with 504 prior
  scoring sessions and 252 measurement sessions. This is the primary start-date
  evidence.
- Warm book: weekly seeds 63-126 sessions before each 2019-2025 annual anchor,
  with state carried into the same 252-session measurement window. This is a
  state-carrying replication, not independent evidence.

The expected realization is 78 empty-book paths, 97 warm paths, seven annual
clusters in each protocol, two costs, two arms, and 700 cells.

Do not use warm-seed IQR as evidence. Six of seven completed-bracket anchors
were structurally degenerate because fractional sizing is scale invariant and
the 30-session maximum hold washed out books before anchors. The 2023 exception
shows that state carrying itself works.

## Reporting and interpretation

For every protocol and cost, pair identical paths. Report:

- mean, median, P25, and P75 paired net-EV changes in R;
- positive-path and bit-identical-path fractions;
- the median paired delta within each year and the median across seven years;
- simple 90% cluster-bootstrap context for EV and Calmar, with no CI gate;
- mean paired PF, Gain-to-Pain, Sortino, Calmar/MAR, CAGR, maximum drawdown,
  total return, and Sharpe changes;
- trades, floor rejections, holding time, cash, gross exposure, average/peak
  positions, turnover, and costs.

Means and identical-path fractions must appear beside medians so inert cohorts
cannot turn a left- or right-skewed treatment into a misleading zero headline.
For these 252-session windows, the implementation's full-window Calmar is CAGR
divided by maximum drawdown, the same numeric definition commonly called MAR;
do not present the duplicate label as a second independent metric.

Today's production membership is projected backward. Use paired differences
for the treatment conclusion; absolute profitability remains descriptive and
survivorship-biased. Empty and warm protocols cover the same seven market years
and must not be interpreted as independent replications.

Interpretation is deliberately simple:

- a positive result means the isolated floor improves the paired EV
  distribution without an economically important loss of total-return or
  drawdown quality;
- a negative result closes the floor;
- mixed EV/portfolio-quality results are reported as a trade-off, not forced
  through a composite score.

## Reproducibility and macOS execution

The authoritative run refuses a dirty worktree. Its fingerprint includes the
implementation commit, this specification hash, snapshot hash, candidate-cache
key, construction view, cohort manifest, arm definitions, costs, and study
version. Cells checkpoint atomically and --resume verifies the fingerprint.

From the repository root on macOS:

    python3 -m venv .venv
    ./.venv/bin/python -m pip install -e '.[dev]'

Preflight, reusing the completed bracket's candidate/rank cache:

    ./.venv/bin/python scripts/run_portfolio_construction_matrix.py +      backtest_snapshots/research.sqlite +      --study risk-floor-ab +      --run-id prod505-effective-risk-floor-ab-daily-v1 +      --candidate-cache reports/.cache/prod505-capacity-bracket-daily-v1-candidates.pkl +      --workers 8 +      --resume +      --validate-only

Authoritative run:

    ./.venv/bin/python scripts/run_portfolio_construction_matrix.py +      backtest_snapshots/research.sqlite +      --study risk-floor-ab +      --run-id prod505-effective-risk-floor-ab-daily-v1 +      --candidate-cache reports/.cache/prod505-capacity-bracket-daily-v1-candidates.pkl +      --workers 8 +      --resume

On an M2 Pro, eight workers is the explicit high-utilization setting. Use six
instead on a memory-constrained machine; auto intentionally caps itself at six.
Changing worker count does not change the fingerprint or results.

Commit only the compact final JSON and Markdown reports. Candidate caches,
checkpoints, raw curves, and trade ledgers remain ignored.
