Sessions 5-7a: 955 tests, deployment ready
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
const HITTING_STATS = [
|
||||
'hits', 'total_bases', 'home_runs', 'rbis', 'runs_scored',
|
||||
'strikeouts_batter', 'walks', 'stolen_bases',
|
||||
];
|
||||
|
||||
const PITCHING_STATS = [
|
||||
'strikeouts', 'earned_runs', 'outs_recorded', 'walks_allowed',
|
||||
'hits_allowed', 'pitches_thrown',
|
||||
];
|
||||
|
||||
const ALL_MLB_STATS = [...HITTING_STATS, ...PITCHING_STATS];
|
||||
|
||||
function isMlbStatType(statType) {
|
||||
return ALL_MLB_STATS.includes(statType);
|
||||
}
|
||||
|
||||
function calculateMlbEdge(playerAvg, line, direction) {
|
||||
if (playerAvg == null || line == null) return 0;
|
||||
if (direction === 'over') {
|
||||
return ((playerAvg - line) / line) * 100;
|
||||
}
|
||||
// under
|
||||
return ((line - playerAvg) / line) * 100;
|
||||
}
|
||||
|
||||
function gradeMlbProp({ player, stat_type, line, direction, seasonAvg, recentAvg, killConditions = [] }) {
|
||||
if (!isMlbStatType(stat_type)) {
|
||||
return { grade: 'D', confidence: 30, edge_pct: 0, composite: 0 };
|
||||
}
|
||||
|
||||
const seasonEdge = calculateMlbEdge(seasonAvg, line, direction);
|
||||
const recentEdge = calculateMlbEdge(recentAvg, line, direction);
|
||||
|
||||
// Weighted composite: 60% season, 40% recent
|
||||
const edge_pct = Math.round((seasonEdge * 0.6 + recentEdge * 0.4) * 100) / 100;
|
||||
|
||||
// Grade thresholds based on edge
|
||||
let grade;
|
||||
if (edge_pct >= 5) {
|
||||
grade = 'A';
|
||||
} else if (edge_pct >= 3) {
|
||||
grade = 'B';
|
||||
} else if (edge_pct >= 1) {
|
||||
grade = 'C';
|
||||
} else {
|
||||
grade = 'D';
|
||||
}
|
||||
|
||||
// Confidence based on edge magnitude
|
||||
let confidence;
|
||||
if (grade === 'A') {
|
||||
confidence = Math.min(95, 80 + Math.floor(edge_pct));
|
||||
} else if (grade === 'B') {
|
||||
confidence = Math.min(79, 65 + Math.floor(edge_pct));
|
||||
} else if (grade === 'C') {
|
||||
confidence = Math.min(64, 50 + Math.floor(edge_pct * 2));
|
||||
} else {
|
||||
confidence = Math.max(30, 45 + Math.floor(edge_pct));
|
||||
}
|
||||
|
||||
// Kill condition penalty: cap at C and reduce confidence by 15 per condition
|
||||
if (killConditions.length > 0) {
|
||||
if (grade === 'A' || grade === 'B') {
|
||||
grade = 'C';
|
||||
}
|
||||
confidence -= killConditions.length * 15;
|
||||
}
|
||||
|
||||
confidence = Math.max(30, Math.min(95, confidence));
|
||||
|
||||
const composite = Math.round(edge_pct * 100) / 100;
|
||||
|
||||
return { grade, confidence, edge_pct, composite };
|
||||
}
|
||||
|
||||
module.exports = { gradeMlbProp, calculateMlbEdge, isMlbStatType, HITTING_STATS, PITCHING_STATS, ALL_MLB_STATS };
|
||||
Reference in New Issue
Block a user