Real-time Polymarket soccer market monitor with goal-event correlation. Polls all tradeable markets for a selected match (1X2, Over/Under, BTTS, Handicap), pulls live scores from football-data.org, detects goals, and measures how quickly each market's price updates relative to the score change. Flags stale orders that haven't moved within expected thresholds — potential price-delay arbitrage opportunities.
Built to answer a research question: when a goal happens in a live match, how long does it take for each Polymarket market to fully reprice, and which ones lag enough to be exploitable?
- Match discovery — enumerates all active soccer matches with tradeable markets, ranked by total liquidity
- Interactive selection — pick any match from the list at startup
- Multi-market polling — tracks 1X2, O/U 0.5–4.5, BTTS, and Spread/Handicap simultaneously
- Live score correlation — football-data.org integration with automatic match-name fuzzy matching
- Goal detection — notices score changes and captures pre-goal price snapshots across all markets
- Delay measurement — for each market, records how many seconds elapsed before price moved ≥2¢ after a goal
- Opportunity flagging — heuristic "expected probability" model; surfaces markets where current price diverges ≥5¢ from expected post-goal fair value within 2 minutes of the event
- Per-match CSV logs — three files per match:
prices.csv,events.csv,delays.csv - Rich live dashboard — auto-refreshing terminal UI with color-coded delta indicators
match-monitor/
├── src/
│ ├── polymarket.py # Gamma API discovery + CLOB orderbook polling
│ ├── scores.py # football-data.org client + fuzzy match matching
│ ├── analyzer.py # Goal tracker, delay calc, opportunity heuristics
│ ├── monitor.py # Main event loop (poll → detect → log → render)
│ ├── display.py # Rich Layout with 4 panels
│ └── logger.py # CSV writers (prices / events / delays)
├── docs/
│ ├── dashboard.png
│ └── make_screenshot.py
├── main.py # CLI entry + interactive match picker
├── requirements.txt
└── .env.example
- Python 3.10+ (asyncio, dataclasses, type hints)
- httpx — async HTTP client
- Rich — live terminal dashboard
- python-dotenv — env management
git clone https://github.com/cengizmandros/match-monitor.git
cd match-monitor
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
# edit .env, add FOOTBALL_DATA_KEYGet a free football-data.org API key (10 req/min): https://www.football-data.org/client/register
# Interactive: pick a match from the list, monitor it live
python main.py
# Just list active matches and exit
python main.py --list-only
# Headless (no dashboard, just logs)
python main.py --no-dashboardExample startup flow:
Active Soccer Matches (26 found)
# │ Match │ Markets │ Types │ Total Liq $
1 │ Manchester City FC vs. Crystal Palace FC │ 6 │ 1X2, Spread 1.5, 2.5 │ 28,497
2 │ Al Taawoun Saudi Club vs. Al Kholood Saudi │ 12 │ 1X2, BTTS, O/U 1.5-4.5 │ 52,019
3 │ Wellington Phoenix FC vs. Western Sydney │ 3 │ 1X2 │ 501,135
...
Which match to monitor? (1-26) > 1
Selected: Manchester City FC vs. Crystal Palace FC
Score found: Manchester City FC 0-0 Crystal Palace FC (IN_PLAY)
Starting live dashboard in 3 seconds...
Each match creates a folder under logs/<match-slug>/ with three files.
prices.csv — one row per market per poll:
timestamp, iso_time, match, score, minute, market_type, outcome, question, yes_price, best_bid, best_ask, spread, liquidity
events.csv — score changes and key events:
timestamp, iso_time, match, event_type, old_score, new_score, detail
delays.csv — measured price-update delays after each goal:
timestamp, iso_time, match, score_before, score_after, market_type, outcome, price_before, price_after, delta, delay_seconds
Can be loaded into pandas for post-match analysis — "which market types update slowest?", "what's the average delay across goals?", etc.
src/analyzer.py::expected_prob_after_goal() applies simple rules:
- Over/Under: if total goals now exceed threshold → fair value pushes toward 0.99 (Over) or 0.01 (Under). One-away jumps by ~20¢.
- BTTS: once both teams have scored → resolves YES (0.99).
- 1X2: the scoring team gets +~15¢, draw and losing team lose ~8–10¢.
These are crude — real fair values depend on minute, strength, xG, and many other factors — but good enough to flag "this market clearly hasn't moved yet" cases. The opportunity panel surfaces anything with a ≥5¢ gap between heuristic expected and current price, within 2 minutes of the goal.
- Free-tier score API latency: football-data.org updates live scores every ~30–60s, so we're already behind someone watching the actual stream. A paid feed (Sportradar, StatsBomb) would cut this to <1s.
- Polling, not WebSocket: every refresh is a full orderbook fetch. WebSocket subscription (via py-clob-client) is a worthwhile upgrade.
- Heuristic opportunity model: doesn't account for match minute, team strength, or in-game momentum. A trained model on historical Polymarket data would be far better.
- No auto-trading: by design. This is monitoring/research infrastructure, not an execution engine.
The interesting question isn't "can I profit from this" — with free-tier inputs, probably not. The question is what does the data tell us about Polymarket's price-update latency distribution? Run the monitor across many matches, aggregate the delays.csv files, and you get a real empirical measurement of how fast various market types respond to real-world events. That's a publishable observation.
MIT