""" Referee Predictor Engine - V20 Ensemble Component Analyzes referee patterns for cards, goals, and home bias. Weight: 15% in ensemble """ import os import sys from typing import Dict, Optional from dataclasses import dataclass sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) from features.referee_engine import get_referee_engine @dataclass class RefereePrediction: """Referee engine prediction output.""" referee_name: str = "" matches_officiated: int = 0 # Card tendencies avg_yellow_cards: float = 4.0 avg_red_cards: float = 0.2 is_card_heavy: bool = False # Above average cards # Goal tendencies avg_goals_per_match: float = 2.5 over_25_rate: float = 0.50 is_high_scoring: bool = False # Above average goals # Home bias home_win_rate: float = 0.45 home_bias: float = 0.0 # -1 to +1, positive = favors home # Penalty tendency penalty_rate: float = 0.15 confidence: float = 0.0 def to_dict(self) -> dict: return { "referee_name": self.referee_name, "matches_officiated": self.matches_officiated, "avg_yellow_cards": round(self.avg_yellow_cards, 1), "avg_red_cards": round(self.avg_red_cards, 2), "is_card_heavy": self.is_card_heavy, "avg_goals_per_match": round(self.avg_goals_per_match, 2), "over_25_rate": round(self.over_25_rate * 100, 1), "is_high_scoring": self.is_high_scoring, "home_win_rate": round(self.home_win_rate * 100, 1), "home_bias": round(self.home_bias, 2), "penalty_rate": round(self.penalty_rate * 100, 1), "confidence": round(self.confidence, 1) } class RefereePredictorEngine: """ Referee-based prediction engine. Analyzes: - Card tendency (sarı/kırmızı kart ortalaması) - Goal tendency (maç başına gol, 2.5 üst oranı) - Home bias (ev sahibi lehine karar oranı) - Penalty tendency (penaltı verme oranı) """ # League average benchmarks LEAGUE_AVG_GOALS = 2.65 LEAGUE_AVG_YELLOW = 4.0 LEAGUE_HOME_WIN_RATE = 0.45 def __init__(self): self.referee_engine = get_referee_engine() print("✅ RefereePredictorEngine initialized") def predict(self, match_id: Optional[str] = None, referee_name: Optional[str] = None, league_id: Optional[str] = None) -> RefereePrediction: """ Generate referee-based prediction. Args: match_id: Match ID to find referee referee_name: Or provide referee name directly league_id: League ID to scope stats (prevents name collisions) Returns: RefereePrediction with referee analysis """ # Get referee features if match_id: features = self.referee_engine.get_features(match_id, league_id=league_id or "") # Live flows may already have referee_name while match_officials table is sparse. # Prefer the richer profile if direct-name lookup has more history. if referee_name: name_features = self.referee_engine.get_features_by_name(referee_name, league_id=league_id or "") if (name_features.get("referee_matches", 0) or 0) > (features.get("referee_matches", 0) or 0): features = name_features elif referee_name: features = self.referee_engine.get_features_by_name(referee_name, league_id=league_id or "") else: # Return default return RefereePrediction(confidence=10.0) ref_name = str(features.get("referee_name", "Unknown")) matches = int(features.get("referee_matches", 0)) if matches < 5: # Not enough data return RefereePrediction( referee_name=ref_name, matches_officiated=matches, confidence=20.0 ) # Extract features avg_yellow = features.get("referee_avg_yellow", 4.0) avg_red = features.get("referee_avg_red", 0.2) avg_goals = features.get("referee_avg_goals", 2.5) over25_rate = features.get("referee_over25_rate", 0.5) home_win_rate = features.get("referee_home_win_rate", 0.45) if "referee_home_win_rate" in features else 0.45 home_bias = features.get("referee_home_bias", 0.0) penalty_rate = features.get("referee_penalty_rate", 0.15) # Determine tendencies is_card_heavy = (avg_yellow + avg_red * 4) > (self.LEAGUE_AVG_YELLOW + 1) is_high_scoring = avg_goals > self.LEAGUE_AVG_GOALS # Confidence based on matches officiated confidence = min(90.0, 30.0 + matches * 2) return RefereePrediction( referee_name=ref_name, matches_officiated=matches, avg_yellow_cards=avg_yellow, avg_red_cards=avg_red, is_card_heavy=is_card_heavy, avg_goals_per_match=avg_goals, over_25_rate=over25_rate, is_high_scoring=is_high_scoring, home_win_rate=home_win_rate, home_bias=home_bias, penalty_rate=penalty_rate, confidence=confidence ) def get_modifiers(self, prediction: RefereePrediction) -> Dict[str, float]: """ Get modifiers to apply to other predictions based on referee profile. """ return { # Home team gets slight boost if referee has home bias "home_modifier": 1.0 + (prediction.home_bias * 0.05), # O/U modifier "over_25_modifier": 1.0 + (prediction.avg_goals_per_match - self.LEAGUE_AVG_GOALS) * 0.1, # Card modifier for card markets "cards_modifier": 1.0 + (prediction.avg_yellow_cards - self.LEAGUE_AVG_YELLOW) * 0.05 } # Singleton _engine: Optional[RefereePredictorEngine] = None def get_referee_predictor() -> RefereePredictorEngine: global _engine if _engine is None: _engine = RefereePredictorEngine() return _engine if __name__ == "__main__": engine = get_referee_predictor() print("\n🧪 Referee Predictor Engine Test") print("=" * 50) pred = engine.predict(referee_name="Cüneyt Çakır") print(f"\n📊 Prediction:") for k, v in pred.to_dict().items(): print(f" {k}: {v}")