This commit is contained in:
@@ -0,0 +1,357 @@
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user