Cross-venue prediction market arbitrage tooling for executable YES order flow between Predict.fun and Polymarket.
Prediction market arbitrage tooling focused on Predict.fun and Polymarket. The repository contains:
- Go binaries for fetching market data, building cross-market pairs, serving a dashboard, and scanning arb opportunities
- TypeScript utilities for local data fetching and a lightweight dev server
- Static dashboard assets under
site/
The live implementation in this repository currently supports:
| Platform | Data source | Status |
|---|---|---|
| Predict.fun | GraphQL + WebSocket orderbook snapshot flow | Implemented |
| Polymarket | Gamma API + CLOB orderbook proxy | Implemented |
Other platforms mentioned in older notes are not implemented in the current codebase.
cmd/
arb_scan/ # CLI scanner for profitable YES cross opportunities
fetch_open_markets/ # Fetch Predict.fun markets
fetch_all_markets/ # Combine Predict.fun + Polymarket and build pairs
server/ # Go HTTP dashboard server
ws_debug/ # Debug utility for raw websocket streams
internal/
config/ # CLI/config parsing helpers
market/ # Shared market and payload types
matcher/ # Pairing and text-matching logic
polymarket/ # Polymarket client + normalization
predict/ # Predict.fun client + normalization
server/ # Dashboard HTTP/SSE server
worker/ # Bounded concurrency helpers
site/
data/ # Generated JSON payloads
index.html # Static dashboard
*.ts # TypeScript fetch/server utilities
The scanner looks for executable YES cross opportunities:
- buy YES on venue A at the best ask
- sell YES on venue B at the best bid
- subtract taker fees
- reject mid-price-only inputs that are not actually executable
This avoids double-counting complement-style math and keeps the output aligned with actionable orderbook prices.
make buildThis produces:
bin/fetch_open_marketsbin/fetch_all_marketsbin/serverbin/arb
./bin/fetch_all_marketsQuick smoke run with bounded live data:
POLYMARKET_MAX_MARKETS=1 ./bin/fetch_all_markets \
--max-markets 1 \
--skip-orderbook \
--skip-holders \
--skip-comments \
--skip-timeseriesGenerated files land under site/data/:
predict_markets_full.jsonpredict_markets_view.jsonmarkets_full.jsonmarkets_view.jsonmarkets_pairs.json
./bin/arbOr pass a custom pairs file:
./bin/arb ./site/data/markets_pairs.json./bin/serverDefault server URL:
http://localhost:8050
The TypeScript files are useful for local development and debugging.
Install dependencies:
npm ciRun the dev server:
npm run devCompile TypeScript output:
npm run buildRun type-only verification:
npm run typecheckRun the TypeScript normalization tests:
npm run test:tsnpm run build emits compiled files into dist/, which is intentionally ignored by Git.
Go tests:
go test ./...Go build:
go build ./...TypeScript check:
npm ci
npm run checkUnified local check:
make checkBounded live smoke:
make smoke-liveScanner:
ARB_MIN_NET_BPSARB_MIN_FILL_RATIO
Dashboard / refresh loop:
PORTREFRESH_INTERVAL_MSSSE_PING_MSAUTO_REFRESHFETCH_CONCURRENCYCATEGORY_CONCURRENCY
Polymarket orderbook proxy:
POLYMARKET_CLOB_URLPOLYMARKET_ORDERBOOK_TTL_MSPOLYMARKET_ORDERBOOK_CONCURRENCYPOLYMARKET_ORDERBOOK_LEVELSPOLYMARKET_ORDERBOOK_MAX_TOKENSPOLYMARKET_MAX_MARKETS
fetch_all_markets --max-markets N now limits Predict.fun input and, unless POLYMARKET_MAX_MARKETS is explicitly set, applies the same cap to the Polymarket side for smoke tests and small-batch runs.
- The Go server reads
site/data/markets_pairs.jsonby default. site/data/*.jsonis runtime data, not a source of truth.- If the cached JSON is malformed, the server now rejects it instead of silently serving invalid metadata.