77 lines
2.2 KiB
JavaScript
77 lines
2.2 KiB
JavaScript
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 };
|