File size: 2,802 Bytes
6028d62
 
 
 
c0278e8
6028d62
 
c0278e8
 
 
6028d62
c0278e8
 
6028d62
c0278e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3ea96c1
 
 
c0278e8
 
 
3ea96c1
c0278e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3ea96c1
c0278e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b603c74
c0278e8
 
 
 
 
 
 
 
 
 
 
 
 
3ea96c1
c0278e8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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)