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:
@@ -260,7 +260,7 @@ const REFERENCE_POLICIES = Object.freeze({
|
||||
* incumbent is first-row-wins, so it reports WHICH KIND of book has been acting
|
||||
* as "the market" — and DFS pick'em has the highest coverage in the feed.
|
||||
*/
|
||||
function rulerDelta(raw) {
|
||||
function rulerDelta(raw, allowed) {
|
||||
const { compareRulers } = require('./consensusRuler');
|
||||
const { roleOf } = require('../config/bookRoles');
|
||||
|
||||
@@ -289,6 +289,7 @@ function rulerDelta(raw) {
|
||||
|
||||
const deltas = [];
|
||||
const roleCounts = {};
|
||||
let gradeable = 0;
|
||||
let consensusAvailable = 0;
|
||||
let bothAvailable = 0;
|
||||
let disagree2pts = 0;
|
||||
@@ -296,9 +297,10 @@ function rulerDelta(raw) {
|
||||
|
||||
for (const [key, quotes] of byPropLine.entries()) {
|
||||
const line = quotes[0].line;
|
||||
const c = compareRulers(quotes, line, 'over');
|
||||
const c = compareRulers(quotes, line, 'over', { allowedBooks: allowed });
|
||||
if (c.consensus.source === 'consensus') consensusAvailable += 1;
|
||||
if (c.incumbent.book) {
|
||||
gradeable += 1;
|
||||
const r = roleOf(c.incumbent.book);
|
||||
roleCounts[r] = (roleCounts[r] || 0) + 1;
|
||||
roleCounts[`book:${c.incumbent.book}`] = (roleCounts[`book:${c.incumbent.book}`] || 0) + 1;
|
||||
@@ -317,6 +319,8 @@ function rulerDelta(raw) {
|
||||
|
||||
return {
|
||||
prop_line_groups: byPropLine.size,
|
||||
gradeable_groups: gradeable,
|
||||
gradeable_pct: byPropLine.size ? round2((100 * gradeable) / byPropLine.size) : null,
|
||||
consensus_available: consensusAvailable,
|
||||
consensus_available_pct: byPropLine.size ? round2((100 * consensusAvailable) / byPropLine.size) : null,
|
||||
comparable: bothAvailable,
|
||||
@@ -435,7 +439,7 @@ async function verify(opts = {}) {
|
||||
continue;
|
||||
}
|
||||
out.per_sport[sport] = analyseBreadth(raw, allowed);
|
||||
out.per_sport[sport].ruler_delta = rulerDelta(raw);
|
||||
out.per_sport[sport].ruler_delta = rulerDelta(raw, allowed);
|
||||
const ev = raw.find((e) => e && e.id && Array.isArray(e.bookmakers) && e.bookmakers.length);
|
||||
if (ev) firstEvent[sport] = { id: ev.id, key: PA.SPORT_KEYS[sport] };
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user