Skip to main content

๐Ÿ“ˆ Prediction markets with sportsdataverse-py

This page is the executed notebook 18_prediction_markets_intro.ipynb: download it to run it yourself.

Sportsbooks quote odds; prediction markets quote prices. A YES contract that pays $1 and trades at $0.62 is a 62% implied probability, set by whoever is trading it. sportsdataverse.odds wraps the public market-data APIs of the two biggest venues:

VenueFunctionsAuth
Kalshikalshi_*none for market data
Polymarketpolymarket_gamma_* (catalog), polymarket_clob_* (order book)none

Sportsbook odds from The Odds API live in 12_odds_intro. This notebook only reads public prices; it places no orders.

๐Ÿงฐ The toolboxโ€‹

FunctionWhat it gives you
kalshi_exchange_statusIs the exchange open for trading?
kalshi_series_listMarket families (series) in a category
kalshi_eventsEvents, filterable by series and status
kalshi_marketsMarkets of an event, with bid / ask / last price
kalshi_orderbookOrder book of one market
kalshi_tradesPublic trades
polymarket_gamma_eventsEvents, filterable by tag (sports, ...)
polymarket_gamma_marketsMarkets with outcomes, prices and volume
polymarket_clob_midpointMidpoint of one outcome token's book

๐Ÿ”Œ Setupโ€‹

pip install sportsdataverse

The Kalshi and Polymarket helpers take their paging arguments (limit, offset) as strings, the way the APIs document them.

import json

import matplotlib.pyplot as plt
import polars as pl

from sportsdataverse import odds

pl.Config.set_tbl_rows(10)
pl.Config.set_fmt_str_lengths(60)
polars.config.Config

Provider feeds are seasonal and occasionally rate-limited, so a tiny safe() helper runs each call defensively: you get the frame when the feed is up, and a one-line skip when it isn't, naming whether the answer was nothing there (NoDataError) or the fetch failed (AssetFetchError). ๐Ÿ›Ÿ

from sportsdataverse.errors import AssetFetchError, NoDataError

def safe(label, thunk):
try:
out = thunk()
print(f"โœ… {label}")
return out
except (NoDataError, AssetFetchError) as e:
print(f"\u23ed\ufe0f {label}: {type(e).__name__}: {e}")
return None

๐Ÿฆ Kalshiโ€‹

Kalshi groups markets as series (a market family, such as single NFL games), events (one game) and markets (one contract). Start with the exchange status and the sports series.

safe("Kalshi status", odds.kalshi_exchange_status)
โœ… Kalshi status





shape: (1, 4)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ exchange_active โ”† exchange_index_statuses โ”† intra_exchange_transfers_acti โ”† trading_active โ”‚
โ”‚ --- โ”† --- โ”† ve โ”† --- โ”‚
โ”‚ bool โ”† str โ”† --- โ”† bool โ”‚
โ”‚ โ”† โ”† bool โ”† โ”‚
โ•žโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ก
โ”‚ true โ”† [{"description": "Default", โ”† true โ”† true โ”‚
โ”‚ โ”† "exchange_active": true, โ”† โ”† โ”‚
โ”‚ โ”† "exchanโ€ฆ โ”† โ”† โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
series = safe("Kalshi sports series", lambda: odds.kalshi_series_list(category="Sports"))
(series.filter(pl.col("title").str.contains("(?i)NFL|NBA|NHL|college"))
.select("ticker", "title", "frequency").head(10)
if series is not None and series.height else "series unavailable")
โœ… Kalshi sports series





shape: (10, 3)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ticker โ”† title โ”† frequency โ”‚
โ”‚ --- โ”† --- โ”† --- โ”‚
โ”‚ str โ”† str โ”† str โ”‚
โ•žโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ก
โ”‚ KXNCAAMBBIG12REG โ”† Men's College Basketball Big 12 Regular Season Champion โ”† custom โ”‚
โ”‚ KXNFLDRAFTSELECT โ”† NFL Draft Exact Selection โ”† custom โ”‚
โ”‚ KXNCAAF1HFT โ”† College Football 1st Half Fulltime Result โ”† custom โ”‚
โ”‚ KXNCAAWBTOTAL โ”† Women's College Basketball Total Points โ”† custom โ”‚
โ”‚ KXNHLNORRIS โ”† NHL James Norris Memorial Trophy โ”† annual โ”‚
โ”‚ KXNCAAFFIRSTTDTEAM โ”† College Football First Team to Score a Touchdown โ”† custom โ”‚
โ”‚ KXNFLCAREERRSHTDS โ”† NFL Career Rushing Touchdowns โ”† custom โ”‚
โ”‚ KXNFLCAREERPASSYDS โ”† NFL Career Passing Yards โ”† custom โ”‚
โ”‚ KXNBA2QTOTAL โ”† NBA 2nd Quarter Total โ”† custom โ”‚
โ”‚ KXNBAGAMES โ”† NBA games โ”† daily โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

One NFL game market and its order book ๐Ÿ“Šโ€‹

KXNFLGAME is the single-game NFL series. Take its first open event, list the markets (kalshi_markets), then read one market's book (kalshi_orderbook). The book holds bids only, on both sides; prices and sizes arrive as strings, so we cast them before plotting.

events = safe("Kalshi NFL events", lambda: odds.kalshi_events(limit="10", status="open", series_ticker="KXNFLGAME"))
event = events["event_ticker"][0] if events is not None and events.height else None
mkts = safe(f"markets {event}", lambda: odds.kalshi_markets(event_ticker=event)) if event else None
cols = ["ticker", "title", "yes_bid_dollars", "yes_ask_dollars", "last_price_dollars", "volume_fp"]
mkts.select([c for c in cols if c in mkts.columns]) if mkts is not None and mkts.height else "no open NFL events"
โœ… Kalshi NFL events
โœ… markets KXNFLGAME-26OCT26DALPHI





shape: (2, 6)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ticker โ”† title โ”† yes_bid_dollar โ”† yes_ask_dollar โ”† last_price_dol โ”† volume_fp โ”‚
โ”‚ --- โ”† --- โ”† s โ”† s โ”† lars โ”† --- โ”‚
โ”‚ str โ”† str โ”† --- โ”† --- โ”† --- โ”† str โ”‚
โ”‚ โ”† โ”† str โ”† str โ”† str โ”† โ”‚
โ•žโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ก
โ”‚ KXNFLGAME-26OCT โ”† Philadelphia โ”† 0.3100 โ”† 0.3200 โ”† 0.3200 โ”† 721704.90 โ”‚
โ”‚ 26DALPHI-PHI โ”† wins โ”† โ”† โ”† โ”† โ”‚
โ”‚ KXNFLGAME-26OCT โ”† Dallas wins โ”† 0.6700 โ”† 0.6800 โ”† 0.6800 โ”† 556008.54 โ”‚
โ”‚ 26DALPHI-DAL โ”† โ”† โ”† โ”† โ”† โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
ticker = mkts["ticker"][0] if mkts is not None and mkts.height else None
book = safe(f"order book {ticker}", lambda: odds.kalshi_orderbook(ticker)) if ticker else None
if book is not None and book.height:
fig, ax = plt.subplots(figsize=(8, 4))
book = book.with_columns(pl.col("price", "size").cast(pl.Float64))
yes = book.filter(pl.col("side") == "yes")
# A NO bid at p is an offer to sell YES at 1 - p, so both sides fit on one YES-price axis.
no = book.filter(pl.col("side") == "no").with_columns(price=1 - pl.col("price"))
ax.bar(yes["price"], yes["size"], width=0.008, color="#2ca02c", alpha=0.7, label="YES bids")
ax.bar(no["price"], no["size"], width=0.008, color="#d62728", alpha=0.7, label="YES offers (NO bids)")
ax.set_yscale("log")
ax.set_xlabel("YES price in dollars (= implied probability)")
ax.set_ylabel("contracts (log scale)")
ax.set_title(f"Kalshi order book: {ticker}")
ax.legend()
plt.show()
else:
print("no order book to draw")
โœ… order book KXNFLGAME-26OCT26DALPHI-PHI

png

trades = safe(f"trades {ticker}", lambda: odds.kalshi_trades(limit="20", ticker=ticker)) if ticker else None
trades.head() if trades is not None and trades.height else "no recent trades"
โœ… trades KXNFLGAME-26OCT26DALPHI-PHI





shape: (5, 10)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ count_fp โ”† created_t โ”† is_block_ โ”† no_price_ โ”† โ€ฆ โ”† taker_sid โ”† ticker โ”† trade_id โ”† yes_price โ”‚
โ”‚ --- โ”† ime โ”† trade โ”† dollars โ”† โ”† e โ”† --- โ”† --- โ”† _dollars โ”‚
โ”‚ str โ”† --- โ”† --- โ”† --- โ”† โ”† --- โ”† str โ”† str โ”† --- โ”‚
โ”‚ โ”† str โ”† bool โ”† str โ”† โ”† str โ”† โ”† โ”† str โ”‚
โ•žโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ก
โ”‚ 14.91 โ”† 2026-10-2 โ”† false โ”† 0.6800 โ”† โ€ฆ โ”† yes โ”† KXNFLGAME โ”† 0724ab06- โ”† 0.3200 โ”‚
โ”‚ โ”† 6T08:23:2 โ”† โ”† โ”† โ”† โ”† -26OCT26D โ”† 0b29-83ff โ”† โ”‚
โ”‚ โ”† 1.219164Z โ”† โ”† โ”† โ”† โ”† ALPHI-PHI โ”† -908c-143 โ”† โ”‚
โ”‚ โ”† โ”† โ”† โ”† โ”† โ”† โ”† 3676aadb8 โ”† โ”‚
โ”‚ 13.60 โ”† 2026-10-2 โ”† false โ”† 0.6800 โ”† โ€ฆ โ”† yes โ”† KXNFLGAME โ”† 0724ab04- โ”† 0.3200 โ”‚
โ”‚ โ”† 6T08:22:5 โ”† โ”† โ”† โ”† โ”† -26OCT26D โ”† 9589-bb73 โ”† โ”‚
โ”‚ โ”† 6.209996Z โ”† โ”† โ”† โ”† โ”† ALPHI-PHI โ”† -fac8-d87 โ”† โ”‚
โ”‚ โ”† โ”† โ”† โ”† โ”† โ”† โ”† 33fc7cacc โ”† โ”‚
โ”‚ 74.57 โ”† 2026-10-2 โ”† false โ”† 0.6800 โ”† โ€ฆ โ”† yes โ”† KXNFLGAME โ”† 0724ab0a- โ”† 0.3200 โ”‚
โ”‚ โ”† 6T08:22:1 โ”† โ”† โ”† โ”† โ”† -26OCT26D โ”† d069-a4cb โ”† โ”‚
โ”‚ โ”† 9.463875Z โ”† โ”† โ”† โ”† โ”† ALPHI-PHI โ”† -0c9f-96b โ”† โ”‚
โ”‚ โ”† โ”† โ”† โ”† โ”† โ”† โ”† 2df68711c โ”† โ”‚
โ”‚ 5.00 โ”† 2026-10-2 โ”† false โ”† 0.6800 โ”† โ€ฆ โ”† yes โ”† KXNFLGAME โ”† 0724ab09- โ”† 0.3200 โ”‚
โ”‚ โ”† 6T08:22:0 โ”† โ”† โ”† โ”† โ”† -26OCT26D โ”† 97e9-8d3b โ”† โ”‚
โ”‚ โ”† 7.376608Z โ”† โ”† โ”† โ”† โ”† ALPHI-PHI โ”† -0d60-b19 โ”† โ”‚
โ”‚ โ”† โ”† โ”† โ”† โ”† โ”† โ”† abf761e48 โ”† โ”‚
โ”‚ 5.62 โ”† 2026-10-2 โ”† false โ”† 0.6900 โ”† โ€ฆ โ”† no โ”† KXNFLGAME โ”† 0724a83f- โ”† 0.3100 โ”‚
โ”‚ โ”† 6T08:22:0 โ”† โ”† โ”† โ”† โ”† -26OCT26D โ”† ed09-b1ce โ”† โ”‚
โ”‚ โ”† 5.713199Z โ”† โ”† โ”† โ”† โ”† ALPHI-PHI โ”† -5a65-27c โ”† โ”‚
โ”‚ โ”† โ”† โ”† โ”† โ”† โ”† โ”† 3abf8693f โ”† โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ”ฎ Polymarketโ€‹

Polymarket has two public hosts: Gamma (the catalog of events and markets) and the CLOB (the order books). polymarket_gamma_events with tag_slug="sports" lists the open events Polymarket tags as sports (its own tagging, so an odd non-sport event can slip in); volume is in USDC.

pev = safe("Polymarket sports events",
lambda: odds.polymarket_gamma_events(limit="20", closed="false", tag_slug="sports"))
if pev is not None and pev.height:
top = pev.with_columns(pl.col("volume").cast(pl.Float64, strict=False)).sort("volume").tail(12)
fig, ax = plt.subplots(figsize=(8, 6))
ax.barh([t[:45] for t in top["title"]], top["volume"] / 1e6, color="#3b5bdb")
ax.set_xlabel("volume (million USDC)")
ax.set_title("Open Polymarket sports events by volume")
plt.tight_layout()
plt.show()
else:
print("no Polymarket sports events")
โœ… Polymarket sports events

png

From catalog to order book ๐Ÿ”—โ€‹

A Gamma market lists its outcomes, their prices, and the CLOB token id of each outcome (clob_token_ids, a JSON list as text). That token id is what polymarket_clob_midpoint takes.

pmk = safe("Polymarket markets by volume",
lambda: odds.polymarket_gamma_markets(limit="5", closed="false", order="volume", ascending="false"))
pmk.select("question", "outcomes", "outcome_prices", "volume") if pmk is not None and pmk.height else None
โœ… Polymarket markets by volume





shape: (5, 4)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ question โ”† outcomes โ”† outcome_prices โ”† volume โ”‚
โ”‚ --- โ”† --- โ”† --- โ”† --- โ”‚
โ”‚ str โ”† str โ”† str โ”† str โ”‚
โ•žโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ชโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ก
โ”‚ Will York City FC win on โ”† ["Yes", "No"] โ”† ["0.295", "0.705"] โ”† 99.997143 โ”‚
โ”‚ 2026-10-03? โ”† โ”† โ”† โ”‚
โ”‚ Sheffield Wednesday FC vs. โ”† ["Over", "Under"] โ”† ["0.17", "0.83"] โ”† 99.996667 โ”‚
โ”‚ Cambridge United FC: O/U 4.5 โ”† โ”† โ”† โ”‚
โ”‚ Al Ahli Saudi FC vs. Al Gharafa โ”† ["Over", "Under"] โ”† ["0.17", "0.83"] โ”† 99.996667 โ”‚
โ”‚ SC: O/U 5.5 โ”† โ”† โ”† โ”‚
โ”‚ Spread: Como 1907 (-1.5) โ”† ["Como 1907", "Torino FC"] โ”† ["0.315", "0.685"] โ”† 99.992259 โ”‚
โ”‚ Rot-Weiss Essen vs. RB Leipzig: โ”† ["Over", "Under"] โ”† ["0.17", "0.83"] โ”† 99.991177 โ”‚
โ”‚ O/U 5.5 โ”† โ”† โ”† โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
if pmk is not None and pmk.height and pmk["clob_token_ids"][0]:
token = json.loads(pmk["clob_token_ids"][0])[0]
mid = safe("CLOB midpoint", lambda: odds.polymarket_clob_midpoint(token))
out = mid
else:
out = "no token id to look up"
out
โœ… CLOB midpoint





shape: (1, 1)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ mid โ”‚
โ”‚ --- โ”‚
โ”‚ str โ”‚
โ•žโ•โ•โ•โ•โ•โ•โ•โ•ก
โ”‚ 0.295 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐ŸŽ‰ Where to nextโ€‹

  • Compare a market's price with a sportsbook's no-vig probability from 12_odds_intro.
  • kalshi_series / kalshi_event / kalshi_market fetch one record by ticker; polymarket_clob_book / polymarket_clob_price read the full book or one side.
  • Full reference: the Odds pages in the sidebar.