Fix: the incumbent ruler respects the allow-list (correcting my own model)
My first delta run modelled the incumbent as first-row-wins over the RAW feed and reported that an EXCLUDED book was "the market" on 69% of MLB prop-lines, with prizepicks alone at 47%. That is WRONG and I caught it before it went anywhere. normalizeProps applies ALLOWED_BOOKS BEFORE gradeSlateService.dedupeProps runs, so DFS books never reach the incumbent. The allow-list, for all the coverage it costs, does keep DFS out of the ruler. incumbentFairProb now takes the allow-list (defaulting to the live ALLOWED_BOOKS) and reproduces the real chain. Two tests lock it, including that a prop with no admitted book has NO incumbent -- it is never graded at all, which is the real loss and is already measured as invisible_props. Overstating the incumbent's badness would have been as dishonest as understating it, and more persuasive. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc
This commit is contained in:
@@ -114,16 +114,26 @@ describe('consensusRuler — labelling and the median', () => {
|
||||
});
|
||||
|
||||
describe('consensusRuler — the incumbent it is challenging', () => {
|
||||
it('incumbent is literally first-row-wins, and that row can be a DFS book', () => {
|
||||
const qs = [q('prizepicks', 1.5, -119, -119), q('novig', 1.5, -104, -104)];
|
||||
const inc = incumbentFairProb(qs, 1.5, 'over');
|
||||
expect(inc.book).toBe('prizepicks');
|
||||
const LIVE_ALLOWED = new Set(['draftkings', 'fanduel', 'betmgm', 'betrivers', 'pinnacle']);
|
||||
|
||||
it('incumbent is first-row-wins AMONG ADMITTED BOOKS — the allow-list runs first', () => {
|
||||
// normalizeProps applies ALLOWED_BOOKS before dedupeProps, so a DFS book
|
||||
// sitting first in the raw feed is NOT the incumbent. Getting this wrong
|
||||
// overstates the incumbent's badness, which is its own dishonesty.
|
||||
const qs = [q('prizepicks', 1.5, -119, -119), q('betmgm', 1.5, -115, -105), q('draftkings', 1.5, -110, -110)];
|
||||
const inc = incumbentFairProb(qs, 1.5, 'over', LIVE_ALLOWED);
|
||||
expect(inc.book).toBe('betmgm');
|
||||
expect(inc.ruler_version).toBe(roles.RULER_V1);
|
||||
});
|
||||
|
||||
it('a prop with NO admitted book has no incumbent at all — it is never graded', () => {
|
||||
const qs = [q('prizepicks', 1.5, -119, -119), q('novig', 1.5, -104, -104)];
|
||||
expect(incumbentFairProb(qs, 1.5, 'over', LIVE_ALLOWED).fair_prob).toBeNull();
|
||||
});
|
||||
|
||||
it('compareRulers returns a signed delta in probability points', () => {
|
||||
const qs = [q('draftkings', 1.5, -140, 120), q('novig', 1.5, -104, -104), q('kalshi', 1.5, -103, -105)];
|
||||
const c = compareRulers(qs, 1.5, 'over');
|
||||
const c = compareRulers(qs, 1.5, 'over', { allowedBooks: LIVE_ALLOWED });
|
||||
expect(c.incumbent.book).toBe('draftkings');
|
||||
expect(c.consensus.source).toBe('consensus');
|
||||
expect(c.delta_pts).toBeLessThan(0); // dk's favourite priced over above the exchanges
|
||||
@@ -131,6 +141,6 @@ describe('consensusRuler — the incumbent it is challenging', () => {
|
||||
});
|
||||
|
||||
it('delta is null when either side is unavailable — never 0', () => {
|
||||
expect(compareRulers([q('prizepicks', 1.5, -119, -119)], 1.5, 'over').delta_pts).toBeNull();
|
||||
expect(compareRulers([q('prizepicks', 1.5, -119, -119)], 1.5, 'over', { allowedBooks: LIVE_ALLOWED }).delta_pts).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user