Spaces:
Sleeping
Sleeping
Update analytics/recommendation_engine.py
Browse files
analytics/recommendation_engine.py
CHANGED
|
@@ -29,7 +29,7 @@ def build_upcoming_hitter_recommendations(
|
|
| 29 |
) -> list[dict]:
|
| 30 |
"""
|
| 31 |
Decision-layer wrapper.
|
| 32 |
-
Uses simulated fair rows, then
|
| 33 |
1) opportunity adjustment
|
| 34 |
2) confidence
|
| 35 |
3) recommendation tier
|
|
@@ -45,7 +45,7 @@ def build_upcoming_hitter_recommendations(
|
|
| 45 |
recommendations: list[dict] = []
|
| 46 |
|
| 47 |
for row in rows:
|
| 48 |
-
slot = row.get("slot", "
|
| 49 |
lineup_distance = _lineup_distance_from_slot(slot)
|
| 50 |
|
| 51 |
opportunity = estimate_plate_appearance_probability(
|
|
@@ -55,7 +55,7 @@ def build_upcoming_hitter_recommendations(
|
|
| 55 |
|
| 56 |
expected_pa = float(opportunity.get("expected_pa", 1.0) or 1.0)
|
| 57 |
|
| 58 |
-
# Apply opportunity adjustment to
|
| 59 |
for prob_col in ["hit_prob", "hr_prob", "tb2p_prob"]:
|
| 60 |
if prob_col in row and row.get(prob_col) is not None:
|
| 61 |
try:
|
|
@@ -64,7 +64,36 @@ def build_upcoming_hitter_recommendations(
|
|
| 64 |
except Exception:
|
| 65 |
pass
|
| 66 |
|
| 67 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
row["lineup_distance"] = lineup_distance
|
| 69 |
row["pa_prob_this_inning"] = opportunity.get("pa_prob_this_inning")
|
| 70 |
row["pa_prob_next_two_innings"] = opportunity.get("pa_prob_next_two_innings")
|
|
|
|
| 29 |
) -> list[dict]:
|
| 30 |
"""
|
| 31 |
Decision-layer wrapper.
|
| 32 |
+
Uses simulated fair rows, then applies:
|
| 33 |
1) opportunity adjustment
|
| 34 |
2) confidence
|
| 35 |
3) recommendation tier
|
|
|
|
| 45 |
recommendations: list[dict] = []
|
| 46 |
|
| 47 |
for row in rows:
|
| 48 |
+
slot = row.get("slot", "Current")
|
| 49 |
lineup_distance = _lineup_distance_from_slot(slot)
|
| 50 |
|
| 51 |
opportunity = estimate_plate_appearance_probability(
|
|
|
|
| 55 |
|
| 56 |
expected_pa = float(opportunity.get("expected_pa", 1.0) or 1.0)
|
| 57 |
|
| 58 |
+
# Apply opportunity adjustment to the simulated probabilities
|
| 59 |
for prob_col in ["hit_prob", "hr_prob", "tb2p_prob"]:
|
| 60 |
if prob_col in row and row.get(prob_col) is not None:
|
| 61 |
try:
|
|
|
|
| 64 |
except Exception:
|
| 65 |
pass
|
| 66 |
|
| 67 |
+
# Recalculate fair odds and edges after probability adjustment
|
| 68 |
+
if row.get("hit_prob") is not None:
|
| 69 |
+
row["fair_hit_odds"] = probability_to_american(row["hit_prob"])
|
| 70 |
+
if row.get("hr_prob") is not None:
|
| 71 |
+
row["fair_hr_odds"] = probability_to_american(row["hr_prob"])
|
| 72 |
+
if row.get("tb2p_prob") is not None:
|
| 73 |
+
row["fair_tb2p_odds"] = probability_to_american(row["tb2p_prob"])
|
| 74 |
+
|
| 75 |
+
try:
|
| 76 |
+
book_hit_odds = float(row.get("book_hit_odds"))
|
| 77 |
+
row["hit_edge"] = compute_edge(row["hit_prob"], 100 / (book_hit_odds + 100))
|
| 78 |
+
row["hit_bet_ev"] = compute_bet_ev(row["hit_prob"], int(book_hit_odds))
|
| 79 |
+
except Exception:
|
| 80 |
+
pass
|
| 81 |
+
|
| 82 |
+
try:
|
| 83 |
+
book_hr_odds = float(row.get("book_hr_odds"))
|
| 84 |
+
row["hr_edge"] = compute_edge(row["hr_prob"], 100 / (book_hr_odds + 100))
|
| 85 |
+
row["hr_bet_ev"] = compute_bet_ev(row["hr_prob"], int(book_hr_odds))
|
| 86 |
+
except Exception:
|
| 87 |
+
pass
|
| 88 |
+
|
| 89 |
+
try:
|
| 90 |
+
book_tb2p_odds = float(row.get("book_tb2p_odds"))
|
| 91 |
+
row["tb2p_edge"] = compute_edge(row["tb2p_prob"], 100 / (book_tb2p_odds + 100))
|
| 92 |
+
row["tb2p_bet_ev"] = compute_bet_ev(row["tb2p_prob"], int(book_tb2p_odds))
|
| 93 |
+
except Exception:
|
| 94 |
+
pass
|
| 95 |
+
|
| 96 |
+
# Carry diagnostics forward
|
| 97 |
row["lineup_distance"] = lineup_distance
|
| 98 |
row["pa_prob_this_inning"] = opportunity.get("pa_prob_this_inning")
|
| 99 |
row["pa_prob_next_two_innings"] = opportunity.get("pa_prob_next_two_innings")
|