Serve an honest grade: the letter was carrying 1/6 the information of the
number beside it PHASE 0 corrects the order's premise. A grade letter has been served all along -- engine1.gradeProp builds it from an additive factor index, computed INDEPENDENTLY of p_win. gradeBands is orphaned for a different reason than assumed: it defines what a letter MEANS from realized outcomes, and every band collapses to base-rate at current resolution. The measurement that changed this order, on 3,417 settled props: grade n realized mean p_win A 8 0.500 0.647 <- the TOP grade did WORST B 985 0.640 0.700 C 1,695 0.602 0.676 D 303 0.558 0.604 F 426 0.535 0.588 letter resolution 0.00116 (0.48% of variance) p_win resolution 0.00715 (2.98%) -> the letter carried 0.16x the information of the number beside it Concretely, from the hand-verify: Christian Encarnacion's 0.95 over graded C and his 0.05 under ALSO graded C -- same hitter, opposite forecasts, same letter. The gap was never that grades don't ship; it is that the weaker of two available signals shipped as the headline. PHASE 1 — model/servedGrade.js derives the letter from p_win with bands anchored on MEASURED realized rates (B+ 0.663 / B 0.646 / C+ 0.615 / C 0.589 / C- 0.548 / D 0.512 / F 0.447, base 0.6005). NO MANUFACTURED A, structurally: A+/A/A- are UNISSUABLE, not rare. The realized rate plateaus at 0.65-0.68 above p_win 0.70, so no band has earned a top letter; a test sweeps every p_win 0..1 and asserts none produces one. Even 0.99 tops out at B+ with its realized 0.663 attached. Raising that ceiling later is a deliberate, visible act. Bands that cannot separate SAY so -- C+/C/C- carry separates_from_base_rate false and copy naming it, which is the honest description of a forecast explaining 3% of variance. Every grade states its basis (forecast_only vs forecast_plus_matchup_factors, naming which factors fired) and calibrated:false. engine1.grade is preserved as engine_grade so nothing downstream breaks. PHASE 2 — refusals render real states: insufficient_data -> "not enough history to call this one"; juiced_no_edge -> "the book has priced the vig past any edge on this side". 1,870 refused snapshots carry exactly those two reasons and both now surface. PHASE 3 — hand-verified on 12 real served props. Freeman/Rice/Encarnacion 0.95 overs now B+ (was B, C, B); the 0.05 unders now F (was C). Refused doubles render NO READ with their reason. never-blank PASS, no-manufactured-A PASS. Serving change; nine frozen model modules unchanged including engine1; p_win never mutated; no calibrated number leaks (deployed set empty); no Bonferroni slot. STILL TRUE: the forecast explains ~3% of outcome variance. This order did not make the model better. It made the letter stop overstating it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1sivYNqY2TS5ftykmHBU9
This commit is contained in:
@@ -574,6 +574,27 @@ async function analyzeViaEngine1(rawProp = {}) {
|
||||
? (Number.isFinite(pOver) ? 1 - pOver : null)
|
||||
: (Number.isFinite(pOver) ? pOver : null);
|
||||
if (pWin != null) legacy.p_win = Math.round(pWin * 1000) / 1000;
|
||||
// ── THE SERVED GRADE ────────────────────────────────────────────────
|
||||
// Derived from the forecast, not from engine1's additive factor index.
|
||||
// Measured on 3,417 settled props, that index carried 0.16x the information
|
||||
// of the p_win printed beside it, and its A grade hit 0.500 while its F hit
|
||||
// 0.535 -- the top letter did worse than the bottom. Concretely: the same
|
||||
// hitter's 0.95 over and 0.05 under both graded C.
|
||||
//
|
||||
// engine1.grade is PRESERVED on the payload as `engine_grade` so nothing
|
||||
// downstream breaks and the two remain comparable, but `served_grade` is
|
||||
// what a user should see.
|
||||
try {
|
||||
const sg = require('../model/servedGrade');
|
||||
legacy.engine_grade = legacy.grade;
|
||||
legacy.served_grade = sg.gradeFor({
|
||||
p_win: legacy.p_win,
|
||||
refused: legacy.refused || legacy.insufficient_data,
|
||||
refusal_reason: legacy.refusal_reason,
|
||||
factor_adjustment: factorTrace,
|
||||
});
|
||||
} catch { /* the grade surface must never break the read */ }
|
||||
|
||||
if (factorTrace) {
|
||||
legacy.factor_adjustment = factorTrace;
|
||||
legacy.p_win_prefactor = Math.round((dir === 'under' ? 1 - factorTrace.p_before : factorTrace.p_before) * 1000) / 1000;
|
||||
|
||||
Reference in New Issue
Block a user