Files
iddaai-be/ai-engine/schemas/prediction.py
T
fahricansecer 94c7a4481a
Deploy Iddaai Backend / build-and-deploy (push) Successful in 37s
main
2026-05-17 02:17:22 +03:00

293 lines
11 KiB
Python
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.
"""
Shared prediction dataclasses used across the AI engine.
These were originally defined in models/v20_ensemble.py and are extracted here
so they can be used without importing the full V20 ensemble.
"""
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional
from core.calculators.score_calculator import ScorePrediction
@dataclass
class MarketPrediction:
"""Prediction for a single betting market."""
market_type: str
pick: str
probability: float
confidence: float
odds: float = 0.0
is_recommended: bool = False
is_value_bet: bool = False
edge: float = 0.0 # Expected edge over market
def to_dict(self) -> dict:
return {
"market_type": self.market_type,
"pick": self.pick,
"probability": round(self.probability * 100, 1),
"confidence": round(self.confidence, 1),
"odds": self.odds,
"is_recommended": self.is_recommended,
"is_value_bet": self.is_value_bet,
"edge": round(self.edge, 1)
}
@dataclass
class FullMatchPrediction:
"""Complete prediction for a match with ALL markets."""
match_id: str
home_team: str
away_team: str
match_date: str = ""
# === MAÇ SONUCU (1X2) ===
ms_home_prob: float = 0.33
ms_draw_prob: float = 0.33
ms_away_prob: float = 0.33
ms_pick: str = ""
ms_confidence: float = 0.0
# === ÇİFTE ŞANS ===
dc_1x_prob: float = 0.66
dc_x2_prob: float = 0.66
dc_12_prob: float = 0.66
dc_pick: str = ""
dc_confidence: float = 0.0
# === ALT/ÜST GOLLER ===
# 1.5
over_15_prob: float = 0.70
under_15_prob: float = 0.30
ou15_pick: str = ""
ou15_confidence: float = 0.0
# 2.5
over_25_prob: float = 0.50
under_25_prob: float = 0.50
ou25_pick: str = ""
ou25_confidence: float = 0.0
# 3.5
over_35_prob: float = 0.30
under_35_prob: float = 0.70
ou35_pick: str = ""
ou35_confidence: float = 0.0
# === KARŞILIKLI GOL (BTTS) ===
btts_yes_prob: float = 0.50
btts_no_prob: float = 0.50
btts_pick: str = ""
btts_confidence: float = 0.0
# === İLK YARI SONUCU ===
ht_home_prob: float = 0.30
ht_draw_prob: float = 0.40
ht_away_prob: float = 0.30
ht_pick: str = ""
ht_confidence: float = 0.0
# === SKOR TAHMİNLERİ ===
score: Optional[ScorePrediction] = None
predicted_ft_score: str = "1-1"
predicted_ht_score: str = "0-0"
ft_scores_top5: List[Dict] = field(default_factory=list)
# === xG (Expected Goals) ===
home_xg: float = 1.3
away_xg: float = 1.1
total_xg: float = 2.4
# === RISK DEĞERLENDİRMESİ ===
risk_level: str = "MEDIUM" # LOW, MEDIUM, HIGH, EXTREME
risk_score: float = 0.0
is_surprise_risk: bool = False
surprise_type: str = ""
risk_warnings: List[str] = field(default_factory=list)
ht_ft_probs: Dict[str, float] = field(default_factory=dict)
# === GLM-5 SÜRPRİZ SKORU ===
upset_score: int = 0 # 0-100 arası sürpriz skoru
upset_level: str = "LOW" # LOW, MEDIUM, HIGH, EXTREME
upset_reasons: List[str] = field(default_factory=list)
# === SÜRPRİZ PROFİLİ ===
surprise_score: float = 0.0 # 0-100 overall surprise risk score
surprise_comment: str = "" # Human-readable surprise commentary
surprise_reasons: List[str] = field(default_factory=list) # Flagged risk reasons
surprise_breakdown: List[Dict[str, Any]] = field(default_factory=list) # Per-factor {code, points, label}
# === ENGINE KATKILARI ===
team_confidence: float = 0.0
player_confidence: float = 0.0
odds_confidence: float = 0.0
referee_confidence: float = 0.0
# === KORNER & KART & DİĞER ===
total_corners_pred: float = 9.5
corner_pick: str = "9.5 Üst"
total_cards_pred: float = 4.5
card_pick: str = "4.5 Alt"
cards_over_prob: float = 0.50
cards_under_prob: float = 0.50
cards_confidence: float = 0.0
handicap_pick: str = ""
handicap_home_prob: float = 0.33
handicap_draw_prob: float = 0.34
handicap_away_prob: float = 0.33
handicap_confidence: float = 0.0
ht_over_05_prob: float = 0.65
ht_under_05_prob: float = 0.35
ht_over_15_prob: float = 0.30
ht_under_15_prob: float = 0.70
ht_ou_pick: str = "İY 0.5 Üst"
ht_ou15_pick: str = "İY 1.5 Alt"
odd_even_pick: str = "Çift"
odd_prob: float = 0.50 # Tek olasılığı
even_prob: float = 0.50 # Çift olasılığı
# === TAVSİYELER (RECOMMENDATIONS) ===
best_bet: Optional[MarketPrediction] = None
recommended_bets: List[MarketPrediction] = field(default_factory=list)
alternative_bet: Optional[MarketPrediction] = None
expert_recommendation: Dict[str, Any] = field(default_factory=dict)
# === DETAILED ANALYSIS ===
analysis_details: Dict[str, Any] = field(default_factory=dict)
def to_dict(self) -> dict:
return {
"match_info": {
"match_id": self.match_id,
"home_team": self.home_team,
"away_team": self.away_team,
"match_date": self.match_date
},
"predictions": {
"match_result": {
"1": round(self.ms_home_prob * 100, 1),
"X": round(self.ms_draw_prob * 100, 1),
"2": round(self.ms_away_prob * 100, 1),
"pick": self.ms_pick,
"confidence": round(self.ms_confidence, 1)
},
"double_chance": {
"1X": round(self.dc_1x_prob * 100, 1),
"X2": round(self.dc_x2_prob * 100, 1),
"12": round(self.dc_12_prob * 100, 1),
"pick": self.dc_pick,
"confidence": round(self.dc_confidence, 1)
},
"over_under": {
"1.5": {
"over": round(self.over_15_prob * 100, 1),
"under": round(self.under_15_prob * 100, 1),
"pick": self.ou15_pick,
"confidence": round(self.ou15_confidence, 1)
},
"2.5": {
"over": round(self.over_25_prob * 100, 1),
"under": round(self.under_25_prob * 100, 1),
"pick": self.ou25_pick,
"confidence": round(self.ou25_confidence, 1)
},
"3.5": {
"over": round(self.over_35_prob * 100, 1),
"under": round(self.under_35_prob * 100, 1),
"pick": self.ou35_pick,
"confidence": round(self.ou35_confidence, 1)
}
},
"btts": {
"yes": round(self.btts_yes_prob * 100, 1),
"no": round(self.btts_no_prob * 100, 1),
"pick": self.btts_pick,
"confidence": round(self.btts_confidence, 1)
},
"first_half": {
"1": round(self.ht_home_prob * 100, 1),
"X": round(self.ht_draw_prob * 100, 1),
"2": round(self.ht_away_prob * 100, 1),
"pick": self.ht_pick,
"confidence": round(self.ht_confidence, 1),
"over_under_05": {
"over": round(self.ht_over_05_prob * 100, 1),
"under": round(self.ht_under_05_prob * 100, 1),
"pick": self.ht_ou_pick
},
"over_under_15": {
"over": round(self.ht_over_15_prob * 100, 1),
"under": round(self.ht_under_15_prob * 100, 1),
"pick": self.ht_ou15_pick
}
},
"scores": {
"predicted_ft": self.predicted_ft_score,
"predicted_ht": self.predicted_ht_score,
"top_5_ft_scores": self.ft_scores_top5
},
"others": {
"handicap": {
"pick": self.handicap_pick,
"confidence": round(self.handicap_confidence, 1),
"home": round(self.handicap_home_prob * 100, 1),
"draw": round(self.handicap_draw_prob * 100, 1),
"away": round(self.handicap_away_prob * 100, 1)
},
"corners": {
"total": round(self.total_corners_pred, 1),
"pick": self.corner_pick
},
"cards": {
"total": round(self.total_cards_pred, 1),
"pick": self.card_pick,
"confidence": round(self.cards_confidence, 1),
"over": round(self.cards_over_prob * 100, 1),
"under": round(self.cards_under_prob * 100, 1)
},
"odd_even": {
"pick": self.odd_even_pick,
"tek": round(self.odd_prob * 100, 1),
"cift": round(self.even_prob * 100, 1)
}
},
"xg": {
"home": round(self.home_xg, 2),
"away": round(self.away_xg, 2),
"total": round(self.total_xg, 2)
}
},
"risk": {
"level": self.risk_level,
"score": round(self.risk_score, 1),
"is_surprise_risk": self.is_surprise_risk,
"surprise_type": self.surprise_type,
"ht_ft_probs": {k: round(v * 100, 1) for k, v in self.ht_ft_probs.items()} if self.ht_ft_probs else {},
"warnings": self.risk_warnings
},
"upset_analysis": {
"score": self.upset_score,
"level": self.upset_level,
"reasons": self.upset_reasons
},
"engine_breakdown": {
"team_engine": round(self.team_confidence, 1),
"player_engine": round(self.player_confidence, 1),
"odds_engine": round(self.odds_confidence, 1),
"referee_engine": round(self.referee_confidence, 1)
},
"recommendations": {
"best_bet": self.best_bet.to_dict() if self.best_bet else None,
"all_recommended": [b.to_dict() for b in self.recommended_bets] if self.recommended_bets else [],
"alternative_bet": self.alternative_bet.to_dict() if self.alternative_bet else None
},
"analysis_details": self.analysis_details
}