function gradeParlayFromLegs(legResults, correlationFlags) { if (legResults.length === 0) { return { grade: 'D', confidence: 30, composite: 0 }; } // Extract leg composites and grades const legComposites = legResults.map((l) => l._composite || 0); const legGrades = legResults.map((l) => l.grade); const legConfidences = legResults.map((l) => l.confidence); const dCount = legGrades.filter((g) => g === 'D').length; // Average of leg composites const legAvg = legComposites.reduce((a, b) => a + b, 0) / legComposites.length; // Correlation penalties let correlationPenalty = 0; let hasMajorNegative = false; for (const flag of correlationFlags) { if (flag.impact === 'minor_negative') correlationPenalty -= 0.3; if (flag.impact === 'major_negative') { correlationPenalty -= 1.0; hasMajorNegative = true; } } const parlayComposite = legAvg + correlationPenalty; // Grade assignment let grade; if (parlayComposite >= 2.5 && dCount === 0 && !hasMajorNegative) { grade = 'A'; } else if (parlayComposite >= 1.5 && dCount <= 1) { grade = 'B'; } else if (parlayComposite >= 0.5) { grade = 'C'; } else { grade = 'D'; } // 2+ D legs forces grade D if (dCount >= 2) grade = 'D'; // Major negative caps at B if (hasMajorNegative && (grade === 'A')) { grade = 'B'; } // Confidence: average of legs, adjusted for correlations let confidence = legConfidences.reduce((a, b) => a + b, 0) / legConfidences.length; for (const flag of correlationFlags) { if (flag.impact === 'minor_negative') confidence -= 5; if (flag.impact === 'major_negative') confidence -= 15; } confidence = Math.max(30, Math.min(95, Math.round(confidence))); return { grade, confidence, composite: Math.round(parlayComposite * 100) / 100, }; } module.exports = { gradeParlayFromLegs };