This commit is contained in:
+9
-9
@@ -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",
|
||||
|
||||
+9
-9
@@ -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",
|
||||
|
||||
@@ -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({
|
||||
<Badge colorPalette={confidenceBandPalette} variant="subtle">
|
||||
{getConfidenceBandLabel(pick.confidence_interval?.band)}
|
||||
</Badge>
|
||||
<Badge colorPalette={pick.ev_edge > 0 ? "green" : "red"} variant="subtle">
|
||||
EV {pick.ev_edge > 0 ? "+" : ""}
|
||||
{formatPercent(pick.ev_edge * 100, 1)}
|
||||
<Badge colorPalette={getEdgePalette(pick.ev_edge)} variant="subtle">
|
||||
Teorik avantaj {formatEdgeSignal(pick.ev_edge)}
|
||||
</Badge>
|
||||
</HStack>
|
||||
</VStack>
|
||||
@@ -498,7 +515,7 @@ function PickCard({
|
||||
label={labels.recommendedStake}
|
||||
value={formatUnits(pick.stake_units || stakeFallback)}
|
||||
/>
|
||||
<MetricTile label={labels.playScore} value={formatPercent(pick.play_score, 0)} />
|
||||
<MetricTile label={labels.playScore} value={formatSignalScore(pick.play_score)} />
|
||||
<MetricTile label="Guven Araligi" value={formatInterval(pick.confidence_interval)} />
|
||||
<MetricTile
|
||||
label="Band"
|
||||
@@ -514,12 +531,12 @@ function PickCard({
|
||||
{labels.playability}
|
||||
</Text>
|
||||
<Text fontSize="sm" color="fg.muted">
|
||||
{formatPercent(pick.play_score, 1)}
|
||||
{formatSignalScore(pick.play_score)}
|
||||
</Text>
|
||||
</HStack>
|
||||
<Bar
|
||||
value={pick.play_score}
|
||||
color={pick.ev_edge > 0 ? "green.400" : "orange.400"}
|
||||
color={pick.ev_edge > 0 ? "blue.400" : "orange.400"}
|
||||
trackBg={trackBg}
|
||||
/>
|
||||
</Box>
|
||||
@@ -596,9 +613,8 @@ function SummaryTable({
|
||||
</HStack>
|
||||
<HStack gap={5} fontSize="sm">
|
||||
<Text minW="48px">{formatOdds(item.odds)}</Text>
|
||||
<Text minW="68px" color={item.ev_edge > 0 ? "green.500" : "red.500"} fontWeight="semibold">
|
||||
{item.ev_edge > 0 ? "+" : ""}
|
||||
{formatPercent(item.ev_edge * 100, 1)}
|
||||
<Text minW="96px" color={`${getEdgePalette(item.ev_edge)}.500`} fontWeight="semibold">
|
||||
{formatEdgeSignal(item.ev_edge)}
|
||||
</Text>
|
||||
<Text minW="48px">{formatPercent(item.calibrated_confidence, 0)}</Text>
|
||||
<Badge colorPalette={getConfidenceBandPalette(item.confidence_interval?.band)} variant="subtle">
|
||||
@@ -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.",
|
||||
)}
|
||||
/>
|
||||
<Box
|
||||
p={3}
|
||||
bg={useColorModeValue("orange.50", "orange.950")}
|
||||
borderWidth="1px"
|
||||
borderColor={useColorModeValue("orange.200", "orange.800")}
|
||||
borderRadius="xl"
|
||||
>
|
||||
<HStack align="start" gap={2}>
|
||||
<Icon as={LuShieldAlert} boxSize={4.5} color="orange.500" mt={0.5} />
|
||||
<Text fontSize="sm" color="fg.muted" lineHeight="tall">
|
||||
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.
|
||||
</Text>
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
{recommendedPick ? (
|
||||
<Grid templateColumns={{ base: "1fr", xl: "1.4fr 1fr" }} gap={4}>
|
||||
@@ -877,7 +907,7 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
|
||||
<HStack justify="space-between" align="start" mb={4}>
|
||||
<VStack align="start" gap={2}>
|
||||
<Badge colorPalette="green" variant="solid" borderRadius="full">
|
||||
{uiText("main-recommendation", "Ana Oneri")}
|
||||
{uiText("main-recommendation", "Öne Çıkan Sinyal")}
|
||||
</Badge>
|
||||
<Text fontSize="2xl" fontWeight="bold">
|
||||
{recommendedPick.pick}
|
||||
@@ -903,13 +933,13 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
|
||||
<MetricTile label={uiText("odds-label", "Oran")} value={formatOdds(recommendedPick.odds)} />
|
||||
<MetricTile label="Guven Araligi" value={formatInterval(recommendedPick.confidence_interval)} />
|
||||
<MetricTile
|
||||
label={uiText("edge-label", "Beklenen Avantaj (Edge)")}
|
||||
value={`${recommendedPick.ev_edge > 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`}
|
||||
/>
|
||||
<MetricTile
|
||||
label={uiText("stake-label", "Onerilen Miktar (Stake)")}
|
||||
@@ -1028,7 +1058,7 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
|
||||
{recommendedPick ? (
|
||||
<PickCard
|
||||
pick={recommendedPick}
|
||||
title={uiText("best-single-pick", "En iyi tekli secim")}
|
||||
title={uiText("best-single-pick", "En güçlü sinyal")}
|
||||
resolveReason={resolveReason}
|
||||
palette="green"
|
||||
stakeFallback={prediction.bet_advice.suggested_stake_units}
|
||||
@@ -1037,8 +1067,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"),
|
||||
}}
|
||||
/>
|
||||
) : 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"),
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user