2026_MLB_Model / visualization /edge_strip.py
Syntrex's picture
Update visualization/edge_strip.py
b603c74 verified
raw
history blame
2.8 kB
from __future__ import annotations
from typing import Any
import streamlit.components.v1 as components
def _fmt_odds(value: Any) -> str:
if value is None:
return "—"
try:
iv = int(value)
return f"+{iv}" if iv > 0 else str(iv)
except Exception:
return str(value)
def _fmt_edge(value: Any) -> str:
if value is None:
return ""
try:
edge = float(value)
except Exception:
return str(value)
pct = edge * 100
if pct >= 5:
color = "#22c55e"
elif pct >= 2:
color = "#84cc16"
elif pct >= 0:
color = "#eab308"
elif pct >= -3:
color = "#f97316"
else:
color = "#ef4444"
return f'<span style="color:{color};font-weight:800;">{pct:.1f}%</span>'
def render_upcoming_edge_strip(rows: list[dict[str, Any]]) -> None:
if not rows:
return
body_rows = []
for row in rows:
ev90 = row.get("ev90")
ev90_text = f" | EV90 {float(ev90):.1f}" if ev90 is not None else ""
body_rows.append(
f"""
<div class="grid-row">
<div>{row.get("slot", "")}: {row.get("batter_name", "")}{ev90_text}</div>
<div>{_fmt_odds(row.get("fair_hr_odds"))}</div>
<div>{_fmt_odds(row.get("book_hr_odds"))}</div>
<div>{_fmt_edge(row.get("hr_edge"))}</div>
</div>
"""
)
html = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<style>
body {{
margin: 0;
padding: 0;
background: transparent;
font-family: Arial, sans-serif;
}}
.edge-strip {{
margin-top: -4px;
margin-bottom: 14px;
background: rgba(255,255,255,0.03);
border: 1px solid rgba(148,163,184,0.14);
border-radius: 16px;
padding: 12px;
box-sizing: border-box;
color: #e2e8f0;
}}
.title {{
color: #94a3b8;
font-size: 12px;
font-weight: 700;
margin-bottom: 8px;
}}
.grid-header, .grid-row {{
display: grid;
grid-template-columns: 1.7fr 0.6fr 0.6fr 0.6fr;
gap: 8px;
}}
.grid-header {{
color: #94a3b8;
font-size: 11px;
font-weight: 700;
margin-bottom: 6px;
}}
.grid-row {{
color: #e2e8f0;
font-size: 13px;
font-weight: 700;
margin-bottom: 4px;
}}
</style>
</head>
<body>
<div class="edge-strip">
<div class="title">UPCOMING BATTER HR EDGES • EV90 SIM MODEL V3</div>
<div class="grid-header">
<div>BATTER</div>
<div>FAIR</div>
<div>BOOK</div>
<div>EDGE</div>
</div>
{''.join(body_rows)}
</div>
</body>
</html>
"""
height = 88 + (len(rows) * 30)
components.html(html, height=height, scrolling=False)