Files
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

126 lines
4.1 KiB
Python

"""
Pydantic v2 response schemas for the V2 Betting Engine.
Strictly mirrors the NestJS DTO contract for SingleMatchPredictionPackage.
"""
from __future__ import annotations
from typing import Any
from pydantic import BaseModel, Field
# ── Sub-models ──────────────────────────────────────────────────────────────
class MatchInfo(BaseModel):
match_id: str
match_name: str = ""
home_team: str = ""
away_team: str = ""
league: str = ""
match_date_ms: int = 0
class DataQuality(BaseModel):
label: str = Field(default="MEDIUM", description="HIGH | MEDIUM | LOW")
score: float = Field(default=0.5, ge=0.0, le=1.0)
flags: list[str] = Field(default_factory=list)
home_lineup_count: int = 0
away_lineup_count: int = 0
class RiskAssessment(BaseModel):
level: str = Field(default="MEDIUM", description="LOW | MEDIUM | HIGH | EXTREME")
score: float = Field(default=0.0, ge=0.0, le=1.0)
is_surprise_risk: bool = False
surprise_type: str | None = None
warnings: list[str] = Field(default_factory=list)
class PickDetail(BaseModel):
market: str = Field(..., description="MS, OU25, BTTS, DC, HT, HTFT, etc.")
pick: str = Field(..., description="1, X, 2, Over, Under, Yes, No, 1/1, etc.")
probability: float = Field(..., ge=0.0, le=1.0)
confidence: float = Field(default=0.0, description="Percentage 0-100")
odds: float | None = Field(default=None, gt=0.0)
raw_confidence: float = 0.0
calibrated_confidence: float = 0.0
min_required_confidence: float = 0.0
edge: float = Field(default=0.0, description="Model prob minus implied prob")
play_score: float = Field(default=0.0, ge=0.0, le=100.0)
playable: bool = False
bet_grade: str = Field(default="PASS", description="A | B | C | PASS")
stake_units: float = Field(default=0.0, ge=0.0)
decision_reasons: list[str] = Field(default_factory=list)
class BetAdvice(BaseModel):
playable: bool = False
suggested_stake_units: float = 0.0
reason: str = "no_playable_pick"
class BetSummaryRow(BaseModel):
market: str
pick: str
raw_confidence: float = 0.0
calibrated_confidence: float = 0.0
bet_grade: str = "PASS"
playable: bool = False
stake_units: float = 0.0
play_score: float = 0.0
reasons: list[str] = Field(default_factory=list)
class ScoreScenario(BaseModel):
score: str
prob: float
class ScorePrediction(BaseModel):
ft: str = "0-0"
ht: str = "0-0"
xg_home: float = 0.0
xg_away: float = 0.0
xg_total: float = 0.0
class EngineBreakdown(BaseModel):
team: float = 0.0
player: float = 0.0
odds: float = 0.0
referee: float = 0.0
class MarketProbs(BaseModel):
pick: str = ""
confidence: float = 0.0
probs: dict[str, float] = Field(default_factory=dict)
# ── Root Response ───────────────────────────────────────────────────────────
class PredictionResponse(BaseModel):
"""
Root API contract. Every field matches the NestJS
`SingleMatchPredictionPackage` DTO exactly.
"""
model_version: str = "v2.betting_engine"
match_info: MatchInfo
data_quality: DataQuality = Field(default_factory=DataQuality)
risk: RiskAssessment = Field(default_factory=RiskAssessment)
engine_breakdown: EngineBreakdown = Field(default_factory=EngineBreakdown)
main_pick: PickDetail | None = None
value_pick: PickDetail | None = None
bet_advice: BetAdvice = Field(default_factory=BetAdvice)
bet_summary: list[BetSummaryRow] = Field(default_factory=list)
supporting_picks: list[PickDetail] = Field(default_factory=list)
aggressive_pick: PickDetail | None = None
scenario_top5: list[ScoreScenario] = Field(default_factory=list)
score_prediction: ScorePrediction = Field(default_factory=ScorePrediction)
market_board: dict[str, Any] = Field(default_factory=dict)
reasoning_factors: list[str] = Field(default_factory=list)