The receipts · published verbatim from the working file
Bat speed as an early signal — pre-registration
Frozen 2026-07-13, before any 2026 bat-tracking data was examined.
Last changed
Pre-registration — bat speed as an early-season signal (H-BT)
Frozen 2026-07-13, before any 2026 bat-tracking or 2026 outcome data was examined. The git timestamp of this file is the freeze receipt. One confirmatory run; results published regardless.
Motivation
Statcast bat tracking (public since 2024) measures bat speed on
every competitive swing — roughly 3–4x the April sample size that
EV95 gets from balls in play. Exploratory work on the train years
(2024, 2025; catalyst/analysis/bt_exploratory.py) found:
-
Bat speed is the most stable metric we have measured: YoY r = 0.914 (2024→2025), above EV95's 0.884 in our build.
-
At ANNUAL granularity it adds nothing beyond EV95 (r ≈ 0.89 collinearity; incremental betas insignificant). Season-scale integration is NOT on the table.
-
At APRIL granularity it adds real signal. Predicting May-onward value on contact from April-only inputs (non-overlapping design):
2024 (n=198): bat_speed beta +0.413, p < 0.0001 2025 (n=208): bat_speed beta +0.231, p = 0.015In both years April bat speed out-predicted April results (aval) and rivaled April EV95.
Data (all archived before the freeze)
- Savant bat-tracking leaderboard CSVs, raw archived at
data/raw/battracking/(seasons 2024–2026; April windows 2024/2025/2026), parquets atdata/parquet/bat_tracking_*. The 2026 files were fetched but ONLY row counts observed. - Outcomes from the engine's own lake:
bip_{season}.parquetjoined to game dates viaevents_{season}.parquet; value map {1B .9, 2B 1.25, 3B 1.6, HR 2.0}, the same linear weights the engine uses everywhere.
PRIMARY hypothesis (H-BT-E, confirmatory)
On 2026 data, untouched at freeze time:
z(aval_May1_to_Jul12) ~ z(aval_April) + z(ev95_April)
+ z(bat_speed_April) [OLS]
Sample: batters qualified on the Savant April-2026 window (minSwings=q) AND >= 30 April balls in play in our lake AND
= 60 balls in play from 2026-05-01 through the data on hand at run time (ingested through last night's press).
SUCCESS = bat_speed coefficient positive AND two-tailed p < 0.05. Anything else — wrong sign, p >= 0.05 — is a MISS and gets published exactly like a hit would.
SECONDARY (reported, with criterion)
Stability 2025→2026: YoY r of avg bat speed among batters qualified in both seasons. SUCCESS = r >= 0.75.
TERTIARY (reported, no criterion — expected null)
2026 season-to-date aval ~ 2025 aval + 2025 ev95 + 2025 bat_speed. The train years say bat speed adds nothing at annual scale; we report this to keep ourselves honest about scope, not to claim it.
Integration gate
If PRIMARY passes: bat speed earns candidate status as an EARLY-SEASON de-noiser only — an April/May adjustment to the boards' contact-quality estimate while EV95 is still stabilizing. Any product integration is designed separately, ratified by the founder, and validated on its own terms before it touches a board. If PRIMARY fails: the finding dies in public, filed next to the clutch null.
Caveats declared in advance
- Two train pairs is thin history; bat tracking simply hasn't existed longer. The 2024 leaderboard covers from early April 2024 (public rollout backfilled), so "2024 April" is slightly truncated at the front.
- The confirmatory outcome window ends at run time (~2026-07-12), not season's end. It measures 2.5 months of "the rest of the season," which matches the operational use (early boards), not a full-season claim.
- Savant qualification thresholds ("q") are Savant's, not ours; they gate the sample identically in train and test.
- One evaluation. No re-runs, no threshold shopping. The script
is
catalyst/analysis/bt_confirmatory.py, committed before it was executed.