Spaces:
Configuration error
Configuration error
Download src/prediction_model.py from Brettapps/Trifecta-Lab: direct link, hf CLI and curl.
- Browser
- Download file 4.53 kB
-
https://huggingface.co/spaces/Brettapps/Trifecta-Lab/resolve/main/src/prediction_model.py
- Command line
-
hf download hf://spaces/Brettapps/Trifecta-Lab/src/prediction_model.py
-
curl -L -o prediction_model.py https://huggingface.co/spaces/Brettapps/Trifecta-Lab/resolve/main/src/prediction_model.py
4.53 kB
| """Lightweight open-source trifecta prediction model.""" | |
| from __future__ import annotations | |
| from dataclasses import dataclass, field | |
| from typing import Any | |
| class Runner: | |
| number: int | |
| name: str | |
| jockey: str = "" | |
| trainer: str = "" | |
| weight: float | None = None | |
| barrier: int | None = None | |
| form: str = "" | |
| last20Starts: str = "" | |
| careerPrizeMoney: str = "$0" | |
| scratched: bool = False | |
| stats: dict[str, Any] = field(default_factory=dict) | |
| class Race: | |
| date: str | |
| track: str | |
| track_slug: str | |
| race_number: str | |
| race_name: str | |
| distance: str | |
| condition: str | |
| weather: str | |
| race_class: str | |
| start_time: str | |
| prize_money: str | |
| number_of_runners: int | |
| runners: list[Runner] = field(default_factory=list) | |
| class TrifectaPredictor: | |
| """Open-source trifecta prediction model.""" | |
| def predict(self, race: Race) -> dict[str, Any]: | |
| runners = [r for r in race.runners if not r.scratched] | |
| if len(runners) < 3: | |
| return {"error": "Insufficient runners"} | |
| scored: list[dict[str, Any]] = [] | |
| for runner in runners: | |
| scored.append({ | |
| "number": runner.number, | |
| "name": runner.name, | |
| "score": self._score_runner(runner), | |
| "win_prob": self._win_probability(runner), | |
| "place_prob": self._place_probability(runner), | |
| }) | |
| scored.sort(key=lambda x: x["score"], reverse=True) | |
| top = scored[:3] | |
| primary = f"{top[0]['number']}-{top[1]['number']}-{top[2]['number']}" | |
| secondary = None | |
| value = None | |
| if len(scored) > 3: | |
| secondary = f"{top[0]['number']}-{top[2]['number']}-{scored[3]['number']}" | |
| outsiders = [s for s in scored[3:] if s["score"] > 30] | |
| if outsiders: | |
| value = f"{scored[1]['number']}-{top[0]['number']}-{outsiders[0]['number']}" | |
| else: | |
| value = f"{scored[1]['number']}-{top[0]['number']}-{top[2]['number']}" | |
| return { | |
| "date": race.date, | |
| "track": race.track, | |
| "race_number": race.race_number, | |
| "race_name": race.race_name, | |
| "primary": primary, | |
| "secondary": secondary, | |
| "value": value, | |
| "top3": top, | |
| "confidence": "MEDIUM", | |
| } | |
| def _score_runner(self, runner: Runner) -> float: | |
| score = 0.0 | |
| form = str(runner.form or runner.last20Starts or "") | |
| recent = form[-5:] if len(form) > 5 else form | |
| score += min((recent.count("1") * 8 + recent.count("2") * 4 + recent.count("3") * 4), 25) | |
| overall = runner.stats.get("overall", {}) | |
| starts = overall.get("starts", 0) or 0 | |
| win_pct = overall.get("winPercent", 0) or 0 | |
| place_pct = overall.get("placePercent", 0) or 0 | |
| score += win_pct * 20 | |
| score += place_pct * 10 | |
| track_stats = runner.stats.get("track", {}) | |
| track_starts = track_stats.get("starts", 0) or 0 | |
| track_places = track_stats.get("places", 0) or 0 | |
| score += min((track_places / max(track_starts, 1)) * 10, 10) | |
| dist_stats = runner.stats.get("distance", {}) | |
| dist_starts = dist_stats.get("starts", 0) or 0 | |
| dist_places = dist_stats.get("places", 0) or 0 | |
| score += min((dist_places / max(dist_starts, 1)) * 8, 8) | |
| cond_stats = runner.stats.get("conditions", {}) | |
| for key, data in cond_stats.items(): | |
| c_starts = data.get("starts", 0) or 0 | |
| c_places = data.get("places", 0) or 0 | |
| score += min((c_places / max(c_starts, 1)) * 8, 8) | |
| try: | |
| barrier = int(runner.barrier) if runner.barrier else 5 | |
| score += max(0, 5 - abs(barrier - 5)) | |
| except Exception: | |
| score += 3 | |
| try: | |
| prize = float(str(runner.careerPrizeMoney).replace("$", "").replace(",", "")) | |
| score += min(prize / 20000, 5) | |
| except Exception: | |
| pass | |
| return min(round(score, 1), 100) | |
| def _win_probability(self, runner: Runner) -> float: | |
| overall = runner.stats.get("overall", {}) | |
| win_pct = overall.get("winPercent", 0) or 0 | |
| return min(max(round(win_pct * 100, 1), 0), 100) | |
| def _place_probability(self, runner: Runner) -> float: | |
| overall = runner.stats.get("overall", {}) | |
| place_pct = overall.get("placePercent", 0) or 0 | |
| return min(max(round(place_pct * 100, 1), 0), 100) | |