amarorn / pipelines /predict_wc.py
beAnalytic's picture
feat: sync main with feature/superbet-live-inplay
16c19b8 verified
Raw
History Blame Contribute Delete
4.56 kB
import argparse
import json
from pathlib import Path
from models.wc_predictor import WcPredictor
from schemas.national_teams import normalize_national_team
from schemas.wc_kxl_dynamic import WcKxlMatchInput
DEFAULT_ROUND = Path("data/rounds/wc_2026.json")
def _load_round(path: Path) -> dict:
if not path.exists():
raise FileNotFoundError(f"Arquivo de rodada não encontrado: {path}")
return json.loads(path.read_text(encoding="utf-8"))
def _print_prediction(pred, verbose: bool = True) -> None:
label_map = {"1": "vitória mandante", "X": "empate", "2": "vitória visitante"}
print(f"\n{'=' * 60}")
print(f"{pred.home_team} x {pred.away_team}")
print(f"Palpite: {pred.prediction} ({label_map[pred.prediction]})")
print(f"Confiança: {pred.confidence:.1%}")
print(
f"Probabilidades: 1={pred.prob_home:.1%} | X={pred.prob_draw:.1%} | 2={pred.prob_away:.1%}"
)
print(f"Placar provável (Dixon-Coles): {pred.poisson_score} (gols esp. {pred.expected_goals})")
print(f"H2H: {pred.h2h_summary}")
if verbose:
print(f"\n{pred.context}")
print(f"\nModelos: {json.dumps(pred.model_breakdown, ensure_ascii=False)}")
def main() -> None:
parser = argparse.ArgumentParser(description="Palpites Copa do Mundo (Dixon-Coles + Logística)")
parser.add_argument("--round-file", type=Path, default=DEFAULT_ROUND, help="JSON com jogos")
parser.add_argument("--home", type=str, help="Seleção mandante (palpite avulso)")
parser.add_argument("--away", type=str, help="Seleção visitante (palpite avulso)")
parser.add_argument("--phase", type=str, default="group", help="Fase: group, round_16, quarter...")
parser.add_argument("--json", action="store_true", help="Saída JSON")
parser.add_argument("--quiet", action="store_true", help="Menos detalhes")
parser.add_argument(
"--kxl-json",
type=Path,
help="JSON com campo kxl_match (mesmo formato da API)",
)
args = parser.parse_args()
kxl_match: WcKxlMatchInput | None = None
if args.kxl_json:
payload = json.loads(args.kxl_json.read_text(encoding="utf-8"))
if "kxl_match" in payload:
kxl_match = WcKxlMatchInput.model_validate(payload["kxl_match"])
predictor = WcPredictor()
results = []
if args.home and args.away:
home = normalize_national_team(args.home)
away = normalize_national_team(args.away)
pred = predictor.predict(home, away, phase=args.phase, kxl_match=kxl_match)
results.append(pred)
else:
round_data = _load_round(args.round_file)
phase = round_data.get("phase", "group")
round_matches = round_data.get("matches", [])
for match in round_matches:
home = normalize_national_team(match["home_team"])
away = normalize_national_team(match["away_team"])
match_phase = match.get("phase", phase)
pred = predictor.predict(
home, away, phase=match_phase, kxl_match=kxl_match
)
results.append((pred, match))
if args.json:
output = []
for item in results:
if isinstance(item, tuple):
p, meta = item
row = {
"group": meta.get("group"),
"matchday": meta.get("matchday"),
"phase": meta.get("phase"),
}
else:
p, row = item, {}
output.append({
**row,
"home_team": p.home_team,
"away_team": p.away_team,
"prediction": p.prediction,
"confidence": round(p.confidence, 4),
"probabilities": {"1": p.prob_home, "X": p.prob_draw, "2": p.prob_away},
"likely_score": p.poisson_score,
"expected_goals": p.expected_goals,
"h2h": p.h2h_summary,
"models": p.model_breakdown,
})
print(json.dumps(output, ensure_ascii=False, indent=2))
else:
metrics = predictor.training_metrics
print(f"Modelo treinado com {metrics.get('train_size', '?')} jogos históricos")
if "holdout_accuracy" in metrics:
print(f"Acurácia holdout Copa {metrics.get('holdout_season')}: {metrics['holdout_accuracy']:.1%}")
for item in results:
pred = item[0] if isinstance(item, tuple) else item
_print_prediction(pred, verbose=not args.quiet)
if __name__ == "__main__":
main()