357 lines
13 KiB
Python
357 lines
13 KiB
Python
"""
|
||
Smart Bet Recommender
|
||
=====================
|
||
|
||
Skor tahminine göre akıllı bahis önerileri yapan sistem.
|
||
|
||
Örnek: Beşiktaş-Galatasaray için model 3-1 tahmin ediyor
|
||
→ DÜŞÜK RİSK: 1.5 Üst (yüksek ihtimal tutar)
|
||
→ ORTA RİSK: MS 1 + 2.5 Üst (orta ihtimal)
|
||
→ YÜKSEK RİSK: 3.5 Üst veya skor 3-1 (düşük ihtimal, yüksek kazanç)
|
||
|
||
Ayrıca kombinasyonlar:
|
||
- MS 1 + 1.5 Üst
|
||
- MS 1 + KG Var
|
||
- Her iki takım skor > 0.5 (her takım en az 1 gol atar)
|
||
"""
|
||
|
||
from dataclasses import dataclass
|
||
from typing import Dict, List, Optional, Tuple
|
||
from enum import Enum
|
||
|
||
|
||
class RiskLevel(Enum):
|
||
LOW = "LOW" # Yüksek olasılık, düşük oran (güvenli)
|
||
MEDIUM = "MEDIUM" # Orta olasılık, orta oran
|
||
HIGH = "HIGH" # Düşük olasılık, yüksek kazanç
|
||
EXTREME = "EXTREME" # Çok düşük olasılık, çok yüksek kazanç
|
||
|
||
|
||
@dataclass
|
||
class BetRecommendation:
|
||
"""Tek bir bahis önerisi"""
|
||
market: str # Piyasa adı (örn: "MS 1", "2.5 Üst")
|
||
pick: str # Seçim (örn: "1", "OVER", "YES")
|
||
odds: float # Oran
|
||
probability: float # Model olasılığı (0-1)
|
||
confidence: float # Güven seviyesi (0-100)
|
||
risk_level: RiskLevel
|
||
|
||
def to_dict(self) -> dict:
|
||
return {
|
||
"market": self.market,
|
||
"pick": self.pick,
|
||
"odds": self.odds,
|
||
"probability": round(self.probability * 100, 1),
|
||
"confidence": round(self.confidence, 1),
|
||
"risk_level": self.risk_level.value
|
||
}
|
||
|
||
|
||
@dataclass
|
||
class MatchPredictionSet:
|
||
"""Bir maç için tüm tahmin seti"""
|
||
match_name: str
|
||
predicted_score: Tuple[int, int] # (home, away)
|
||
home_win_prob: float
|
||
draw_prob: float
|
||
away_win_prob: float
|
||
over_15_prob: float
|
||
over_25_prob: float
|
||
over_35_prob: float
|
||
btts_yes_prob: float
|
||
|
||
# Öneriler
|
||
low_risk_bets: List[BetRecommendation]
|
||
medium_risk_bets: List[BetRecommendation]
|
||
high_risk_bets: List[BetRecommendation]
|
||
extreme_risk_bets: List[BetRecommendation]
|
||
|
||
def to_dict(self) -> dict:
|
||
return {
|
||
"match_name": self.match_name,
|
||
"predicted_score": f"{self.predicted_score[0]}-{self.predicted_score[1]}",
|
||
"probs": {
|
||
"home_win": round(self.home_win_prob * 100, 1),
|
||
"draw": round(self.draw_prob * 100, 1),
|
||
"away_win": round(self.away_win_prob * 100, 1),
|
||
"over_15": round(self.over_15_prob * 100, 1),
|
||
"over_25": round(self.over_25_prob * 100, 1),
|
||
"over_35": round(self.over_35_prob * 100, 1),
|
||
"btts": round(self.btts_yes_prob * 100, 1)
|
||
},
|
||
"low_risk": [b.to_dict() for b in self.low_risk_bets],
|
||
"medium_risk": [b.to_dict() for b in self.medium_risk_bets],
|
||
"high_risk": [b.to_dict() for b in self.high_risk_bets],
|
||
"extreme_risk": [b.to_dict() for b in self.extreme_risk_bets]
|
||
}
|
||
|
||
|
||
class SmartBetRecommender:
|
||
"""
|
||
Akıllı Bahis Öneri Sistemi
|
||
|
||
Skor tahminine göre farklı risk seviyelerinde bahisler önerir.
|
||
|
||
Mantık:
|
||
1. DÜŞÜK RİSK: Yüksek olasılıklı (>70%), düşük oranlı bahisler
|
||
- 1.5 Üst
|
||
- Double Chance
|
||
- Favori takım gol atar
|
||
|
||
2. ORTA RİSK: Orta olasılıklı (50-70%), orta oranlı bahisler
|
||
- MS favori
|
||
- 2.5 Üst
|
||
- KG Var/Var
|
||
|
||
3. YÜKSEK RİSK: Düşük olasılıklı (30-50%), yüksek oranlı bahisler
|
||
- 3.5 Üst
|
||
- Skor tahmini
|
||
- Handikap
|
||
|
||
4. EXTREME RİSK: Çok düşük olasılıklı (<30%), çok yüksek oranlı
|
||
- Tam skor
|
||
- Uzunluklu kombinasyonlar
|
||
"""
|
||
|
||
# Olasılık eşikleri
|
||
PROB_LOW_RISK = 0.70 # > %70 olasılık
|
||
PROB_MEDIUM_RISK = 0.50 # %50-70 olasılık
|
||
PROB_HIGH_RISK = 0.30 # %30-50 olasılık
|
||
# < %30 = EXTREME
|
||
|
||
def __init__(self):
|
||
pass
|
||
|
||
def _determine_risk(self, probability: float) -> RiskLevel:
|
||
"""Olasılığa göre risk seviyesi belirle"""
|
||
if probability >= self.PROB_LOW_RISK:
|
||
return RiskLevel.LOW
|
||
elif probability >= self.PROB_MEDIUM_RISK:
|
||
return RiskLevel.MEDIUM
|
||
elif probability >= self.PROB_HIGH_RISK:
|
||
return RiskLevel.HIGH
|
||
else:
|
||
return RiskLevel.EXTREME
|
||
|
||
def _get_favorite(self, home_prob: float, draw_prob: float, away_prob: float) -> Tuple[str, float]:
|
||
"""Favori sonucu ve olasılığını döndür"""
|
||
if home_prob >= draw_prob and home_prob >= away_prob:
|
||
return "1", home_prob
|
||
elif away_prob >= home_prob and away_prob >= draw_prob:
|
||
return "2", away_prob
|
||
else:
|
||
return "X", draw_prob
|
||
|
||
def _calculate_expected_goals(self, predicted_score: Tuple[int, int]) -> float:
|
||
"""Tahmin edilen skora göre beklenen gol sayısı"""
|
||
return predicted_score[0] + predicted_score[1]
|
||
|
||
def recommend(
|
||
self,
|
||
match_name: str,
|
||
predicted_score: Tuple[int, int],
|
||
probs: Dict[str, float],
|
||
odds: Dict[str, float]
|
||
) -> MatchPredictionSet:
|
||
"""
|
||
Maç için tüm bahis önerilerini oluştur.
|
||
|
||
Args:
|
||
match_name: Maç adı
|
||
predicted_score: (home_goals, away_goals)
|
||
probs: {"home_win": 0.55, "draw": 0.25, "away_win": 0.20,
|
||
"over_15": 0.85, "over_25": 0.65, "over_35": 0.35,
|
||
"btts_yes": 0.55}
|
||
odds: {"1": 1.80, "X": 3.50, "2": 4.20,
|
||
"ou15_o": 1.25, "ou15_u": 3.80,
|
||
"ou25_o": 1.90, "ou25_u": 1.85,
|
||
"ou35_o": 3.20, "ou35_u": 1.30,
|
||
"btts_y": 1.75, "btts_n": 2.00}
|
||
|
||
Returns:
|
||
MatchPredictionSet with all recommendations
|
||
"""
|
||
home_prob = probs.get("home_win", 0.33)
|
||
draw_prob = probs.get("draw", 0.33)
|
||
away_prob = probs.get("away_win", 0.33)
|
||
over_15_prob = probs.get("over_15", 0.70)
|
||
over_25_prob = probs.get("over_25", 0.50)
|
||
over_35_prob = probs.get("over_35", 0.30)
|
||
btts_prob = probs.get("btts_yes", 0.50)
|
||
|
||
# Beklenen goller
|
||
expected_goals = self._calculate_expected_goals(predicted_score)
|
||
|
||
# Favori
|
||
favorite, favorite_prob = self._get_favorite(home_prob, draw_prob, away_prob)
|
||
|
||
# Önerileri oluştur
|
||
low_risk = []
|
||
medium_risk = []
|
||
high_risk = []
|
||
extreme_risk = []
|
||
|
||
# ========== DÜŞÜK RİSK ÖNERİLERİ ==========
|
||
# 1.5 Üst (en güvenli)
|
||
if over_15_prob >= self.PROB_LOW_RISK:
|
||
low_risk.append(BetRecommendation(
|
||
market="1.5 Üst/Alt",
|
||
pick="OVER",
|
||
odds=odds.get("ou15_o", 1.25),
|
||
probability=over_15_prob,
|
||
confidence=over_15_prob * 100,
|
||
risk_level=RiskLevel.LOW
|
||
))
|
||
|
||
# Double Chance
|
||
if home_prob > away_prob:
|
||
dc_prob = home_prob + draw_prob
|
||
if dc_prob >= self.PROB_LOW_RISK:
|
||
low_risk.append(BetRecommendation(
|
||
market="Double Chance",
|
||
pick="1X",
|
||
odds=odds.get("dc_1x", 1.30),
|
||
probability=dc_prob,
|
||
confidence=dc_prob * 100,
|
||
risk_level=RiskLevel.LOW
|
||
))
|
||
elif away_prob > home_prob:
|
||
dc_prob = away_prob + draw_prob
|
||
if dc_prob >= self.PROB_LOW_RISK:
|
||
low_risk.append(BetRecommendation(
|
||
market="Double Chance",
|
||
pick="X2",
|
||
odds=odds.get("dc_x2", 1.30),
|
||
probability=dc_prob,
|
||
confidence=dc_prob * 100,
|
||
risk_level=RiskLevel.LOW
|
||
))
|
||
|
||
# ========== ORTA RİSK ÖNERİLERİ ==========
|
||
# MS Favori
|
||
if self.PROB_MEDIUM_RISK <= favorite_prob < self.PROB_LOW_RISK:
|
||
medium_risk.append(BetRecommendation(
|
||
market="Maç Sonucu",
|
||
pick=favorite,
|
||
odds=odds.get(favorite, 2.00),
|
||
probability=favorite_prob,
|
||
confidence=favorite_prob * 100,
|
||
risk_level=RiskLevel.MEDIUM
|
||
))
|
||
|
||
# 2.5 Üst
|
||
if self.PROB_MEDIUM_RISK <= over_25_prob < self.PROB_LOW_RISK:
|
||
medium_risk.append(BetRecommendation(
|
||
market="2.5 Üst/Alt",
|
||
pick="OVER",
|
||
odds=odds.get("ou25_o", 1.90),
|
||
probability=over_25_prob,
|
||
confidence=over_25_prob * 100,
|
||
risk_level=RiskLevel.MEDIUM
|
||
))
|
||
|
||
# KG Var
|
||
if self.PROB_MEDIUM_RISK <= btts_prob < self.PROB_LOW_RISK:
|
||
medium_risk.append(BetRecommendation(
|
||
market="Karşılıklı Gol",
|
||
pick="YES",
|
||
odds=odds.get("btts_y", 1.75),
|
||
probability=btts_prob,
|
||
confidence=btts_prob * 100,
|
||
risk_level=RiskLevel.MEDIUM
|
||
))
|
||
|
||
# MS + 2.5 Üst kombinasyonu
|
||
if favorite_prob >= 0.45 and over_25_prob >= 0.50:
|
||
combo_prob = favorite_prob * over_25_prob # Basit çarpım
|
||
combo_odds = odds.get(favorite, 2.00) * odds.get("ou25_o", 1.90)
|
||
if combo_prob >= 0.30: # En az %30 olasılık
|
||
medium_risk.append(BetRecommendation(
|
||
market=f"MS {favorite} + 2.5 Üst",
|
||
pick=f"{favorite} & OVER",
|
||
odds=combo_odds,
|
||
probability=combo_prob,
|
||
confidence=combo_prob * 100,
|
||
risk_level=RiskLevel.MEDIUM
|
||
))
|
||
|
||
# ========== YÜKSEK RİSK ÖNERİLERİ ==========
|
||
# 3.5 Üst
|
||
if self.PROB_HIGH_RISK <= over_35_prob < self.PROB_MEDIUM_RISK:
|
||
high_risk.append(BetRecommendation(
|
||
market="3.5 Üst/Alt",
|
||
pick="OVER",
|
||
odds=odds.get("ou35_o", 3.20),
|
||
probability=over_35_prob,
|
||
confidence=over_35_prob * 100,
|
||
risk_level=RiskLevel.HIGH
|
||
))
|
||
|
||
# Skor tahmini (yüksek skorlu maçlar için)
|
||
if expected_goals >= 3.5:
|
||
score_str = f"{predicted_score[0]}-{predicted_score[1]}"
|
||
# Skor olasılığı tahmini (basit model)
|
||
score_prob = 0.15 if expected_goals <= 4 else 0.10
|
||
high_risk.append(BetRecommendation(
|
||
market="Tam Skor",
|
||
pick=score_str,
|
||
odds=8.0, # Tahmini oran
|
||
probability=score_prob,
|
||
confidence=score_prob * 100,
|
||
risk_level=RiskLevel.HIGH
|
||
))
|
||
|
||
# MS + 3.5 Üst
|
||
if favorite_prob >= 0.40 and over_35_prob >= 0.30:
|
||
combo_prob = favorite_prob * over_35_prob
|
||
combo_odds = odds.get(favorite, 2.00) * odds.get("ou35_o", 3.20)
|
||
high_risk.append(BetRecommendation(
|
||
market=f"MS {favorite} + 3.5 Üst",
|
||
pick=f"{favorite} & OVER",
|
||
odds=combo_odds,
|
||
probability=combo_prob,
|
||
confidence=combo_prob * 100,
|
||
risk_level=RiskLevel.HIGH
|
||
))
|
||
|
||
# ========== EXTREME RİSK ÖNERİLERİ ==========
|
||
# Uzun kombinasyonlar
|
||
if favorite_prob >= 0.50 and btts_prob >= 0.50 and over_25_prob >= 0.60:
|
||
combo_prob = favorite_prob * btts_prob * over_25_prob
|
||
combo_odds = odds.get(favorite, 2.00) * odds.get("btts_y", 1.75) * odds.get("ou25_o", 1.90)
|
||
if combo_prob >= 0.15: # En az %15 olasılık
|
||
extreme_risk.append(BetRecommendation(
|
||
market=f"MS {favorite} + KG Var + 2.5 Üst",
|
||
pick=f"{favorite} & BTTS & OVER",
|
||
odds=combo_odds,
|
||
probability=combo_prob,
|
||
confidence=combo_prob * 100,
|
||
risk_level=RiskLevel.EXTREME
|
||
))
|
||
|
||
return MatchPredictionSet(
|
||
match_name=match_name,
|
||
predicted_score=predicted_score,
|
||
home_win_prob=home_prob,
|
||
draw_prob=draw_prob,
|
||
away_win_prob=away_prob,
|
||
over_15_prob=over_15_prob,
|
||
over_25_prob=over_25_prob,
|
||
over_35_prob=over_35_prob,
|
||
btts_yes_prob=btts_prob,
|
||
low_risk_bets=low_risk,
|
||
medium_risk_bets=medium_risk,
|
||
high_risk_bets=high_risk,
|
||
extreme_risk_bets=extreme_risk
|
||
)
|
||
|
||
|
||
# Singleton
|
||
_recommender = None
|
||
|
||
def get_smart_bet_recommender() -> SmartBetRecommender:
|
||
global _recommender
|
||
if _recommender is None:
|
||
_recommender = SmartBetRecommender()
|
||
return _recommender |