diff --git a/messages/en.json b/messages/en.json
index c6efb24..798130a 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -201,11 +201,11 @@
"missing_total_odds": "Over/Under odds are missing.",
"missing_spread_odds": "Spread (Handicap) odds are missing.",
"no_bet_conditions_met": "The algorithm could not find a safe/valuable bet for this match.",
- "insufficient_play_score": "Play score is below the playability threshold.",
+ "insufficient_play_score": "Model signal is below the threshold.",
"no_ev_edge_minimum_stake": "Passed safety gates but no mathematical edge — minimum stake applied.",
"upset_risk_detected": "High upset risk detected, proceed with caution."
},
- "ev-edge": "EV Edge",
+ "ev-edge": "Theoretical Edge",
"implied-prob": "Market Probability",
"model-prob": "Model Probability",
"kelly-stake": "Kelly Stake",
@@ -238,23 +238,23 @@
},
"ui": {
"summary-title": "Prediction Summary",
- "summary-info": "Shows what stands out first and then explains why it stands out.",
- "main-recommendation": "Main Recommendation",
+ "summary-info": "Shows model signals and uncertainty in a conservative summary.",
+ "main-recommendation": "Highlighted Signal",
"best-market-copy": "is the strongest option in this market.",
"confidence-label": "Confidence",
"odds-label": "Odds",
- "edge-label": "Expected Advantage (Edge)",
- "edge-info": "Edge is the gap between the model probability and the market probability. If it is positive, the model sees value in this price.",
+ "edge-label": "Theoretical Edge",
+ "edge-info": "The theoretical gap between model probability and market probability; it is not a guarantee or a certain profit expectation.",
"stake-label": "Suggested Bet Size (Stake)",
"stake-label-short": "Bet Size",
"stake-info": "Stake is the suggested bet size. 2.0u means a 2-unit bet in your own bankroll plan.",
- "play-score-label": "Playability Score",
- "playability-label": "Playability",
+ "play-score-label": "Model Signal",
+ "playability-label": "Model signal",
"quick-read": "Quick read",
"lineup-source": "Lineup Source",
"model-label": "Model",
"engine-info": "Shows which components influence the prediction the most.",
- "best-single-pick": "Best Single Pick",
+ "best-single-pick": "Strongest Signal",
"alternative-markets": "Alternative Markets",
"alternative-markets-info": "Options outside the main recommendation.",
"alternative": "Alternative",
diff --git a/messages/tr.json b/messages/tr.json
index 3af689d..b8ace5b 100644
--- a/messages/tr.json
+++ b/messages/tr.json
@@ -202,10 +202,10 @@
"missing_total_odds": "Alt/Üst oranları eksik.",
"missing_spread_odds": "Handikap oranları eksik.",
"no_bet_conditions_met": "Algoritma bu maç için güvenli/değerli bir bahis önerisi bulamadı.",
- "insufficient_play_score": "Oynanabilirlik skoru eşiğin altında kaldı.",
+ "insufficient_play_score": "Model sinyali eşiğin altında kaldı.",
"no_ev_edge_minimum_stake": "Güvenlik kontrollerini geçti ancak matematik avantaj yok — minimum bahis uygulandı."
},
- "ev-edge": "EV Edge",
+ "ev-edge": "Teorik Avantaj",
"implied-prob": "Piyasa Olasılığı",
"model-prob": "Model Olasılığı",
"kelly-stake": "Kelly Bahis",
@@ -238,23 +238,23 @@
},
"ui": {
"summary-title": "Tahmin Özeti",
- "summary-info": "Önce neyin oynanabileceğini, sonra bunun neden öne çıktığını gösterir.",
- "main-recommendation": "Ana Öneri",
+ "summary-info": "Model sinyallerini ve belirsizlikleri sade şekilde gösterir.",
+ "main-recommendation": "Öne Çıkan Sinyal",
"best-market-copy": "marketinde en güçlü seçim.",
"confidence-label": "Güven",
"odds-label": "Oran",
- "edge-label": "Beklenen Avantaj (Edge)",
- "edge-info": "Edge, model olasılığı ile piyasa olasılığı arasındaki farktır. Pozitifse model bu oranı avantajlı görüyor demektir.",
+ "edge-label": "Teorik Avantaj",
+ "edge-info": "Model olasılığı ile piyasa olasılığı arasındaki teorik farktır; tutma garantisi veya kesin kazanç beklentisi değildir.",
"stake-label": "Önerilen Miktar (Stake)",
"stake-label-short": "Bahis Miktarı",
"stake-info": "Stake, bu bahis için önerilen bahis birimidir. 2.0u, kendi bankroll planınızdaki 2 birimlik bahis anlamına gelir.",
- "play-score-label": "Oynanabilirlik Puanı",
- "playability-label": "Oynanabilirlik",
+ "play-score-label": "Model Sinyali",
+ "playability-label": "Model sinyali",
"quick-read": "Hızlı yorum",
"lineup-source": "Kadronun Kaynağı",
"model-label": "Model",
"engine-info": "Tahmini en çok hangi bileşenlerin etkilediğini gösterir.",
- "best-single-pick": "En İyi Tekli Seçim",
+ "best-single-pick": "En Güçlü Sinyal",
"alternative-markets": "Alternatif Marketler",
"alternative-markets-info": "Ana tahmin dışındaki seçenekler.",
"alternative": "Alternatif",
diff --git a/src/components/matches/prediction-card.tsx b/src/components/matches/prediction-card.tsx
index 6da3f39..7ac87ad 100644
--- a/src/components/matches/prediction-card.tsx
+++ b/src/components/matches/prediction-card.tsx
@@ -49,7 +49,7 @@ interface PredictionCardProps {
function formatReasonFallback(reason: string): string {
if (reason.startsWith("risk:")) return formatReasonFallback(reason.slice(5));
const evMatch = reason.match(/^ev_edge_([+\-][\d.]+%)_grade_(\w)$/);
- if (evMatch) return `Beklenen avantaj ${evMatch[1]} (Not ${evMatch[2]})`;
+ if (evMatch) return `Teorik avantaj sinyali: Not ${evMatch[2]}`;
const negMatch = reason.match(/^negative_model_edge_([+\-][\d.]+)$/);
if (negMatch) return `Model avantajı negatif (${negMatch[1]})`;
const thresholdMatch = reason.match(/^below_market_edge_threshold_([+\-]?[\d.]+)$/);
@@ -90,6 +90,24 @@ function formatProbability(value?: number, digits = 1): string {
return `${(value * 100).toFixed(digits)}%`;
}
+function formatSignalScore(value?: number): string {
+ if (value === undefined || value === null || Number.isNaN(value)) return "-";
+ return `${Math.max(0, Math.min(100, value)).toFixed(0)}/100`;
+}
+
+function formatEdgeSignal(value?: number): string {
+ if (value === undefined || value === null || Number.isNaN(value)) return "-";
+ return `${value > 0 ? "+" : ""}${formatPercent(value * 100, 1)}`;
+}
+
+function getEdgePalette(value?: number): string {
+ if (value === undefined || value === null || Number.isNaN(value)) return "gray";
+ if (value <= 0) return "red";
+ if (value < 0.08) return "yellow";
+ if (value < 0.15) return "orange";
+ return "purple";
+}
+
function formatOdds(value?: number | null): string {
if (!value || value <= 1.01) return "-";
return value.toFixed(2);
@@ -485,9 +503,8 @@ function PickCard({
{getConfidenceBandLabel(pick.confidence_interval?.band)}
- 0 ? "green" : "red"} variant="subtle">
- EV {pick.ev_edge > 0 ? "+" : ""}
- {formatPercent(pick.ev_edge * 100, 1)}
+
+ Teorik avantaj {formatEdgeSignal(pick.ev_edge)}
@@ -498,7 +515,7 @@ function PickCard({
label={labels.recommendedStake}
value={formatUnits(pick.stake_units || stakeFallback)}
/>
-
+
- {formatPercent(pick.play_score, 1)}
+ {formatSignalScore(pick.play_score)}
0 ? "green.400" : "orange.400"}
+ color={pick.ev_edge > 0 ? "blue.400" : "orange.400"}
trackBg={trackBg}
/>
@@ -596,9 +613,8 @@ function SummaryTable({
{formatOdds(item.odds)}
- 0 ? "green.500" : "red.500"} fontWeight="semibold">
- {item.ev_edge > 0 ? "+" : ""}
- {formatPercent(item.ev_edge * 100, 1)}
+
+ {formatEdgeSignal(item.ev_edge)}
{formatPercent(item.calibrated_confidence, 0)}
@@ -867,9 +883,23 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
title={uiText("summary-title", "Tahmin Ozeti")}
info={uiText(
"summary-info",
- "Kullanicinin once neyi oynayacagini, sonra nedenini anlamasi icin sade ozet.",
+ "Model sinyallerini ve belirsizlikleri sade sekilde gosterir.",
)}
/>
+
+
+
+
+ Bu bir model sinyalidir; kesin sonuç, garanti veya tutma yüzdesi değildir. Sinyal puanı maç içi varyans, kadro ve veri kalitesi nedeniyle yanılabilir.
+
+
+
{recommendedPick ? (
@@ -877,7 +907,7 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
- {uiText("main-recommendation", "Ana Oneri")}
+ {uiText("main-recommendation", "Öne Çıkan Sinyal")}
{recommendedPick.pick}
@@ -903,13 +933,13 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
0 ? "+" : ""}${formatPercent(recommendedPick.ev_edge * 100, 1)}`}
+ label={uiText("edge-label", "Teorik Avantaj")}
+ value={formatEdgeSignal(recommendedPick.ev_edge)}
helper={uiText(
"edge-info",
- "Edge, model olasiligi ile piyasa olasiligi arasindaki farktir. Pozitifse model bu orani avantajli buluyor demektir.",
+ "Model olasiligi ile piyasa olasiligi arasindaki teorik farktir; tutma garantisi veya kesin kazanc beklentisi degildir.",
)}
- accent={recommendedPick.ev_edge > 0 ? "green.500" : "red.500"}
+ accent={`${getEdgePalette(recommendedPick.ev_edge)}.500`}
/>
) : null}
@@ -1064,8 +1094,8 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
confidence: uiText("confidence-label", "Guven"),
odds: uiText("odds-label", "Oran"),
recommendedStake: uiText("stake-label-short", "Stake"),
- playScore: uiText("play-score-label", "Play Score"),
- playability: uiText("playability-label", "Oynanabilirlik"),
+ playScore: uiText("play-score-label", "Model Sinyali"),
+ playability: uiText("playability-label", "Model sinyali"),
}}
/>
))}
diff --git a/src/lib/api/predictions/types.ts b/src/lib/api/predictions/types.ts
index 5aff377..6d6e975 100644
--- a/src/lib/api/predictions/types.ts
+++ b/src/lib/api/predictions/types.ts
@@ -69,6 +69,13 @@ export interface MatchPickDto {
edge: number;
ev_edge: number;
implied_prob: number;
+ model_probability?: number;
+ model_edge?: number;
+ calibrated_probability?: number;
+ odds_band_probability?: number;
+ odds_band_sample?: number;
+ odds_band_edge?: number;
+ odds_band_aligned?: boolean;
play_score: number;
playable: boolean;
bet_grade: BetGrade;
@@ -76,6 +83,7 @@ export interface MatchPickDto {
decision_reasons: string[];
confidence_interval?: ConfidenceIntervalDto;
signal_tier?: SignalTier;
+ is_guaranteed?: boolean;
}
export interface MatchBetAdviceDto {
@@ -99,6 +107,13 @@ export interface MatchBetSummaryItemDto {
play_score: number;
ev_edge: number;
implied_prob: number;
+ model_probability?: number;
+ model_edge?: number;
+ calibrated_probability?: number;
+ odds_band_probability?: number;
+ odds_band_sample?: number;
+ odds_band_edge?: number;
+ odds_band_aligned?: boolean;
odds: number;
reasons: string[];
confidence_interval?: ConfidenceIntervalDto;