# A4 — Publication-lagged days-to-cover exclusion battery (pre-registration)

**Date frozen: 2026-08-01.** Provenance: quant panel 2026-08-01, item **A4**
(short-side microstructure specialist; survived adversarial review 3/3),
findings 12 and 14 (`research/2026-08-01_quant_panel_suggestions.md`). The
red team's binding sentence, transcribed verbatim: *"Red team: pin exact
baselines and bootstrap details; earliest ship post-December (a selection
change now resets the clean clock)."* Both conditions are honored below.

This document is **hash-frozen**: everything above the sentinel line is
pinned by SHA-256 in `tests/test_docs_consistency.py`
(`test_a4_dtc_battery_memo_frozen`) — the same mechanism as the December
gate memo and the micro-cap battery pre-registration. Amendments append
below the sentinel, are dated, and are allowed **only until any
return-bearing artifact of this battery exists**; after that the record is
final. Updating the pinned hash is itself a recorded act, never a silent
edit.

Plain language, one clause each: **days-to-cover (DTC)** = total shares
sold short ÷ average daily volume — how many days of normal trading shorts
would need to buy back; **squeeze** = forced short buy-backs pushing the
price up before it falls harder; **lookahead** = letting the backtest act
on data before the market actually published it; **vacuous** = the screen
almost never changes the book, so no return test could mean anything.

## 1. Hypothesis (exclusion, never harvest)

When shorting a stock is expensive or crowded, pessimists are locked out
and the price reflects optimists only (Muravyev–Pearson–Pollet class); a
momentum winner with high days-to-cover is often a squeeze the long book
buys late. The claim under test is **tail-avoidance, not new alpha**:
excluding high-DTC names from the momentum top-50 at selection should
remove a small number of structurally overpriced names. This is the exact
consumer the `short_interest` capture registration in CAPTURES.md
pre-named ("a days-to-cover exclusion screen … screened OUT, never
harvested"); its activation criterion re-binds to this registration —
this memo IS the v2 consumer registration (v1, the micro-cap battery, was
KILLED 2026-07-22 on other grounds; the kill covered that sleeve's ranking
signal, not this screen).

## 2. Mandatory precondition: the publication-lag fix (finding 14)

The engine's `universe.dtc_screen` hook used the FINRA **settlement date
as the knowledge date**. FINRA disseminates consolidated short interest
**~T+7 business days after settlement** (CAPTURES.md `short_interest`),
so every prior consumption of this panel carried a ~7-business-day
lookahead. Fixed 2026-08-01 (this registration's companion commit):

- `load_dtc_panel(dirs, publication_lag_days)` keys each panel by
  **knowledge date = settlement + `publication_lag_days` NYSE trading
  days**. Business-day shift (not calendar-day) because the dissemination
  schedule is business-day-denominated; a calendar shift would understate
  the lag across holiday clusters.
- Engine + loader default: **`publication_lag_days: 7`**. An explicit `0`
  reproduces the legacy settlement-keyed behavior and exists **only as a
  negative control** (it IS the lookahead).
- **Every computation under this registration runs at lag 7.** Any
  return-bearing run at any other lag is invalid for this registration
  and cannot be cited by it.
- The hook is config-gated OFF in production (no `universe.dtc_screen`
  key in any sleeve config): the default changes nothing live.

## 3. The screen (pinned, inherited, not tuned)

- **Exclude at selection any name whose most recent USABLE (post-lag)
  consolidated short-interest panel shows `days_to_cover > 10.0`.**
- The 10.0 threshold is **inherited verbatim from the micro-cap battery
  pre-registration §5** (`research/2026-07-22_microcap_battery_preregistration.md`),
  which pinned it before any micro-cap backtest existed. It has never
  been fit to flagship data and is not tunable under this registration.
- Names with no SI record are **INCLUDED** (missing ≠ shorted) —
  unchanged from §5.
- **One sensitivity, REPORTED and never selected:** `days_to_cover > 7.9`
  — the flagship-universe DTC p90 as measured by the panel's short-side
  specialist on the owned stores (panel finding 12). Every artifact this
  registration produces (vacuity count and, if authorized, battery runs)
  reports the 7.9 variant **alongside** the 10.0 primary. The 7.9 variant
  can never be promoted, selected, or shipped; it is descriptive output
  of the same single trial, not a second candidate.

## 4. Data (pinned)

- **`data/short_interest_history/` only**: the frozen owned FINRA
  consolidated short-interest store — **205 settlement partitions,
  2017-12-29 → 2026-06-30** (CAPTURES.md-registered, offsite-backed-up).
  It fully covers the frozen judgment window.
- The live forward-capture dir (`data/short_interest/`) is **excluded**
  so the result cannot drift as new settlements accrue — the frozen
  store makes every run of this registration reproducible.
- Prices/universe/config: the production momentum sleeve as frozen —
  `data/raw`, `config/settings.yaml`, unmodified.

## 5. Ordering (non-negotiable) and the vacuity gate FIRST

**Step 0 — this memo merges to main before anything computes.** The
vacuity tool ships in the same commit but is not run by its author; the
first run happens post-merge, by the operator.

**Step 1 — vacuity count, BEFORE any return is seen.** Tool:
`thales dtc-vacuity-count` (`src/thales/backtest/dtc_vacuity.py`),
returns-free by construction — it computes selections and set
intersections only; no equity curve, forward return, or performance
statistic of any screened variant exists anywhere until Step 2 is
authorized. Pinned definitions:

- **Unscreened top-50** at selection date *d*: the production momentum
  selection stage verbatim (filter to *d*, drop null composite signals,
  ascending composite sort, head 50) — the candidate set the engine
  hands to weighting; weighting never changes membership.
- **Selection dates**: the engine's monthly first-trading-day grid over
  the **frozen byte-identical-guard window 2010-01-01 → 2026-03-17**
  (`results/baseline_metrics.json`).
- **Usable panel** at *d*: the engine's as-of walk — the latest panel
  with knowledge date ≤ *d* (lag 7). A selection with no usable panel is
  **uncovered**.
- **Removals at *d***: |top-50 ∩ {usable DTC > 10.0}|.
- **Quarter**: calendar quarter of *d*. A quarter is **covered** iff ≥ 1
  of its selections is covered. Expected coverage starts at the 2018-02
  selection (first knowledge date ≈ 2018-01-10) — ~33 covered quarters;
  the tool reports the exact count.
- **The vacuity statistic**: total removals across covered selections ÷
  number of covered quarters. Uncovered quarters are EXCLUDED from the
  denominator — including the 2010–2017 no-data years would dilute the
  mean toward vacuous, a rigged gate.
- **Gate**: statistic **strictly < 1.0 name-change per covered quarter →
  VACUOUS**: the result is recorded, the stream stays capture-only, and
  **the battery never runs** — the registration closes as a legitimate
  cheap closure (CAPTURES.md criterion re-binds to any future v3).
  Statistic ≥ 1.0 → Step 2 is authorized, nothing more.

**Step 2 — the battery (only if authorized).** Walk-forward A/B
(production baseline vs. baseline + screen) and **both CPCV modes**
(standard + survivorship-free) under the **full 6-clause SHIP gate**
(CLAUDE.md), as paired same-session runs: candidate and baseline on the
identical harness, data snapshot, and seeds.

## 6. Baselines (pinned exactly — paths, dates, numbers)

The SHIP comparison is judged against the paired same-session baseline
leg; that leg must be sanity-checked against these pinned artifacts, and
any discrepancy is REPORTED in the battery output (never silently
adopted, never a reason to re-pick a flattering baseline):

- **Walk-forward baseline eval**:
  `results/evaluations/eval_baseline_baseline_2026-06-09_postaudit.json`
  (2026-06-09, post-audit). 5 windows, per-window Sharpe
  W1 +0.254 / W2 +1.065 / W3 −0.220 / W4 −0.180 / W5 +0.333;
  aggregate mean 0.250; bootstrap Sharpe 0.4166 [−0.2193, 0.9977].
- **Standard CPCV baseline**:
  `results/diagnostics/cpcv_2026-06-09T21-36-25.json` (2026-06-09).
  PBO **75.0%**, observed Sharpe **0.787**, DSR **0.943**, IS-OOS corr
  **−0.696**, 15/15 OOS paths positive.
- **Survivorship-free CPCV baseline**:
  `results/diagnostics/cpcv_2026-06-09T21-42-01.json` (2026-06-09).
  PBO **37.5%**, observed Sharpe **0.532**, DSR **0.698**, IS-OOS corr
  **−0.194**, 15/15 OOS paths positive.
- Context only (not a judgment baseline): the monthly code-integrity
  tripwire's survfree-golden run
  `results/diagnostics/cpcv_2026-08-01T02-16-30.json` — PBO 50.0%,
  observed 0.524, DSR 0.613 (the DSR drift vs 07-21's 0.665 is the
  trial-ledger issue panel item C3 owns, not a strategy change).
- Standing caveat carried onto every absolute Sharpe: the frozen-window
  headline is the **rank-1-of-8 start-day draw** (`thales
  start-day-sweep`); quote WITH the spread.

## 7. Bootstrap and harness details (pinned)

Transcribed from the harness this registration will run
(`src/thales/backtest/evaluate.py` as of this freeze) — pinned so the
battery cannot quietly run under different inference settings:

- Walk-forward: `n_windows = 5`, `initial_train_pct = 0.60`.
- Bootstrap: Politis–Romano **stationary block bootstrap** on daily
  returns, expected block length **10** trading days, wrap-around;
  `n_bootstrap = 1000`; `seed = 42`; **95% CI** at the 2.5/97.5
  percentiles.
- A/B deltas: **paired** stationary-block bootstrap — identical block
  indices for baseline and candidate in each resample.
- Primary metric: `sharpe_ratio`. Walk-forward verdict **BETTER** iff
  the paired delta CI excludes 0 in the favorable direction (SHIP gate
  clause 1); INCONCLUSIVE does not ship.
- CPCV: `n_groups = 6`, `k_test = 2`, `purge_days = 252`,
  `embargo_days = 5` (production `cpcv:` block); survivorship-free mode
  is the mandatory confirmation (clause 3); if any margin is borderline,
  IS-OOS corr must be robust at `n_groups = 8` too (clause 4).

## 8. Kill criteria (pinned)

Judged on the paired same-session runs, primary screen (DTC > 10.0, lag
7) only:

- **The overfitting score worsens** — PBO above its own-session baseline
  leg in EITHER CPCV mode (any increase; PBO moves in 12.5-point quanta,
  so any movement is one full path) → **KILL**.
- **Any single walk-forward window regresses ≥ 0.05 Sharpe** vs. the
  baseline leg → **KILL**.
- Anything short of the full 6-clause SHIP gate → **NOT SHIPPED** (and
  the verdict recorded; no partial adoption, no "keep it as a soft
  filter").
- A kill or a vacuous verdict is final for this hypothesis version;
  relitigation requires a new hash-frozen registration.

## 9. Ship timing and mechanics (pinned)

- **Earliest ship: post-December** — after the 2026-12-01 go-live gate
  decision. A selection change resets the forward paper track's clean
  clock (`oos_monitor.since` re-pin), and the December gate needs the
  incumbent's clock intact; even a full SHIP-gate pass parks the change
  until then. This is the red team's second binding condition.
- Shipping (if ever) = enabling `universe.dtc_screen` (with
  `max_days_to_cover: 10.0`, `publication_lag_days: 7`, the frozen dirs)
  in `config/settings.yaml`, keystone-pinning those keys in
  `utils/config_guard.py` in the same commit, re-baselining the
  byte-identical guard (selection changes move it by design), and
  re-pinning `oos_monitor.since` — its own reviewed PR, never a silent
  edit.

## 10. Trial accounting and cross-registrations

- This registration is **one (+1) trial** on the multiple-testing ledger
  at freeze (context at freeze, per panel finding 9: 274 eval files, 113
  axis-sets, ledger reform owned by item C3). The 7.9 sensitivity is
  descriptive output of the same trial — it can never be selected, so it
  is not a second candidate.
- **B3 (SEC fails-to-deliver)**, if its own memo freezes before the
  battery runs, enters the battery output **REPORTED-ONLY**, counted as
  its own (N+1) trial per its registration; an FTD pass with a DTC fail
  ships **NOTHING** — FTD can never substitute for the primary.
- **B2 (option-implied borrow-fee wedge)** enters this battery's scope
  only once its own memo is actually frozen; until then it does not
  exist for this registration.

## 11. Expected modal outcome (recorded at freeze)

**Vacuous-or-inconclusive.** The proposing specialist's own prior: the
flagship universe is liquid large-caps where DTC > 10 is rare (universe
median 4.3, p90 7.9 per panel finding 12), so the screen
plausibly moves ~0–1 names per quarter. Recording this now makes the
cheap closure legitimate: if the vacuity gate fires, the honest reading
is "measured, immaterial on this universe, closed," not "failed."

## 12. Observed at registration (auditable no-peek disclosure)

What the author had seen when this text froze: the DTC store's coverage
metadata (205 partitions, 2017-12-29 → 2026-06-30 — directory listing,
no values); the flagship-universe DTC quantiles (median 4.3, p90 7.9) as
measured and published in the panel report (finding 12); the panel
report. **No vacuity count had been computed; no screened flagship
selection or backtest had ever been run; no return of any screened
variant existed.** The only DTC-screen backtest exposure in the shop's
history is the micro-cap battery (different universe, KILLED
2026-07-22) — which is precisely why its untuned 10.0 threshold is the
inherited pin.

<!-- SENTINEL: A4 registration v1 ends here; amendments append below this line. -->
