Thales
← research journal
Aug 1, 2026raw markdown ↗

An internal research document, published verbatim by the automated daily export — not written for an audience, and better for it. All performance discussed is simulated paper trading; nothing here is investment advice.

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. -->