""" Poisson Engine - Matematiksel Gol Modeli V9 Model için Poisson dağılımı ile gol olasılıkları hesaplar. Özellikler: 1. Exact score olasılıkları (0-0, 1-0, 1-1, 2-1, vb.) 2. Over/Under olasılıkları (matematiksel) 3. BTTS (Karşılıklı Gol) olasılıkları 4. Expected Goals (xG) tahmini """ import math from typing import Dict, Tuple, Optional from dataclasses import dataclass, field def poisson_prob(lam: float, k: int) -> float: """ Poisson olasılık formülü. P(X = k) = (λ^k * e^(-λ)) / k! """ if lam <= 0: return 1.0 if k == 0 else 0.0 return (math.pow(lam, k) * math.exp(-lam)) / math.factorial(k) @dataclass class PoissonPrediction: """Poisson tahmin sonuçları""" home_xg: float = 0.0 # Ev sahibi beklenen gol away_xg: float = 0.0 # Deplasman beklenen gol total_xg: float = 0.0 # Toplam beklenen gol # Maç sonucu olasılıkları home_win_prob: float = 0.0 draw_prob: float = 0.0 away_win_prob: float = 0.0 # Alt/Üst olasılıkları over_15_prob: float = 0.0 over_25_prob: float = 0.0 over_35_prob: float = 0.0 under_15_prob: float = 0.0 under_25_prob: float = 0.0 under_35_prob: float = 0.0 # BTTS btts_yes_prob: float = 0.0 btts_no_prob: float = 0.0 # En olası skorlar most_likely_scores: list = field(default_factory=list) class PoissonEngine: """ Poisson dağılımı ile gol olasılıkları hesaplar. İstatistiksel bir yaklaşım - machine learning'den bağımsız. """ # Lig bazlı ortalama gol verileri (varsayılan değerler) DEFAULT_HOME_XG = 1.45 DEFAULT_AWAY_XG = 1.15 DEFAULT_LEAGUE_AVG = 2.60 def __init__(self): self.max_goals = 7 # Hesaplama için maksimum gol sayısı def calculate_xg( self, home_goals_avg: float, home_conceded_avg: float, away_goals_avg: float, away_conceded_avg: float, league_home_avg: float = None, league_away_avg: float = None, league_total_avg: float = None ) -> Tuple[float, float]: """ Beklenen gol (xG) hesapla. Attack strength * Defense weakness * League average """ # Varsayılan lig ortalamaları if league_home_avg is None: league_home_avg = self.DEFAULT_HOME_XG if league_away_avg is None: league_away_avg = self.DEFAULT_AWAY_XG if league_total_avg is None: league_total_avg = self.DEFAULT_LEAGUE_AVG # Güç hesaplamaları # Ev sahibi saldırı gücü = Ev gol ortalaması / Lig ev gol ortalaması home_attack = home_goals_avg / league_home_avg if league_home_avg > 0 else 1.0 # Deplasman savunma zayıflığı = Deplasman yenilen gol / Lig deplasman yenilen away_defense = away_conceded_avg / league_away_avg if league_away_avg > 0 else 1.0 # Deplasman saldırı gücü away_attack = away_goals_avg / league_away_avg if league_away_avg > 0 else 1.0 # Ev sahibi savunma zayıflığı home_defense = home_conceded_avg / league_home_avg if league_home_avg > 0 else 1.0 # Expected Goals home_xg = home_attack * away_defense * league_home_avg away_xg = away_attack * home_defense * league_away_avg # Aşırı değerleri sınırla home_xg = max(0.3, min(home_xg, 4.0)) away_xg = max(0.2, min(away_xg, 3.5)) return home_xg, away_xg def calculate_score_matrix( self, home_xg: float, away_xg: float ) -> Dict[Tuple[int, int], float]: """ Tüm skor kombinasyonlarının olasılıklarını hesapla. Returns: Dict[(home_goals, away_goals)] = probability """ matrix = {} for home_goals in range(self.max_goals + 1): for away_goals in range(self.max_goals + 1): prob = poisson_prob(home_xg, home_goals) * poisson_prob(away_xg, away_goals) matrix[(home_goals, away_goals)] = prob return matrix def calculate_match_odds( self, home_xg: float, away_xg: float ) -> Tuple[float, float, float]: """ 1X2 olasılıklarını hesapla. Returns: (home_win, draw, away_win) probabilities """ matrix = self.calculate_score_matrix(home_xg, away_xg) home_win = 0.0 draw = 0.0 away_win = 0.0 for (h, a), prob in matrix.items(): if h > a: home_win += prob elif h == a: draw += prob else: away_win += prob # Normalize (toplam 1 olmalı) total = home_win + draw + away_win if total > 0: home_win /= total draw /= total away_win /= total return home_win, draw, away_win def calculate_over_under( self, home_xg: float, away_xg: float ) -> Dict[str, float]: """ Alt/Üst olasılıklarını hesapla. """ matrix = self.calculate_score_matrix(home_xg, away_xg) over_15 = 0.0 over_25 = 0.0 over_35 = 0.0 for (h, a), prob in matrix.items(): total = h + a if total > 1.5: over_15 += prob if total > 2.5: over_25 += prob if total > 3.5: over_35 += prob return { "over_15": over_15, "over_25": over_25, "over_35": over_35, "under_15": 1 - over_15, "under_25": 1 - over_25, "under_35": 1 - over_35, } def calculate_btts( self, home_xg: float, away_xg: float ) -> Tuple[float, float]: """ Karşılıklı Gol (Both Teams To Score) olasılığı. """ # P(Home scores at least 1) = 1 - P(Home scores 0) home_scores = 1 - poisson_prob(home_xg, 0) # P(Away scores at least 1) = 1 - P(Away scores 0) away_scores = 1 - poisson_prob(away_xg, 0) # P(BTTS) = P(Home scores) * P(Away scores) btts_yes = home_scores * away_scores btts_no = 1 - btts_yes return btts_yes, btts_no def get_most_likely_scores( self, home_xg: float, away_xg: float, top_n: int = 5 ) -> list: """ En olası skorları getir. """ matrix = self.calculate_score_matrix(home_xg, away_xg) # Olasılığa göre sırala sorted_scores = sorted(matrix.items(), key=lambda x: x[1], reverse=True) return [ {"score": f"{h}-{a}", "probability": round(prob * 100, 1)} for (h, a), prob in sorted_scores[:top_n] ] def predict( self, home_goals_avg: float, home_conceded_avg: float, away_goals_avg: float, away_conceded_avg: float, league_home_avg: float = None, league_away_avg: float = None, league_total_avg: float = None ) -> PoissonPrediction: """ Tam Poisson tahmini. """ prediction = PoissonPrediction() # 1. xG hesapla home_xg, away_xg = self.calculate_xg( home_goals_avg, home_conceded_avg, away_goals_avg, away_conceded_avg, league_home_avg, league_away_avg, league_total_avg ) prediction.home_xg = round(home_xg, 2) prediction.away_xg = round(away_xg, 2) prediction.total_xg = round(home_xg + away_xg, 2) # 2. Maç sonucu hw, d, aw = self.calculate_match_odds(home_xg, away_xg) prediction.home_win_prob = round(hw, 3) prediction.draw_prob = round(d, 3) prediction.away_win_prob = round(aw, 3) # 3. Alt/Üst ou = self.calculate_over_under(home_xg, away_xg) prediction.over_15_prob = round(ou["over_15"], 3) prediction.over_25_prob = round(ou["over_25"], 3) prediction.over_35_prob = round(ou["over_35"], 3) prediction.under_15_prob = round(ou["under_15"], 3) prediction.under_25_prob = round(ou["under_25"], 3) prediction.under_35_prob = round(ou["under_35"], 3) # 4. BTTS btts_yes, btts_no = self.calculate_btts(home_xg, away_xg) prediction.btts_yes_prob = round(btts_yes, 3) prediction.btts_no_prob = round(btts_no, 3) # 5. En olası skorlar prediction.most_likely_scores = self.get_most_likely_scores(home_xg, away_xg) return prediction def get_features( self, home_goals_avg: float, home_conceded_avg: float, away_goals_avg: float, away_conceded_avg: float, league_home_avg: float = None, league_away_avg: float = None, league_total_avg: float = None ) -> Dict[str, float]: """ Model için feature dict. """ pred = self.predict( home_goals_avg, home_conceded_avg, away_goals_avg, away_conceded_avg, league_home_avg, league_away_avg, league_total_avg ) return { "poisson_home_xg": pred.home_xg, "poisson_away_xg": pred.away_xg, "poisson_total_xg": pred.total_xg, "poisson_home_win": pred.home_win_prob, "poisson_draw": pred.draw_prob, "poisson_away_win": pred.away_win_prob, "poisson_over_15": pred.over_15_prob, "poisson_over_25": pred.over_25_prob, "poisson_over_35": pred.over_35_prob, "poisson_btts_yes": pred.btts_yes_prob, } # Singleton _engine_instance = None def get_poisson_engine() -> PoissonEngine: """Singleton pattern""" global _engine_instance if _engine_instance is None: _engine_instance = PoissonEngine() return _engine_instance # Test if __name__ == "__main__": engine = get_poisson_engine() # Örnek: Güçlü ev sahibi vs zayıf deplasman print("=" * 60) print("POISSON ENGINE TEST") print("Galatasaray (ev) vs Antalyaspor (deplasman)") print("=" * 60) pred = engine.predict( home_goals_avg=2.1, # GS ev ortalaması home_conceded_avg=0.8, # GS ev yenilen away_goals_avg=0.9, # Antalya deplasman gol away_conceded_avg=1.8, # Antalya deplasman yenilen league_home_avg=1.5, league_away_avg=1.1 ) print(f"\n📊 Expected Goals:") print(f" Ev Sahibi xG: {pred.home_xg}") print(f" Deplasman xG: {pred.away_xg}") print(f" Toplam xG: {pred.total_xg}") print(f"\n🎯 Maç Sonucu:") print(f" 1 (Ev): {pred.home_win_prob*100:.1f}%") print(f" X (Beraberlik): {pred.draw_prob*100:.1f}%") print(f" 2 (Deplasman): {pred.away_win_prob*100:.1f}%") print(f"\n⚽ Alt/Üst:") print(f" 2.5 Üst: {pred.over_25_prob*100:.1f}%") print(f" 2.5 Alt: {pred.under_25_prob*100:.1f}%") print(f"\n🤝 Karşılıklı Gol:") print(f" KG Var: {pred.btts_yes_prob*100:.1f}%") print(f" KG Yok: {pred.btts_no_prob*100:.1f}%") print(f"\n📈 En Olası Skorlar:") for score_data in pred.most_likely_scores: print(f" {score_data['score']}: {score_data['probability']}%")