Files
iddaai-be/ai-engine/features/value_calculator.py
T
fahricansecer 2f0b85a0c7
Deploy Iddaai Backend / build-and-deploy (push) Failing after 18s
first (part 2: other directories)
2026-04-16 15:11:25 +03:00

250 lines
8.1 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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")