'use strict'; /** * Ranking instrument — challenger vs incumbent (2026-08-01). * * These lock the reason the challenger exists: measured on n=200 settled MLB * rows, corr(p_win, outcome) = +0.26 while corr(edge, outcome) = -0.010 under * the incumbent ruler and -0.022 under the consensus ruler. A quantity that * does not predict must not rank, gate or decide. */ const { rankGrades, rankByForecast, rankingDelta, gradeKey, takeablePWin, ranksOnForecast, FORECAST_RANKED_SPORTS, } = require('../../src/utils/gradeRanking'); const g = (player, grade, p_win, odds = -110, extra = {}) => ({ player, stat_type: 'hits', line: 1.5, direction: 'over', grade, p_win, book_odds: odds, ...extra, }); describe('rankByForecast — the challenger', () => { it('contains NO edge term: edge cannot move the order at all', () => { const a = [g('A', 'B', 0.62, -110, { edge: -99 }), g('B', 'B', 0.55, -110, { edge: +99 })]; const b = [g('A', 'B', 0.62, -110, { edge: +99 }), g('B', 'B', 0.55, -110, { edge: -99 })]; expect(rankByForecast(a).map((x) => x.player)).toEqual(['A', 'B']); expect(rankByForecast(b).map((x) => x.player)).toEqual(['A', 'B']); }); it('leads with p_win, not the grade letter', () => { // The letter measured r ~ 0.005 and is INVERTED; p_win measures +0.26. // A high-p_win C must outrank a low-p_win A. const out = rankByForecast([g('lowPwinA', 'A', 0.51), g('highPwinC', 'C', 0.74)]); expect(out[0].player).toBe('highPwinC'); }); it('keeps the takeable gate — raw p_win would crown chalk', () => { const chalk = g('chalk', 'A', 0.93, -300); // untakeable price const real = g('real', 'B', 0.61, -115); expect(takeablePWin(chalk)).toBeNull(); expect(rankByForecast([chalk, real])[0].player).toBe('real'); }); it('sorts a missing p_win LAST, never first (Number(null) === 0 guard)', () => { const out = rankByForecast([g('none', 'A', null), g('has', 'C', 0.58)]); expect(out.map((x) => x.player)).toEqual(['has', 'none']); }); it('is stable for genuinely tied rows', () => { const rows = [g('first', 'B', 0.6), g('second', 'B', 0.6)]; expect(rankByForecast(rows).map((x) => x.player)).toEqual(['first', 'second']); }); it('drops ungraded rows, like the incumbent', () => { expect(rankByForecast([g('x', null, 0.9), g('y', 'B', 0.5)]).map((r) => r.player)).toEqual(['y']); }); }); describe('rankGrades — still grade-first (p_win leading is rankByForecast only)', () => { it('leads with the grade letter, NOT p_win', () => { const out = rankGrades([g('lowPwinA', 'A', 0.51), g('highPwinC', 'C', 0.74)]); expect(out[0].player).toBe('lowPwinA'); // grade-first, unchanged }); // SUPERSEDED 2026-08-01: the edge KEY was removed from rankGrades entirely. // Removing it is safe for every sport — it takes a non-predictive signal out // without putting p_win in front (that is `rankByForecast`, gated to sports // whose own model has passed). it('edge can no longer break a tie — the key is gone for EVERY sport', () => { const out = rankGrades([ g('first', 'B', 0.6, -110, { edge: 1 }), g('second', 'B', 0.6, -110, { edge: 9 }), ]); expect(out[0].player).toBe('first'); // stable input order, edge ignored }); }); describe('rankingDelta — the challenger-first measurement', () => { it('reports how far the board moves and whether the top read changes', () => { const rows = [g('A', 'A', 0.52), g('B', 'C', 0.77), g('C', 'B', 0.64)]; const d = rankingDelta(rows, 3); expect(d.n).toBe(3); expect(d.incumbent_top).toBe(gradeKey(rows[0])); // A-grade leads incumbent expect(d.challenger_top).toBe(gradeKey(rows[1])); // highest p_win leads challenger expect(d.top_changed).toBe(true); expect(d.moved).toBeGreaterThan(0); }); it('reports zero movement when both instruments agree', () => { const rows = [g('A', 'A', 0.80), g('B', 'B', 0.60), g('C', 'C', 0.40)]; const d = rankingDelta(rows, 3); expect(d.moved).toBe(0); expect(d.top_changed).toBe(false); expect(d.top_n_overlap_pct).toBe(100); }); it('changes nothing about the inputs (pure)', () => { const rows = [g('A', 'A', 0.52), g('B', 'C', 0.77)]; const snapshot = JSON.stringify(rows); rankingDelta(rows); expect(JSON.stringify(rows)).toBe(snapshot); }); }); describe('per-sport doctrine — a sport ranks only once ITS OWN model is built', () => { // WNBA is held out as NOT-BUILT, not as failed: its -0.12 came from // NBA-template machinery run on WNBA data, which is an unbuilt model's // expected failure rather than a verdict on the sport. Same live behaviour, // corrected meaning. it('MLB may (its own model passed); WNBA may not (its model is not built yet)', () => { expect(ranksOnForecast('mlb')).toBe(true); expect(ranksOnForecast('MLB')).toBe(true); expect(ranksOnForecast('wnba')).toBe(false); }); it('no sport inherits MLB’s result — every unbuilt sport is held out', () => { for (const s of ['nba', 'nfl', 'soccer', 'nhl', 'ncaab', '', null, undefined]) { expect(ranksOnForecast(s)).toBe(false); } expect([...FORECAST_RANKED_SPORTS]).toEqual(['mlb']); }); });