The 2026 FIFA World Cup has turned Polymarket's tournament winner market into the largest prediction market ever, with nearly $2 billion in volume across 48 team outcomes. Prices update constantly (Spain at ~16.95¢, France ~16.05¢), but the web UI shows one market at a time. This guide shows how to build a full Python dashboard for live odds, fair probabilities, order book depth, historical charts, and a smart arbitrage scanner.
You need intermediate Python knowledge. No account, API key, or wallet required for read-only data.
The Two Key Polymarket APIs
-
Gamma API (
https://gamma-api.polymarket.com): For discovery — events, markets, current prices, volumes, and token IDs. -
CLOB API (
https://clob.polymarket.com): For trading details — full order books, historical prices, and executable fills.
Token IDs link them: Gamma provides clobTokenIds; feed those to CLOB endpoints. Prices are in [0, 1] and directly equal implied probabilities.
Project Setup
mkdir polymarket-worldcup-dashboard && cd polymarket-worldcup-dashboard
pip install requests pandas plotly streamlit websockets
Step 1: Fetch All 48 Markets (Gamma API)
Hardcode the World Cup event ID (30615) and pull markets:
import requests
import json
def get_worldcup_markets():
url = "https://gamma-api.polymarket.com/markets"
params = {"event_id": 30615, "active": True, "limit": 100}
resp = requests.get(url, params=params).json()
markets = []
for m in resp.get("data", []):
try:
prices = json.loads(m["outcomePrices"])
token_ids = json.loads(m["clobTokenIds"])
markets.append({
"team": m["groupItemTitle"], # Clean name like "Spain"
"price": float(prices[0]), # YES price
"volume": m["volume"],
"token_id": token_ids[0] # YES token
})
except:
continue
return sorted(markets, key=lambda x: x["price"], reverse=True)
# Example output: top teams with real live prices
One call returns everything in <1s.
Step 2: Normalize Probabilities (Remove Overround)
Raw prices sum >1 due to liquidity premium (~4.3% here). Normalize for fair probs:
def normalize_probabilities(markets):
total = sum(m["price"] for m in markets)
for m in markets:
m["raw_price"] = m["price"]
m["fair_prob"] = m["price"] / total
return markets
Display both: raw for trading, fair for modeling/comparisons.
Step 3: Order Book Depth & Executable Prices (CLOB API)
Screen price ≠ fill price for size. Compute VWAP slippage:
def get_order_book(token_id):
url = f"https://clob.polymarket.com/book?token_id={token_id}&side=sell" # or buy
return requests.get(url).json()
def calculate_executable_price(book, usd_size):
# Walk asks/bids, accumulate volume-weighted average
remaining = usd_size
total_cost = 0
for level in book["asks"]: # example for buy
price = float(level["price"])
size = float(level["size"]) * price # USD depth
if remaining <= size:
total_cost += remaining * price
break
total_cost += size
remaining -= size
return total_cost / usd_size if total_cost else None
For Spain, $100k often fills near top-of-book with minimal slippage; $250k jumps significantly. Add this to your dashboard for real trading insight.
Step 4: Historical Probability Charts (CLOB)
Pull time series and visualize rotations with Plotly:
def get_price_history(token_id, interval="1m", fidelity=360):
url = f"https://clob.polymarket.com/prices-history?token_id={token_id}&interval={interval}&fidelity={fidelity}"
data = requests.get(url).json()
# Convert to pandas DF and plot
import plotly.express as px
# fig = px.line(...) with % scaling and unified hover
Overlay top contenders to spot shifts (e.g., Spain overtaking France).
Step 5: Arbitrage Scanner (With Depth Reality Check)
For mutually exclusive outcomes:
- Long arb: Sum best asks < 1.00 → guaranteed profit.
- Short arb: Sum best bids > 1.00 → collect premium.
def check_arb(markets):
best_asks = sum(m["best_ask"] for m in markets) # from books
best_bids = sum(m["best_bid"] for m in markets)
return {"long_arb": best_asks < 1.0, "short_arb": best_bids > 1.0, "profit": best_bids - 1.0}
Critical: Always validate with executable depth. Many "arbs" vanish on thin longshots (no bids at all for teams like Curaçao). Show capacity per leg.
Step 6: Real-Time WebSocket Feed
For sub-second updates (no polling):
import websocket # or websockets lib
ws_url = "wss://ws-subscriptions-clob.polymarket.com/ws/market"
# Subscribe with {"assets_ids": [token_ids...]}
# Handle 'book' snapshot + 'price_change' patches
Re-run scanner on every patch.
Step 7: Streamlit Dashboard
Wrap in app.py:
import streamlit as st
@st.cache_data(ttl=30)
def load_data(): ...
st.title("Polymarket World Cup 2026 Dashboard")
# Table with prices + fair probs
# Multi-select for charts
# Arb status with warnings
# Book depth viewer
Refresh every 30s; swap to WebSocket + session_state for live.
Extensions
- Cross-compare with Kalshi.
- Telegram alerts on big moves or depth drops.
- Add group/fixture markets (same code, different event IDs).
This dashboard gives superior visibility: probabilities, depth, history, and honest signals in one place — all from public endpoints in a few hundred lines of Python.
The $2B market is a goldmine of crowd wisdom. A solid dashboard beats the official UI for serious analysis or trading prep. Tournament runs through July 2026 — plenty of time to build and iterate.
If you have more questions, please feel free to contact me at any time: https://t.me/FatherSon97

Top comments (0)