2026_MLB_Model / analytics /recommendation_rules.py
Syntrex's picture
Accuracy overhaul: pitcher resolution logging, baseline recalibration, vig fix, XGBoost blend
21151ce
raw
history blame
1.73 kB
from __future__ import annotations
from typing import Any
def apply_recommendation_rules(edge_row: dict[str, Any]) -> dict[str, Any]:
"""
Converts edge/confidence into recommendation tiers.
Focuses on HR market first for now.
"""
hr_edge = float(edge_row.get("hr_edge", 0.0) or 0.0)
hr_bet_ev = float(edge_row.get("hr_bet_ev", 0.0) or 0.0)
confidence = float(edge_row.get("confidence", 0.0) or 0.0)
slot = str(edge_row.get("slot", "") or "").strip().lower()
reason_tags = list(edge_row.get("reason_tags", []) or [])
slot_boost = 0.0
if slot == "on deck":
slot_boost = 0.012
reason_tags.append("On-deck priority")
elif slot == "in hole":
slot_boost = 0.006
reason_tags.append("In-hole visibility")
elif slot == "3 away":
slot_boost = 0.0
reason_tags.append("Farther from action window")
adjusted_edge = hr_edge + slot_boost
if confidence < 50:
tier = "pass"
reason_tags.append("Low confidence")
elif adjusted_edge >= 0.035 and hr_bet_ev > 0 and confidence >= 72:
tier = "bet"
reason_tags.append("Strong positive edge")
elif adjusted_edge >= 0.015 and confidence >= 58:
tier = "watch"
reason_tags.append("Watchlist edge")
else:
tier = "pass"
reason_tags.append("Below action threshold")
priority_score = adjusted_edge * 100 + (confidence / 10.0)
if slot == "on deck":
priority_score += 2.0
elif slot == "in hole":
priority_score += 1.0
return {
"recommendation_tier": tier,
"reason_tags": reason_tags[:6],
"adjusted_edge": adjusted_edge,
"priority_score": priority_score,
}