# C1 — Go-live leverage diff: cap gross exposure, not the scalar

**Date:** 2026-08-01
**Status:** PRE-REGISTERED, BINDING. Committed BEFORE any twin backtest runs
(the runner, `scripts/run_c1_twins.sh`, mechanically refuses to execute if this
memo is absent). No file under `results/diagnostics/c1_twins/` exists at
registration time.
**Source:** quant panel 2026-08-01, item **C1** (CRO; 3/3 survive) + finding 1
(`research/2026-08-01_quant_panel_suggestions.md`).
**Scope:** amends WHICH KNOB the pre-specified December go-live config diff
turns. It does NOT touch the go-live gate's criteria, dates, or thresholds
(`go_live:` in `config/settings.yaml`, keystone-pinned, unchanged), and no
production config VALUE changes at registration — the new engine mode defaults
to today's behavior byte-identically.

---

## 1. The category error, stated precisely

Plain language first: "gross exposure" is how many dollars of stock the book
holds per dollar of equity (1.0x = fully invested, no borrowing). The "vol-
target scalar" is a multiplier the engine applies to the book to steer its
volatility to the 12% target.

`risk.max_leverage` does **not** cap gross exposure. It caps the **scalar**:

```
# portfolio/risk.py::vol_target_scalar
scalar = target_vol / realized_vol
return min(scalar, max_leverage)          # <- max_leverage caps THIS
```

The final book, built in the shared construction path
(`portfolio/construct.py::build_target_weights`, used byte-identically by the
backtest engine and the live executor), is:

```
final_gross = kelly_composite_gross × min(target_vol / realized_vol, max_leverage)
```

where `kelly_composite_gross` is the Kelly-scaled, limits-applied book's gross
before vol-targeting. The panel's verified live decomposition (2026-08-01):

```
gross 0.419 = kelly_gross 0.140 × vol_scalar 2.996
```

The pre-specified go-live diff (settings.yaml `go_live:` comment block:
`risk.max_leverage 3.0 -> 1.0`) therefore clamps the SCALAR at 1.0, and the
live book becomes:

- **exposure ≈ 0.14x** (`0.140 × min(2.996, 1.0) = 0.140`) — not the ~1.0x the
  diff's own rationale reasons about;
- **volatility ≈ 4%** annualized (`0.140/0.419 × 12% ≈ 4.0%`) — one third of
  the validated 12% target;
- **a configuration no backtest ever validated** (every validated run — the
  byte-identical guard, CPCV both modes, all walk-forwards — ran
  `max_leverage: 3.0`);
- **zero margin interest saved**: at 0.419x gross the account borrows nothing.
  There is no borrowed dollar for the cap to save interest on.

The literal diff's own written rationale is exposure-denominated ("Reg-T caps
overnight gross at 2.0x", "Alpaca margin ~6.25%/yr makes borrowing
edge-negative", "the economic ceiling is ~1.0x") — its intent is "never hold
more than 1.0x of equity in stock", i.e. a bound on GROSS, applied to the wrong
knob. Implementation contradicts its own registered intent — the ratified
implementation-matches-intent amendment class (panel §2: amendments land only
"where implementation demonstrably contradicts its own registered intent";
the rule has never consumed data — no go-live flip has occurred).

## 2. The amendment: "no borrowed dollar"

Cap **FINAL GROSS** at 1.0x. Effective scalar cap:

```
scalar_cap = min(max_leverage, target_gross_cap / kelly_gross)
```

with `target_gross_cap = risk.max_gross_exposure = 1.0` and `kelly_gross` the
pre-scalar Kelly-composite gross (measured after position/sector limits — the
exact book the scalar multiplies, so the cap lands on the FINAL book exactly).
Equivalently: `final_gross = min(kelly_gross × min(raw_scalar, max_leverage),
max_gross_exposure)`. This is the panel's formula verbatim
("scalar cap = min(3.0, 1.0/Kelly-gross)"): **the validated composite
everywhere the account lives, clipped only above 1.0x exposure.**
`max_leverage` KEEPS its validated scalar-cap meaning (3.0) in both modes.

### Config surface (registered values, keystone-pinned in the same change)

| key | value | meaning |
|---|---|---|
| `risk.leverage_cap_basis` | `"scalar"` (default) | today's behavior, byte-identical — `max_leverage` caps the scalar only |
| | `"gross"` | additionally cap FINAL gross at `max_gross_exposure` |
| `risk.max_gross_exposure` | `1.0` | the gross cap used by `"gross"` basis (inert under `"scalar"`) |

Guard details (implementation contract): the gross clamp divides by the live
pre-scalar gross; a zero/empty book skips the clamp (gross is 0 regardless),
and the cap value is floored at 0 (a mis-set negative cap must mean all-cash,
never a sign-flipped book). The clamp applies whenever `"gross"` basis is on —
including the vol-estimator warmup window where the scalar is implicitly 1.0 —
because "no borrowed dollar" is an account invariant, not a property of the
scalar formula.

### The amended go-live diff (effective ONLY upon ADOPT, §4)

> execution.paper -> false (unchanged from the pre-specified diff);
> **risk.leverage_cap_basis "scalar" -> "gross"**; NOTHING ELSE.
> (`risk.max_leverage` stays 3.0 — the validated scalar cap;
> `risk.max_gross_exposure` is already 1.0 in config.)

This replaces `risk.max_leverage 3.0 -> 1.0` in the pre-specified diff. If the
adoption criteria FAIL, the pre-specified literal diff stands unmodified and
the §6 disclosure publishes with the gate output.

### Disclosed property: the clamp loosens in losing streaks

Visible from the formula alone: as `kelly_gross` FALLS (Kelly de-weights after
losses), `target_gross_cap / kelly_gross` RISES — the gross clamp loosens
exactly when the book is shrinking. Gross-basis mode **clips prosperity above
1.0x and adds no drawdown protection**; relative to the literal scalar cap it
holds MORE exposure in losing streaks (the literal diff would pin the scalar at
1.0 always; gross basis lets it run to 3.0 while the composite gross is below
1.0x). This is why the amendment is not gate-shopping: it is derivable from the
formula with no forward data, it precedes the gate and any real dollar, and the
direction of its one behavioral asymmetry is risk-INCREASING in the losing
tail — nobody shops for that. (Panel C1 red-team, ratified.)

## 3. The three twins (frozen window; NOT run at registration)

All on the frozen guard window from `results/baseline_metrics.json`
(**2010-01-01 → 2026-03-17**), on the frozen owned `data/raw` snapshot, in one
sitting, via `scripts/run_c1_twins.sh` on the main checkout:

| twin | config | command core |
|---|---|---|
| **T0 baseline** | current config, no overrides | `thales backtest --start <s> --end <e>` |
| **T1 gross-cap go-live** | the amended diff's risk basis | `… -p risk.leverage_cap_basis=gross` |
| **T2 literal go-live** | the pre-specified literal diff | `… -p risk.max_leverage=1.0` |

T2 is measured for the record — it quantifies exactly what the literal diff
would deploy (the §6 disclosure's numbers). **Adoption compares T1 vs T0
only.** Two additional pinned instruments run in the same batch:

- **Walk-forward A/B** (criterion 3): `thales evaluate -p
  risk.leverage_cap_basis=gross --name c1_gross_cap_twin --tag c1-calibration`
  at the harness defaults (n_windows 5, train 60%, bootstrap 1000, seed 42).
- **Survivorship-free CPCV pair** (criterion 2): `thales cpcv
  --survivorship-free --golden` twice — once on the baseline config, once with
  `-p risk.leverage_cap_basis=gross` — the honest frozen instrument (golden
  store + iShares membership, the monthly-revalidation convention), CPCV
  params from config (n_groups 6, k_test 2, purge 252, embargo 5). Only the
  SAME-BATCH pair is comparable (PBO levels are snapshot-sensitive; historical
  PBO numbers are not the baseline here).

## 4. ADOPTION CRITERIA (pinned now, before any twin runs)

Verbatim from the panel report, which is binding:

> "Kill: adopt only if the gross-cap twin differs on <10% of days,
> survivorship-free PBO <= baseline, no window regresses 0.05 Sharpe; else
> keep the diff and pre-write the honest disclosure." (item C1)

> "adopt only if the corrected twin matches the validated baseline on >90% of
> days with no overfitting-score or per-window regression, else keep the diff
> as written and pre-write the honest under-vol disclosure." (§1, rank 1)

Pinned operationalization (evaluated mechanically by
`scripts/c1_twins_verdict.py`; boundary semantics are part of the pin):

1. **Day-difference < 10% (strict).** Compare T1 vs T0
   `weights_history.parquet` over the frozen window. **A day differs if ANY
   weight differs by > 1e-9** (absolute; symbols absent from a day's book count
   as weight 0.0). A date present in exactly one history differs by
   definition; the denominator is the union of dates across both histories.
   ADOPT requires `differing_days / total_days < 0.10`; exactly 0.10 FAILS.
2. **Survivorship-free PBO(T1 config) ≤ PBO(T0 config)** from the §3 same-batch
   CPCV pair. Equality PASSES (the mode is not required to improve the
   overfitting score, only not to worsen it).
3. **No walk-forward window regresses ≥ 0.05 Sharpe.** From the §3 evaluate
   run: FAIL iff any window has `(candidate − baseline) sharpe_ratio ≤ −0.05`
   (a regression of exactly 0.05 fails — the SHIP-gate reading of "regresses
   by ≥ 0.05").

**ALL THREE pass → ADOPT** the amended diff (§2). **Any failure →
KEEP-LITERAL**: the pre-specified diff stands as written and the §6 disclosure
is published alongside every go-live gate reading. The verdict is printed by
the evaluator with the numbers; no discretion at read time.

Upon ADOPT (deliberate follow-up commit, citing this memo): update the
`go_live:` diff comment in settings.yaml to the amended text, and note that at
the actual go-live flip the byte-identical baseline must be re-pinned (§5). The
`risk.leverage_cap_basis` keystone stays `"scalar"` until the real December
flip — adoption changes the PLANNED diff, not the running paper config.

## 5. Re-baseline rule and calibration logging

- **The re-baseline rule applies** (panel C1 red-team: "a NEW engine cap mode —
  the re-baseline rule applies"): gross basis is a new engine mode with no
  validated baseline. At registration the DEFAULT is byte-identical, so
  `results/baseline_metrics.json` is untouched and the guard stays green. IF
  the amended diff is adopted AND the December flip occurs, the production
  config becomes a new engine configuration and the byte-identical baseline
  must be re-pinned at flip time — T1's frozen-window metrics (retained under
  `results/diagnostics/c1_twins/backtest_gross_cap/`) are exactly that
  measurement.
- **These runs are CALIBRATION, not alpha trials.** They measure an
  implementation-matches-intent fix against pre-pinned equivalence criteria;
  nothing ships from their levels. The evaluate run carries the
  `c1-calibration` tag; the runner writes a manifest
  (`results/diagnostics/c1_twins/manifest.json`) recording commands, window,
  data snapshot, and this memo's criteria; when the C3 append-only trial
  registry lands, these rows enter it labeled `calibration`.

## 6. Pre-written honest under-vol disclosure (published only on KEEP-LITERAL)

> **DISCLOSURE (drafted 2026-08-01, before the twins ran; published because the
> C1 gross-cap amendment failed its pre-registered adoption criteria —
> [cite the failing criterion + numbers from c1_twins_verdict.json here].)**
>
> The go-live configuration caps the vol-target scalar at 1.0
> (`risk.max_leverage: 1.0`). This knob does not cap market exposure: with the
> live book's Kelly deployment fraction at its recent ~0.14x level, the
> deployed book is ~0.14x gross exposure and ~4% annualized volatility —
> roughly one third of the validated 12% volatility target. This exposure
> level was never validated by any backtest (the validated configuration runs
> `max_leverage: 3.0`), and the cap saves no margin interest — the book
> borrows nothing at ~0.42x gross. Expected total return scales with the
> exposure gap: results under this deployment must be read as a deliberately
> under-volatized experiment, and every live-vs-paper or live-vs-validated
> comparison (including the December gate's Sharpe criterion and the placebo
> percentile) must account for the ~3x exposure mismatch before any
> conclusion about the strategy is drawn. We keep the literal diff because
> the corrected alternative failed its own pre-registered equivalence tests —
> not because the literal diff's exposure level was ever validated.

## 7. What this memo does NOT do

- No live pinned value changes now: `go_live:` values, `risk.max_leverage:
  3.0`, meanrev/vrp configs — all untouched. The new keys default to
  byte-identical behavior and are keystone-pinned at their registered values.
- It does not move the December gate, its criteria, or its interpretation memo
  (`research/2026-07-13_december_gate_interpretation_memo.md`, still binding).
- It does not run anything: computation is deferred to the runner, which
  requires this memo to exist and refuses to overwrite an existing twin batch.

---

## VERDICT APPEND — 2026-08-01 (same day; the batch ran immediately post-merge)

The §4 twin battery ran on the frozen window (`scripts/run_c1_twins.sh`,
outputs under `results/diagnostics/c1_twins/`, verdict JSON
`c1_twins_verdict.json`). All three pinned criteria evaluated mechanically:

| criterion | pinned bar | measured | result |
|---|---|---|---|
| day difference (T1 vs T0) | < 10% of days, any \|dw\| > 1e-9 | **0.00%** | PASS |
| survfree golden PBO | gross <= baseline | 0.5000 vs 0.5000 | PASS |
| per-window Sharpe regression | none <= -0.05 | deltas [0,0,0,0,0] | PASS |

**VERDICT: ADOPT.** The settings.yaml go-live diff comment now carries the
amended text (basis "scalar" -> "gross"; max_gross_exposure 1.0 is the
binding no-borrowed-dollar cap). The `leverage_cap_basis` keystone stays
"scalar" until the real December flip; at that flip the byte-identical
baseline is re-pinned per §5 (measurement retained under
`backtest_gross_cap/`). T2 (the literal diff) was measured for the record
and its under-vol numbers stand in §6 as the disclosure that is now moot
unless the amendment is ever reverted.
