This project discovers overlapping markets between Polymarket and Kalshi, matches them conservatively, and logs potential arbitrage spreads to CSV. It is read-only by default and does not place any trades.
- Fetches market lists from Polymarket (Gamma API) and Kalshi (REST API).
- Matches events using structural rules (e.g., Fed decisions) plus tight generic matching.
- Subscribes to live quotes via WebSockets and polls Kalshi REST for fresh prices.
- Writes opportunities to
opportunities.csvand near-misses toskipped_opportunities.csv.
- Windows 10/11
- Python 3.11+ (tested with 3.14)
- Git (optional, for version control)
- WSL + Redis or Windows Redis
- Optional: Ollama (only used for future enrichment, not required for core scanning)
-
Create and activate a virtual environment
cd "C:\Users\andre\OneDrive\Documents\Arbitrage" python -m venv .venv .\.venv\Scripts\Activate.ps1
-
Install dependencies
python -m pip install -r requirements.txt
-
Create your
.env- Copy the template below into a new file named
.envat the repo root. - Fill in your Kalshi and Polymarket credentials.
- Copy the template below into a new file named
-
Start Redis (WSL example below)
-
Run the scanner
python -m src.main -
Stop with
Ctrl+C.
Create a .env file in the repo root:
# === KALSHI CONFIG ===
KALSHI_API_KEY_ID=your-kalshi-key-id
KALSHI_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----\n"
# demo or prod
KALSHI_ENVIRONMENT=demo
# === POLYMARKET CONFIG ===
POLY_PRIVATE_KEY=0x...
POLY_API_KEY=...
POLY_API_SECRET=...
POLY_API_PASSPHRASE=...
# === SYSTEM CONFIG ===
REDIS_HOST=localhost
REDIS_PORT=6379
DRY_RUN=True
MIN_SPREAD_PCT=1.5
MAX_POSITION_SIZE=10.0
# === DISCOVERY CONFIG ===
POLY_INCLUDE_POPULAR=True
POLY_SEARCH_TERMS=fed,cpi,inflation,jobs,bitcoin,ethereum,election,rates,oil,gold,treasury,interest,sp500,nasdaq,crypto,sports,nfl,nba,mlb,nhl,wnba,ufc,boxing,tennis,golf,soccer,champions league,europa,conference league,world cup,super bowl,stanley cup,world series,mls,premier league,la liga,bundesliga,serie a,march madness,college football,playoff,playoffs,championship,olympics,f1,nascar,pga,cricket,rugby,esports,league of legends,valorant,cs2,dota,overwatch,apex,rocket league,call of duty,fortnite,formula 1,grand slam,atp,wta,college basketball,ncaa,ncaab
KALSHI_SERIES_TICKERS=KXFEDDECISION,KXCPI,KXBTC,KXETH,KXJOBS,KXINX,KXSPY,KXOIL,KXGOLD,KXNFL,KXNBA,KXMLB,KXNHL,KXNCAAF,KXWNBA,KXUFC,KXBOXING,KXF1,KXNASCAR,KXSB,KXEPL,KXEPLGAME,KXEPLSPREAD,KXEPLTOTAL,KXPREMIERLEAGUE,KXUCL,KXUCLGAME,KXUCLSPREAD,KXUCLTOTAL,KXUEL,KXUELGAME,KXUELSPREAD,KXUELTOTAL,KXUECL,KXUECLGAME,KXUEFAGAME,KXSOCCERBTTS,KXSOCCERSPREAD,KXSOCCERTOTAL,KXATPGAME,KXATPMATCH,KXWTAGAME,KXWTAMATCH,KXPGA,KXTHEOPEN,KXGENESISINVITATIONAL,KXPGAMAJORWIN,KXMLSGAME,KXMLS,KXNFLSPREAD,KXNFLTOTAL,KXNFLTEAMTOTAL,KXNFLPLAYOFF,KXNFLWINS,KXNHLGAME,KXNHLSPREAD,KXNHLTOTAL,KXNHLWINS,KXNBA1QWINNER,KXNBA1HWINNER,KXNBA1HSPREAD,KXNBA1HTOTAL,KXNBA2HWINNER,KXNBA2HSPREAD,KXNBA2HTOTAL,KXNBA3QWINNER,KXNBA4QWINNER,KXMLBGAME,KXMLBSPREAD,KXMLBTOTAL,KXMLBPLAYOFFS,KXMARMAD,KXMARMADSEED,KXLEAGUE,KXLEAGUEWORLDS,KXLOLGAME,KXVALORANT,KXVALORANTGAME,KXCS2,KXDOTA2,KXOVERWATCH,KXR6,KXPUBG
# Optional filter to include only Kalshi markets containing specific words
KALSHI_SEARCH_FILTER=
# Optional: Ollama (not required)
OLLAMA_HOST=http://localhost:11434
OLLAMA_MODEL=qwen3:8bIf you already have Redis in WSL:
sudo service redis-server start
# or
sudo systemctl start redis-serverTo verify:
redis-cli ping
# PONGIf you are not using WSL, you can install Redis on Windows or run it in Docker. Just set REDIS_HOST and REDIS_PORT in .env accordingly.
Ollama isn’t required for the scanner to run. If you want it for future enrichment:
- Install Ollama from the official site.
- Start it and pull a model:
ollama run qwen3:8b
- Set
OLLAMA_HOSTandOLLAMA_MODELin.env.
- Structural matching: Fed decision markets are matched by month/year and action.
- Generic matching: Strict token overlap, sport/competition labels, and market type checks.
- Series hints: Kalshi series tickers supply sport/competition context to reduce mismatches.
The matcher is intentionally conservative to minimize false positives.
opportunities.csv— only rows where net spread exceedsMIN_SPREAD_PCT.skipped_opportunities.csv— near misses and reasons for rejection.
Both CSVs are ignored by git by default.
- 429 rate limits: The scanner automatically retries with backoff.
- No matches: Expand
POLY_SEARCH_TERMSandKALSHI_SERIES_TICKERS. - Duplicate rows: Dedupe uses
CSV_DEDUPE_SECONDSin.env.
This project is read-only and runs in DRY_RUN=True by default. It does not place any trades.
Set FRONTEND_ENABLED=True in .env, then run the scanner and open http://127.0.0.1:8765. The UI lets you view recent opportunities and update key .env settings. After saving, restart the scanner to apply changes.