Edge-shading challenger: built + measured. Flooding NOT fixed — input scale is the bug

Challenger only. Champion grade byte-identical (verified by diff). Nothing
promoted, no live grade re-lettered, no ledger row deleted or re-settled.

BUILT src/services/challengers/efficiencyShading.js (measured-never-served):
  adjusted_edge = raw_edge * f(efficiency); grade = band(adjusted_edge) against
  ONE fixed bar (A+>=10, A>=5, B>=3, C>=1, D>=0, F<0) that never moves.
  f(e) = E_SOFTEST/e bounded to (0,1] — soft markets intact (never amplified),
  sharp shaded toward but not past zero, unscored -> f=1 and FLAGGED.
  A fence test asserts no production grade path imports it.
  Cross-market behaviour is unit-proven: the same raw 6% edge grades A in soft
  mlb:total_bases and B in sharp nba:points.

MEASURED on 1250 live ledger rows — Phase 2.5's answer is NO, the flooding is
not gone: challenger 79.0% A and 80.9% A/B (MLB 93.4% A) vs champion 0.2% A.

TWO findings explain why, and they are the point of the order:

1. The shading is a NO-OP on the live board: rows_actually_shaded = 0 of 1250.
   96.5% of rows are UNSCORED (f=1), and the one scored market present
   (mlb:total_bases) is the anchor so its f is 1.0 by construction.
   mlb:strikeouts and nba:points do not appear in the ledger at all (our
   basketball is wnba, not nba). Challenger vs baseline: 0 rows changed.

2. Placement was never the bug — the INPUT SCALE is. Against a fixed 5% bar the
   RAW edge already clears A on 100% of MLB doubles, 89.6% of hits, before any
   shading. MLB median raw edge is 60%, twelve times the bar. Decisive test:
   apply the sharpest score in the spec (f=0.647) to EVERY row — the maximum
   the design permits — and 75.8% still clear A (MLB 91.7%). Since f is bounded
   <= 1, no achievable shading can close a 12x overshoot. Moving the multiply
   from the threshold to the edge does not change the outcome.

This is edge_pct behaving as the 2026-07-29 diagnosis described: a price-free
(proj-line)/line gap whose scale is a function of line size. It is not a
betting edge, so no fixed betting-edge bar is meaningful against it.

2.6 efficient-market over-suppression: CANNOT DETERMINE — zero live rows are
shaded, so there is no efficient market in the data to over-suppress.

Phase 3: takeable tagging was completed in the previous order (migration 034,
1246/1254 rows) and is not repeated. The model-version boundary is again NOT
applied: nothing promoted, so no boundary exists.

Unblocking needs the input replaced, not the multiply moved: p_win vs
fair_prob (both already computed) instead of edge_pct, plus scores FIT from our
own record for the markets we actually grade.

Floor: 313 suites / 3899 tests green (9 new), web build exit 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc
This commit is contained in:
Kev
2026-07-31 00:39:13 -04:00
parent 896e6e1a00
commit c2c7abbb65
4 changed files with 331 additions and 1 deletions
@@ -0,0 +1,115 @@
'use strict';
/**
* EDGE-SIDE EFFICIENCY SHADING — CHALLENGER (2026-07-31, specs/edge-shading-challenger.md).
*
* MEASURED, NEVER SERVED. Nothing here touches the champion grade.
*
* THE MECHANIC (the corrected placement):
* adjusted_edge = raw_edge × f(efficiency)
* grade = band(adjusted_edge) against ONE FIXED bar, identical for every market
*
* Efficiency shades the EDGE, never the BAR. That is what makes it un-inflatable: `f` is
* bounded to (0, 1], so an edge can only ever be shaded TOWARD zero, never amplified, and the
* bar never moves. There is NO target distribution — however many props clear the honest bar,
* clear it.
*
* WHY f IS A RATIO OF THE SOFTEST SCORE:
* f(e) = E_SOFTEST / e
* At the softest market (e = E_SOFTEST) f = 1.0 → the edge is left INTACT, never amplified
* above raw (the guardrail). Sharper markets shade down monotonically — f(0.60)=0.917,
* f(0.80)=0.688, f(0.85)=0.647 — approaching but never reaching zero. The reasoning: a 3% edge
* against a sharp line is more likely illusory, so it counts for less; an edge against a lazy
* line is more likely real.
*
* A market with NO efficiency score gets f = 1 (no adjustment) and is FLAGGED — never silently
* scaled by a number we invented.
*/
/** Spec scores. Keyed `sport:stat`; `sport:*` is a sport-wide fallback. */
const MARKET_EFFICIENCY = Object.freeze({
'nfl:passing_yards': 0.85,
'nba:points': 0.80,
'mlb:strikeouts': 0.60,
'mlb:total_bases': 0.55,
'nba:role_player': 0.55,
});
/** The softest scored market — the calibration anchor where f = 1 (edge intact). */
const E_SOFTEST = Math.min(...Object.values(MARKET_EFFICIENCY));
/**
* THE FIXED BAR. Identical for every market, and it NEVER moves — efficiency shades the edge,
* not this. (The spec's per-sport MLB-5%/NBA-7% variant is deliberately NOT used: a bar that
* differs by sport is a moving bar, which this design forbids.)
*/
const FIXED_BANDS = Object.freeze([
{ grade: 'A+', min: 10 },
{ grade: 'A', min: 5 },
{ grade: 'B', min: 3 },
{ grade: 'C', min: 1 },
{ grade: 'D', min: 0 },
{ grade: 'F', min: -Infinity },
]);
/** Efficiency score for a market, or null when we have none. */
function efficiencyFor(sport, stat) {
const sp = String(sport || '').toLowerCase();
const st = String(stat || '').toLowerCase();
const exact = MARKET_EFFICIENCY[`${sp}:${st}`];
if (exact != null) return exact;
const wide = MARKET_EFFICIENCY[`${sp}:*`];
return wide != null ? wide : null;
}
/**
* f(efficiency) — BOUNDED to (0, 1]. Soft markets ≤ intact, sharp shaded toward zero.
* Unscored markets → 1 (no adjustment).
*/
function shadingFactor(efficiency) {
if (efficiency == null) return 1;
const e = Number(efficiency);
if (!Number.isFinite(e) || e <= 0) return 1;
const f = E_SOFTEST / e;
// Hard bound: never amplify above the raw edge, even if a future score sits below E_SOFTEST.
return Math.min(1, f);
}
/** Band an adjusted edge against the FIXED bar. */
function bandFor(adjustedEdge) {
if (adjustedEdge == null || !Number.isFinite(Number(adjustedEdge))) return null;
const v = Number(adjustedEdge);
for (const b of FIXED_BANDS) if (v >= b.min) return b.grade;
return 'F';
}
/**
* gradeRow({ sport, stat, edge }) → the challenger verdict for one prop.
* `edge` is the SIGNED raw edge (positive = model agrees with the graded side).
* Returns null when there is no edge to judge — absent, never zero.
*/
function gradeRow(row) {
if (!row) return null;
const raw = row.edge == null || row.edge === '' ? null : Number(row.edge);
if (raw == null || !Number.isFinite(raw)) return null; // absent beats fabricated
const efficiency = efficiencyFor(row.sport, row.stat);
const f = shadingFactor(efficiency);
const adjusted = raw * f;
return {
raw_edge: raw,
efficiency,
unscored: efficiency == null, // FLAGGED, not silently scaled
f,
adjusted_edge: Math.round(adjusted * 1000) / 1000,
grade: bandFor(adjusted),
// the same row graded with NO shading — the isolation baseline, so the delta
// attributable to shading alone is measurable rather than conflated with the
// switch to edge-vs-threshold grading.
baseline_grade: bandFor(raw),
};
}
module.exports = {
MARKET_EFFICIENCY, E_SOFTEST, FIXED_BANDS,
efficiencyFor, shadingFactor, bandFor, gradeRow,
};