fix(ai-engine): sync FEATURE_COLS with trained models (82→102 features)
Deploy Iddaai Backend / build-and-deploy (push) Successful in 6s

- Load feature columns dynamically from feature_cols.json
- Add 20 missing odds_*_present boolean flags to fallback list
- Fixes LightGBM 'features in data (82) != training data (102)' crash
This commit is contained in:
2026-05-05 20:29:55 +03:00
parent 22596e69f2
commit fdb8a5d0f0
+33 -2
View File
@@ -141,8 +141,10 @@ class V25Predictor:
Each market (MS, OU25, BTTS) has its own trained models. Each market (MS, OU25, BTTS) has its own trained models.
""" """
# Feature columns (82 features, NO target leakage) # Feature columns — loaded dynamically from feature_cols.json to stay
FEATURE_COLS = [ # in sync with the trained models. The hardcoded list below is only a
# fallback in case the JSON file is missing.
_FALLBACK_FEATURE_COLS = [
# ELO Features (8) # ELO Features (8)
'home_overall_elo', 'away_overall_elo', 'elo_diff', 'home_overall_elo', 'away_overall_elo', 'elo_diff',
'home_home_elo', 'away_away_elo', 'home_home_elo', 'away_away_elo',
@@ -178,6 +180,17 @@ class V25Predictor:
'odds_ht_ou15_o', 'odds_ht_ou15_u', 'odds_ht_ou15_o', 'odds_ht_ou15_u',
'odds_btts_y', 'odds_btts_n', 'odds_btts_y', 'odds_btts_n',
# Odds Presence Flags (20)
'odds_ms_h_present', 'odds_ms_d_present', 'odds_ms_a_present',
'odds_ht_ms_h_present', 'odds_ht_ms_d_present', 'odds_ht_ms_a_present',
'odds_ou05_o_present', 'odds_ou05_u_present',
'odds_ou15_o_present', 'odds_ou15_u_present',
'odds_ou25_o_present', 'odds_ou25_u_present',
'odds_ou35_o_present', 'odds_ou35_u_present',
'odds_ht_ou05_o_present', 'odds_ht_ou05_u_present',
'odds_ht_ou15_o_present', 'odds_ht_ou15_u_present',
'odds_btts_y_present', 'odds_btts_n_present',
# League Features (4) # League Features (4)
'home_xga', 'away_xga', 'home_xga', 'away_xga',
'league_avg_goals', 'league_zero_goal_rate', 'league_avg_goals', 'league_zero_goal_rate',
@@ -199,6 +212,24 @@ class V25Predictor:
'home_goals_form', 'away_goals_form', 'home_goals_form', 'away_goals_form',
] ]
@staticmethod
def _load_feature_cols() -> list:
"""Load feature columns from feature_cols.json, falling back to hardcoded list."""
feature_json = os.path.join(MODELS_DIR, 'feature_cols.json')
try:
if os.path.exists(feature_json):
with open(feature_json, 'r', encoding='utf-8') as f:
cols = json.load(f)
if isinstance(cols, list) and len(cols) > 0:
print(f"[V25] Loaded {len(cols)} feature columns from feature_cols.json")
return cols
except Exception as e:
print(f"[V25] Warning: could not load feature_cols.json: {e}")
print(f"[V25] Using fallback feature columns ({len(V25Predictor._FALLBACK_FEATURE_COLS)} features)")
return V25Predictor._FALLBACK_FEATURE_COLS
FEATURE_COLS = _load_feature_cols.__func__()
# Model weights for ensemble # Model weights for ensemble
DEFAULT_WEIGHTS = { DEFAULT_WEIGHTS = {
'xgb': 0.50, 'xgb': 0.50,