""" Value Betting Calculator Expected Value (EV) ve stake önerileri hesaplar. """ from typing import Dict, Optional from dataclasses import dataclass @dataclass class ValueBet: """Value bet analiz sonucu""" bet_type: str # MS_1, AU25_Üst, KG_Var my_probability: float # Bizim tahminimiz market_odds: float # Bahis oranı implied_probability: float # Oranın ima ettiği olasılık edge: float # Fark (benim tahmin - implied) expected_value: float # EV = (prob × odds) - 1 is_value: bool # EV > threshold mı? kelly_fraction: float # Kelly stake oranı confidence_tier: str # "banker", "strong", "value", "skip" def to_dict(self) -> Dict: return { 'bet_type': self.bet_type, 'my_probability': round(self.my_probability, 4), 'market_odds': self.market_odds, 'implied_probability': round(self.implied_probability, 4), 'edge': round(self.edge, 4), 'expected_value': round(self.expected_value, 4), 'is_value': self.is_value, 'kelly_fraction': round(self.kelly_fraction, 4), 'confidence_tier': self.confidence_tier, } class ValueCalculator: """ Value Betting Calculator Tahminleri oranlarla karşılaştırarak EV hesaplar. """ # Eşikler MIN_EDGE_FOR_VALUE = 0.05 # Minimum %5 edge MIN_EDGE_FOR_STRONG = 0.10 # %10+ edge = strong value MIN_EDGE_FOR_BANKER = 0.15 # %15+ edge = banker KELLY_FRACTION = 0.25 # 1/4 Kelly (güvenli) MAX_STAKE_PERCENT = 0.10 # Maksimum bank'ın %10'u def __init__(self): pass def calculate_implied_probability(self, odds: float) -> float: """Bahis oranından implied probability hesapla""" if odds <= 1: return 1.0 return 1 / odds def calculate_ev(self, probability: float, odds: float) -> float: """ Expected Value hesapla. EV = (Probability × Odds) - 1 Pozitif EV = uzun vadede kar Negatif EV = uzun vadede zarar """ return (probability * odds) - 1 def calculate_kelly_stake(self, probability: float, odds: float) -> float: """ Kelly Criterion stake hesapla. Kelly = (p × b - q) / b Burada: - p = kazanma olasılığı - q = kaybetme olasılığı (1 - p) - b = odds - 1 (net kar) """ if odds <= 1: return 0 b = odds - 1 p = probability q = 1 - p kelly = (p * b - q) / b # Negatif veya çok yüksek değerleri sınırla kelly = max(0, min(kelly, self.MAX_STAKE_PERCENT)) # Fractional Kelly (daha güvenli) return kelly * self.KELLY_FRACTION def analyze_bet(self, bet_type: str, my_probability: float, market_odds: float) -> ValueBet: """ Tek bir bahis için value analizi yap. Args: bet_type: Bahis türü (MS_1, AU25_Üst, KG_Var vb.) my_probability: Bizim tahminimiz (0-1 arası) market_odds: Bahis oranı Returns: ValueBet: Analiz sonucu """ if market_odds <= 1: return ValueBet( bet_type=bet_type, my_probability=my_probability, market_odds=market_odds, implied_probability=1.0, edge=0, expected_value=-1, is_value=False, kelly_fraction=0, confidence_tier="skip" ) implied = self.calculate_implied_probability(market_odds) edge = my_probability - implied ev = self.calculate_ev(my_probability, market_odds) kelly = self.calculate_kelly_stake(my_probability, market_odds) # Tier belirleme if edge >= self.MIN_EDGE_FOR_BANKER and my_probability >= 0.70: tier = "banker" elif edge >= self.MIN_EDGE_FOR_STRONG: tier = "strong" elif edge >= self.MIN_EDGE_FOR_VALUE: tier = "value" else: tier = "skip" return ValueBet( bet_type=bet_type, my_probability=my_probability, market_odds=market_odds, implied_probability=implied, edge=edge, expected_value=ev, is_value=edge >= self.MIN_EDGE_FOR_VALUE, kelly_fraction=kelly, confidence_tier=tier ) def analyze_match_predictions(self, predictions: Dict[str, float], odds: Dict[str, float]) -> Dict[str, ValueBet]: """ Maç için tüm tahminleri analiz et. Args: predictions: Tahminler {'MS_1': 0.55, 'MS_X': 0.25, ...} odds: Oranlar {'MS_1': 1.80, 'MS_X': 3.50, ...} Returns: Dict[str, ValueBet]: Her bahis için value analizi """ results = {} for bet_type, probability in predictions.items(): if bet_type in odds and odds[bet_type] > 1: results[bet_type] = self.analyze_bet( bet_type=bet_type, my_probability=probability, market_odds=odds[bet_type] ) return results def get_best_value_bets(self, value_bets: Dict[str, ValueBet], top_n: int = 3) -> list: """En iyi value bet'leri döndür""" valid_bets = [vb for vb in value_bets.values() if vb.is_value] sorted_bets = sorted(valid_bets, key=lambda x: x.expected_value, reverse=True) return sorted_bets[:top_n] def calculate_stake(self, value_bet: ValueBet, bankroll: float, use_kelly: bool = True) -> float: """ Önerilen stake miktarını hesapla. Args: value_bet: Value bet analizi bankroll: Toplam bütçe use_kelly: Kelly criterion kullan mı? Returns: float: Önerilen stake miktarı """ if not value_bet.is_value: return 0 if use_kelly: return bankroll * value_bet.kelly_fraction else: # Tier bazlı sabit stake tier_stakes = { "banker": 0.05, "strong": 0.03, "value": 0.02, "skip": 0 } return bankroll * tier_stakes.get(value_bet.confidence_tier, 0) # Singleton _calculator = None def get_value_calculator() -> ValueCalculator: global _calculator if _calculator is None: _calculator = ValueCalculator() return _calculator if __name__ == "__main__": calc = get_value_calculator() print("\n🧪 Value Calculator Test") print("=" * 50) # Test senaryoları test_cases = [ {"bet": "MS_1", "prob": 0.70, "odds": 1.60}, # High prob, low odds {"bet": "MS_1", "prob": 0.55, "odds": 1.90}, # Medium prob, good odds {"bet": "MS_1", "prob": 0.60, "odds": 2.10}, # VALUE! {"bet": "AU25_Üst", "prob": 0.65, "odds": 1.85}, # VALUE! {"bet": "KG_Var", "prob": 0.50, "odds": 1.70}, # No value ] for tc in test_cases: result = calc.analyze_bet(tc["bet"], tc["prob"], tc["odds"]) status_emoji = "✅" if result.is_value else "❌" tier_emoji = {"banker": "🎯", "strong": "💪", "value": "✓", "skip": "⏭️"} print(f"\n{status_emoji} {tc['bet']}") print(f" Tahmin: {tc['prob']:.0%} | Oran: {tc['odds']:.2f} | Implied: {result.implied_probability:.0%}") print(f" Edge: {result.edge:+.1%} | EV: {result.expected_value:+.1%}") print(f" Tier: {tier_emoji.get(result.confidence_tier, '')} {result.confidence_tier.upper()}") print(f" Kelly Stake: {result.kelly_fraction:.2%} of bankroll") if result.is_value: stake = calc.calculate_stake(result, 1000) print(f" 💰 Önerilen Stake (1000 TL bank): {stake:.2f} TL")