Guest User

Sports odds api snippet example

a guest
Aug 28th, 2025
32
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.51 KB | Sports | 0 0
  1. from dataclasses import dataclass
  2. from typing import Dict, List, Optional, Tuple
  3. import math
  4. import time
  5.  
  6. # --- Odds helpers ---
  7.  
  8. def american_to_decimal(a: int) -> float:
  9. return 1 + (a / 100.0) if a >= 100 else 1 + (100.0 / abs(a))
  10.  
  11. def implied_prob_from_decimal(d: float) -> float:
  12. return 1.0 / d
  13.  
  14. def remove_vig_proportional(book_probs: List[float]) -> List[float]:
  15. m = sum(book_probs)
  16. return [p / m for p in book_probs]
  17.  
  18. def decimal_from_prob(p: float) -> float:
  19. return 1.0 / p
  20.  
  21. def ev_percent(p_fair: float, dec_price: float) -> float:
  22. ev = (p_fair * (dec_price - 1.0)) - ((1.0 - p_fair) * 1.0)
  23. return ev * 100.0
  24.  
  25. # --- Domain model ---
  26.  
  27. @dataclass
  28. class Quote:
  29. book: str
  30. decimal: float # normalized to decimal internally
  31. updated_ts: float
  32.  
  33. @dataclass
  34. class OutcomeMarket:
  35. # outcome_key example: "HOME", "AWAY" or "HOME","DRAW","AWAY"
  36. outcome_key: str
  37. quotes: List[Quote] # same outcome across multiple books
  38.  
  39. @dataclass
  40. class EventMarket:
  41. event_id: str
  42. sport: str
  43. league: str
  44. start_ts: float
  45. market: str # "MONEYLINE_2WAY" or "MONEYLINE_3WAY"
  46. outcomes: Dict[str, OutcomeMarket] # e.g. {"HOME": OutcomeMarket(...), "AWAY": ...}
  47.  
  48. # --- Core scanning logic ---
  49.  
  50. def consensus_fair_prob(outcome: OutcomeMarket) -> Optional[float]:
  51. """
  52. Build consensus fair probability by:
  53. 1) converting each book price -> implied prob (booked),
  54. 2) removing vig across sibling outcomes at the book-level is ideal,
  55. 3) averaging no-vig probs across books.
  56. Here we approximate by averaging booked probs then normalizing across outcomes.
  57. """
  58. if not outcome.quotes:
  59. return None
  60. # Step 1: collect booked probs per book for this outcome
  61. booked_probs = [implied_prob_from_decimal(q.decimal) for q in outcome.quotes]
  62. # We'll return the mean booked prob for this outcome; normalization happens outside.
  63. return sum(booked_probs) / len(booked_probs)
  64.  
  65. def build_consensus_no_vig(event_market: EventMarket) -> Dict[str, float]:
  66. """
  67. Approximate consensus:
  68. - For each outcome, average booked probs across books.
  69. - Normalize across outcomes to remove vig (consensus-level).
  70. """
  71. avg_booked = {}
  72. for k, om in event_market.outcomes.items():
  73. p = consensus_fair_prob(om)
  74. if p is not None:
  75. avg_booked[k] = p
  76.  
  77. # Normalize to remove vig
  78. probs = list(avg_booked.values())
  79. keys = list(avg_booked.keys())
  80. if not probs:
  81. return {}
  82.  
  83. norm = remove_vig_proportional(probs)
  84. return {k: norm[i] for i, k in enumerate(keys)}
  85.  
  86. def best_quote(outcome: OutcomeMarket) -> Optional[Quote]:
  87. if not outcome.quotes:
  88. return None
  89. # For backing an outcome, the HIGHEST decimal price is best.
  90. return max(outcome.quotes, key=lambda q: q.decimal)
  91.  
  92. def scan_event(event_market: EventMarket, ev_threshold_percent: float = 1.0) -> List[Tuple[str, Quote, float, float]]:
  93. """
  94. Returns list of (outcome_key, best_quote, p_fair, ev%) that meet threshold.
  95. """
  96. results = []
  97. consensus = build_consensus_no_vig(event_market)
  98. for k, om in event_market.outcomes.items():
  99. if k not in consensus:
  100. continue
  101. p_fair = consensus[k]
  102. bq = best_quote(om)
  103. if not bq:
  104. continue
  105. evp = ev_percent(p_fair, bq.decimal)
  106. if evp >= ev_threshold_percent:
  107. results.append((k, bq, p_fair, evp))
  108. # Sort by EV descending
  109. return sorted(results, key=lambda t: t[3], reverse=True)
  110.  
  111. # --- Example usage with mocked data ---
  112.  
  113. def demo():
  114. now = time.time()
  115. market = EventMarket(
  116. event_id="NFL-12345",
  117. sport="football",
  118. league="NFL",
  119. start_ts=now + 3600,
  120. market="MONEYLINE_2WAY",
  121. outcomes={
  122. "HOME": OutcomeMarket("HOME", [
  123. Quote("BookA", 1.91, now), # -110
  124. Quote("BookB", 1.95, now),
  125. Quote("BookC", 1.88, now),
  126. ]),
  127. "AWAY": OutcomeMarket("AWAY", [
  128. Quote("BookA", 1.95, now),
  129. Quote("BookB", 1.91, now),
  130. Quote("BookC", 1.98, now), # best on AWAY
  131. ])
  132. }
  133. )
  134.  
  135. picks = scan_event(market, ev_threshold_percent=1.0)
  136. for outcome_key, quote, p_fair, evp in picks:
  137. print(f"{outcome_key} @ {quote.book} {quote.decimal:.2f} | fair p={p_fair:.3f} | EV={evp:.2f}%")
  138.  
  139. if __name__ == "__main__":
  140. demo()
  141.  
Advertisement
Add Comment
Please, Sign In to add comment