@@ -86,6 +86,28 @@ POST_CAL_TRUST: Dict[str, float] = {
|
||||
|
||||
|
||||
class MarketBoardMixin:
|
||||
def _league_confidence_for(self, league_id: Optional[str]) -> Optional[Dict[str, Any]]:
|
||||
"""Return the backtest-derived confidence record for a league, or None.
|
||||
|
||||
Shape: {"label": high|medium|low, "bet_roi": float, "bet_n": int,
|
||||
"hit": float}. None → league absent or too few bets ('unknown') → FE
|
||||
shows no badge. Never raises (missing artifact = graceful None)."""
|
||||
if not league_id:
|
||||
return None
|
||||
lookup = getattr(self, "league_confidence", None) or {}
|
||||
info = lookup.get(str(league_id))
|
||||
if not isinstance(info, dict):
|
||||
return None
|
||||
label = info.get("label")
|
||||
if label in (None, "unknown"):
|
||||
return None
|
||||
return {
|
||||
"label": label,
|
||||
"bet_roi": info.get("bet_roi"),
|
||||
"bet_n": info.get("bet_n"),
|
||||
"hit": info.get("hit"),
|
||||
}
|
||||
|
||||
def _build_prediction_package(
|
||||
self,
|
||||
data: MatchData,
|
||||
@@ -320,6 +342,10 @@ class MarketBoardMixin:
|
||||
"home_team": data.home_team_name,
|
||||
"away_team": data.away_team_name,
|
||||
"league": data.league_name,
|
||||
"league_id": data.league_id,
|
||||
# Backtest-derived per-league confidence (ROI + sample size).
|
||||
# None when the league has too little data to judge → FE shows no badge.
|
||||
"league_confidence": self._league_confidence_for(data.league_id),
|
||||
"match_date_ms": data.match_date_ms,
|
||||
"sport": data.sport,
|
||||
# Live snapshot — match_commentary uses this to detect upset-in-progress
|
||||
|
||||
@@ -57,6 +57,7 @@ from services.v26_shadow_engine import V26ShadowEngine, get_v26_shadow_engine
|
||||
from services.match_commentary import generate_match_commentary
|
||||
from utils.top_leagues import load_top_league_ids
|
||||
from utils.league_reliability import load_league_reliability
|
||||
from utils.league_confidence import load_league_confidence
|
||||
from config.config_loader import build_threshold_dict, get_threshold_default, get_config
|
||||
from models.calibration import get_calibrator
|
||||
|
||||
@@ -171,6 +172,7 @@ class SingleMatchOrchestrator(
|
||||
self.engine_mode = str(os.getenv("AI_ENGINE_MODE", "v28-pro-max")).strip().lower()
|
||||
self.top_league_ids = load_top_league_ids()
|
||||
self.league_reliability = load_league_reliability()
|
||||
self.league_confidence = load_league_confidence()
|
||||
self.enrichment = FeatureEnrichmentService()
|
||||
self.odds_band_analyzer = OddsBandAnalyzer()
|
||||
# ── Market Thresholds (loaded from config/market_thresholds.json) ──
|
||||
|
||||
Reference in New Issue
Block a user