Files
vyndr/scripts/grade-correlation-proof.sql
builtbykev ea1157d709 report: grade fix Part 1 — the p_win-vs-fair_prob rebuild is REFUTED by the data
STOPPED at the Part 1 gate. Nothing rebuilt, no grade changed, no cutover.

THE FINDING: grading on p_win vs fair_prob does not work. All three candidate
edge formulations correlate NEGATIVELY with outcomes, on both sports, overall,
and in both time splits (n=432 decided rows carrying p_win AND fair_prob_lock):

  ALL  n=432  champ -0.0016  p_win ALONE +0.1221  additive -0.0615  ratio -0.1161  logodds -0.0438
  MLB  n=240  champ +0.0984  p_win ALONE +0.2278  additive -0.0336  ratio -0.1350  logodds -0.0124
  WNBA n=192  champ -0.1143  p_win ALONE -0.0842  additive -0.1326  ratio -0.1281  logodds -0.1243

Subtracting the market's lock-time fair probability destroys and inverts the
signal. The plain reading: props where the model most disagrees with the market
are LESS likely to hit — the market is better than the model, so "edge vs market"
is anti-predictive here, while the raw probability retains some skill alone.

WHAT DOES CARRY SIGNAL: p_win alone, MLB only, and it is modest. Time-forward
split — TRAIN (07-21..07-26, n=120) r=0.2770; HOLDOUT (07-26..07-30, n=120)
r=0.1647, with the additive edge negative in BOTH halves. So p_win survives
forward validation directionally but the holdout is NOT significant (t~1.81,
p~0.07). Suggestive, not proven.

WNBA MUST ABSTAIN: every measure negative including p_win itself (-0.084). Forcing
one threshold across both sports would make a coin-flip sport look sharp, which the
order forbids.

LOOKAHEAD GUARD SATISFIED: fair_prob_lock is the lock-time field, populated on 432
decided rows, range 0.145-0.713. closing_prob (415 rows) is the CLOSE and was NOT
used in any correlation — using it would have manufactured a correlation.

SAMPLE REALITY: 1103 decided rows but only 432 carry both instrument fields, so a
per-sport train/holdout split leaves ~120 per half — enough to show direction, not
to certify a letter ladder.

I did not tune toward a win: three pre-registered candidates were tested and all
three failed; picking a fourth because the first three lost is the overfitting the
order guards against. Recommended instead: grade MLB on p_win alone with WNBA
abstaining and label it modest/accruing (A-RATED hold stays); or wait ~6 weeks for
n~500 MLB; or investigate WHY the market-relative edge inverts, which is the more
valuable question.

Both queries committed at scripts/grade-correlation-proof.sql so no number here
has to be taken on trust. Working settlement untouched; dead resolve endpoint not
wired.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc
2026-07-31 19:57:21 -04:00

32 lines
1.6 KiB
SQL

-- GRADE CORRELATION PROOF (2026-07-31). Re-runnable evidence for
-- specs/grade-fix-part1-investigation.md. LOOKAHEAD GUARD: fair_prob_lock is the
-- LOCK-TIME market probability; closing_prob (the close) is deliberately unused.
-- 1) Candidate edge formulations vs outcome, per sport.
with r as (
select sport, (outcome='hit')::int as won, p_win::numeric as p, fair_prob_lock::numeric as f,
case grade when 'A+' then 10 when 'A' then 9 when 'A-' then 8 when 'B+' then 7 when 'B' then 6
when 'B-' then 5 when 'C+' then 4 when 'C' then 3 when 'C-' then 2 when 'D' then 1 else 0 end as champ_idx
from ledger_entries
where user_id is null and outcome in ('hit','miss')
and p_win is not null and fair_prob_lock is not null and grade is not null
)
select sport, count(*) n,
corr(champ_idx, won) champ_letter_r,
corr(p, won) p_win_alone_r,
corr(p - f, won) additive_edge_r,
corr(p / nullif(f,0), won) ratio_edge_r,
corr(ln(p/(1-p)) - ln(f/(1-f)), won) logodds_edge_r
from r group by sport;
-- 2) Time-forward split (overfitting guard), MLB.
with r as (
select game_date, (outcome='hit')::int as won, p_win::numeric as p, fair_prob_lock::numeric as f
from ledger_entries
where user_id is null and outcome in ('hit','miss') and sport='mlb'
and p_win is not null and fair_prob_lock is not null
), s as (select *, ntile(2) over (order by game_date) half from r)
select half, count(*) n, min(game_date) from_date, max(game_date) to_date,
corr(p, won) p_win_alone_r, corr(p - f, won) additive_edge_r
from s group by half order by half;