diff --git a/src/components/matches/prediction-card.tsx b/src/components/matches/prediction-card.tsx index 23054ea..6e48358 100644 --- a/src/components/matches/prediction-card.tsx +++ b/src/components/matches/prediction-card.tsx @@ -1172,6 +1172,39 @@ export default function PredictionCard({ prediction }: PredictionCardProps) { const mainBandPalette = getConfidenceBandPalette( prediction.bet_advice.confidence_band, ); + + // ── Maç Sonucu Tahmini (model'in EN OLASI sonucu) ────────────────── + // The value-bet hero below optimizes ROI (often a high-odds draw/underdog, + // ~27% hit) which users misread as a "bad prediction". This block instead + // shows what the model thinks will actually happen: the highest-probability + // 1X2 outcome (~55% hit, on par with the market). Sourced from market_board.MS. + const msBoard = prediction.market_board?.MS; + const matchResultPrediction = (() => { + const probs = msBoard?.probs; + if (!probs) return null; + const labelMap: Record = { + "1": prediction.match_info?.home_team || uiText("home", "Ev Sahibi"), + X: uiText("draw", "Beraberlik"), + "2": prediction.match_info?.away_team || uiText("away", "Deplasman"), + }; + let bestKey = ""; + let bestProb = -1; + for (const [k, v] of Object.entries(probs)) { + const p = typeof v === "number" ? v : 0; + if (p > bestProb) { + bestProb = p; + bestKey = k; + } + } + if (!bestKey) return null; + return { + pick: bestKey, + label: labelMap[bestKey] ?? bestKey, + prob: bestProb, + all: probs as Record, + }; + })(); + const sport = getPredictionSport(prediction); const isBasketball = sport === "basketball"; @@ -1373,6 +1406,85 @@ export default function PredictionCard({ prediction }: PredictionCardProps) { + {matchResultPrediction ? ( + + + + + {uiText("match-result-prediction", "Maç Sonucu Tahmini")} + + + {matchResultPrediction.label} + + + {uiText( + "match-result-copy", + "Modelin en olası gördüğü sonuç (kim kazanır).", + )} + + + + + {formatPercent(matchResultPrediction.prob * 100, 0)} + + + {uiText("probability-short", "olasılık")} + + + + {/* 1X2 dağılımı */} + + {["1", "X", "2"].map((k) => { + const p = matchResultPrediction.all[k] ?? 0; + const lbl: Record = { + "1": + prediction.match_info?.home_team || + uiText("home", "Ev Sahibi"), + X: uiText("draw", "Beraberlik"), + "2": + prediction.match_info?.away_team || + uiText("away", "Deplasman"), + }; + const isTop = k === matchResultPrediction.pick; + return ( + + + + {lbl[k]} + + + {formatPercent(p * 100, 0)} + + + + + ); + })} + + + {uiText( + "match-result-vs-value", + "Bu en olası sonuçtur. Aşağıdaki “Değerli Bahis” ise orana göre en kârlı görülen seçimdir — ikisi farklı olabilir.", + )} + + + ) : null} + {recommendedPick ? ( - {uiText("main-recommendation", "Öne Çıkan Sinyal")} + {uiText("main-recommendation", "Değerli Bahis")} {recommendedPick.pick}