'use strict'; /** * Value engine config (Model Train, steps 3-4). * * Product doctrine (Kev): VYNDR PROMOTES bets people actually take — roughly the * -160 to +200 band — that ALSO carry a genuine vig-free edge. Not * plus-money-only, not heavy chalk. * * The TAKEABLE gate applies to PROMOTED surfaces only (daily hero, featured / * top-of-board, future alerts). The full board still shows every graded read. * Parlay Lab is EXEMPT (juiced legs combine into takeable payouts). * JUICE_ODDS_FLOOR (-400, in rareEventMarkets) stays the absolute refusal * backstop UNDERNEATH this — those reads are never graded at all. * * All thresholds are env-tunable. */ // The promotable price band. Ceiling = most-juiced favorite we'll promote; // max = longest dog we'll promote. const TAKEABLE_ODDS_CEILING = Number(process.env.TAKEABLE_ODDS_CEILING || -160); const TAKEABLE_ODDS_MAX = Number(process.env.TAKEABLE_ODDS_MAX || 200); // A read is VALUE only when its EV clears this (a real edge, not rounding). const VALUE_EV_THRESHOLD = Number(process.env.VALUE_EV_THRESHOLD || 2); /** Is an American price inside the takeable band (default -160..+200)? * Strict on input — null/''/undefined are NOT takeable (Number(null) === 0 * would otherwise land a missing price inside the band). */ function isTakeable(american) { if (american == null || american === '') return false; const a = Number(american); if (!Number.isFinite(a)) return false; return a >= TAKEABLE_ODDS_CEILING && a <= TAKEABLE_ODDS_MAX; } /** A read carries VALUE when it's takeable AND its EV clears the threshold — * "the price pays you to take it", distinct from grade ("read quality"). */ function isValue(american, evPct) { return isTakeable(american) && Number.isFinite(evPct) && evPct >= VALUE_EV_THRESHOLD; } module.exports = { TAKEABLE_ODDS_CEILING, TAKEABLE_ODDS_MAX, VALUE_EV_THRESHOLD, isTakeable, isValue, };