NBA Sports Arbitrage Monitoring Platform - Real-time monitoring of ESPN odds and Polymarket prediction markets to automatically discover arbitrage opportunities
π δΈζζζ‘£ | English
π Detailed Documentation: API Documentation
polysniper/
βββ client/ # Frontend app (React + Vite + TailwindCSS)
βββ server/ # Backend service (Node.js + Express + WebSocket)
βββ shared/ # Shared TypeScript types
βββ package.json # Root configuration
βββ README.md # Project documentation
# Install all dependencies (root + frontend + backend)
npm run install:all# Start both frontend and backend dev servers
npm run dev
# Or start separately
npm run dev:server # Backend: http://localhost:3000
npm run dev:client # Frontend: http://localhost:5173# Build frontend and backend
npm run build
# Start production server
npm start- Framework: React 19 + TypeScript
- Build Tool: Vite 7
- Styling: TailwindCSS 4
- Charts: Recharts
- Icons: Lucide React
- WebSocket: Socket.IO Client
- Runtime: Node.js + TypeScript
- Framework: Express
- WebSocket: Socket.IO + Native WebSocket (Polymarket CLOB)
- Database: SQLite (Paper Trading)
- Cache: Redis (optional)
- Logging: Winston
- Data Sources:
- ESPN API - Game schedules, live scores, win probabilities, injury reports, betting odds (via pickcenter)
- Polymarket CLOB WebSocket - Real-time market price data
- Polymarket Gamma API - Market search and matching
Edge = MIN(ESPN Win Probability, Bookmaker Average Probability) - Polymarket Price
| Data Source | Purpose | Free? |
|---|---|---|
| ESPN Win Probability | Real-time statistical prediction | β |
| ESPN PickCenter Odds | Bookmaker odds (DraftKings, etc.) | β |
| Polymarket Price | Prediction market buy/sell price | β |
- Minimum Edge: 5% (configurable)
- Live Only: Signals only trigger during live games
- Price Used: BestAsk (actual buy price, not mid price)
ESPN Probability: 65%
Bookmaker Probability: 60% (from DraftKings)
Polymarket Ask Price: $0.52
Conservative Probability = MIN(65%, 60%) = 60%
Edge = 60% - 52% = 8% β₯ 5% β BUY SIGNAL β
GET /health- Health checkGET /api/matches- Get all matchesGET /api/matches/:id- Get single matchGET /api/signals- Get arbitrage signalsGET /api/stats- Get statistics
- Connection:
ws://localhost:3000 - Events:
subscribe- Subscribe to match updatesunsubscribe- Unsubscribe from matchesmatchesUpdate- Receive match updatessignalAlert- Receive arbitrage signals
π Detailed API Documentation: server/API.md
- β‘ Millisecond Real-time Updates - Polymarket WebSocket push, price latency < 1s
- π Multi-source Data Integration - ESPN + Polymarket + Bookmaker odds
- π° Conservative Arbitrage Detection - Dual-source consensus, 5%+ edge threshold
- π° Free Odds Data - ESPN PickCenter bookmaker odds (no API key needed)
- π€ Paper Trading - Q1-Q3 value reversion strategy with hybrid exit mechanism
- πΈ Real Price Simulation - Use Ask for buying, Bid for selling, includes slippage
- π― Smart Exit Strategy - Take profit (25%) + logic invalidation + hard stop loss (50%)
- π Data Visualization - ESPN-style win probability curves + bookmaker odds display
- π― Intelligent Matching - Three-layer funnel for precise team and market matching
- β° Time Control - Only trade Q1-Q3, avoid Q4 gambling logic
- β Scores, Time, ESPN Win Prob, Polymarket Prices, Bookmaker Odds
- ESPN: Request every 1 second (throttled)
- Polymarket: WebSocket real-time push (passive receive)
- Frontend: Push every 500ms
- β Today's Match List, Token IDs, Market IDs, Team Mappings
- This data doesn't change during games
- Reduces API requests, improves performance
| Price Type | Usage | Source |
|---|---|---|
| Ask (Sell Price) | Pay when buying | asks[0].price |
| Bid (Buy Price) | Receive when selling | bids[0].price |
| Mid (Mid Price) | Display, valuation | (Bid + Ask) / 2 |
| Decimal Odds | Bookmaker display | Converted from American |
# Service Configuration
PORT=3000
NODE_ENV=development
# Polymarket WebSocket
POLYMARKET_WS_ENABLED=true
POLYMARKET_WS_URL=wss://ws-subscriptions-clob.polymarket.com/ws/market
# Proxy: use 'none' for overseas servers, or 'http://127.0.0.1:7890' for China
POLYMARKET_WS_PROXY=http://127.0.0.1:7890
# CORS
CORS_ORIGIN=*
# Redis (optional)
REDIS_ENABLED=false
# Log level (debug to see heartbeat details)
LOG_LEVEL=info
β οΈ Important:
- Polymarket WebSocket requires HTTP proxy access (for networks in China)
- Use Clash or similar proxy at
127.0.0.1:7890- Heartbeat mechanism uses WebSocket protocol-level Ping/Pong (15s interval)
cd client
npm run dev # Start dev server
npm run build # Production build
npm run lint # Code lintingcd server
npm run dev # Start dev server
npm run build # TypeScript compilation
npm run test # Run tests# 1. Clone the project to your server
git clone <your-repo-url> polysniper
cd polysniper
# 2. Configure environment variables
cp server/.env.example .env
nano .env # Set POLYMARKET_WS_PROXY=none for overseas servers
# 3. Start all services (frontend + backend + Redis)
docker compose up -d --build
# 4. View logs
docker compose logs -f server
# 5. Access the application
# Frontend: http://<your-server-ip>
# API: http://<your-server-ip>/api/matches| Command | Description |
|---|---|
docker compose up -d |
Start all services |
docker compose down |
Stop all services |
docker compose restart server |
Restart backend |
docker compose logs -f server |
View backend logs |
docker compose ps |
Check container status |
cd server
npm run start:pm2-
Proxy Configuration π
- Overseas servers: Set
POLYMARKET_WS_PROXY=none - China networks: Set
POLYMARKET_WS_PROXY=http://127.0.0.1:7890(Clash proxy)
- Overseas servers: Set
-
WebSocket Subscription Limits π‘
- Maximum 10 tokens per subscription
- 100ms interval between batches
- Avoid
INVALID OPERATIONerrors
-
Free Data Sources πΈ
- ESPN PickCenter provides bookmaker odds (DraftKings, etc.) for free
- No API key required for odds data
- Odds automatically extracted and displayed
-
Data Latency β±οΈ
- Polymarket WebSocket: < 1s
- ESPN polling: 1-5s (throttled)
- Auto-run - Automatically initialized after project starts, no manual configuration needed
- Persistent Storage - Uses SQLite database, data saved permanently
- Real Simulation - Buy at Ask, sell at Bid, includes slippage
- Smart Strategy - Q1-Q3 value reversion with hybrid exit mechanism
server/data/polysniper.db
cd server
# View database status (account balance, positions, orders)
npm run init-db
# View latest 20 market snapshots
npm run view-snapshots
# Reset database (clear all data, start fresh)
npm run reset-db- Initial balance: $1000
- Current balance
- Total trades, win rate
- Total P&L, P&L percentage
- Buy/sell records
- Entry/exit prices
- P&L statistics
- Battle Context: scores, quarter, time, ESPN probabilities
- Only saves LIVE and FINAL status (pre-game data not saved)
- Scores, probabilities, prices
- Arbitrage signals
Start project β Auto-connect to database
Game starts β Begin saving snapshots
Find signal β Auto-place order and record
Game ongoing β Update position P&L
Game ends β Auto-close and record
Close project β Data remains in database
Restart project β Auto-load previous data
- Data Never Lost - SQLite database persistently saves all data
- Auto-initialize - First run automatically creates $1000 initial account
- Pre-game Not Recorded - Only saves LIVE/FINAL status snapshots to avoid invalid data
- Privacy Protected -
.dbfile added to.gitignore
Real-time View (Frontend):
- Visit
http://localhost:5173 - Check "Paper Trading" panel
Historical View (Command Line):
cd server
npm run view-snapshots # View market snapshots
npm run init-db # View account status// Runs automatically, no configuration needed
Initial Capital: $1000 USDC
Position Management: 10% capital per trade
Trading Logic:
- Find signal β Auto buy (Ask price)
- Real-time P&L β Market valuation (Mid price)
- Game ends β Auto close (Bid price)
Exit Strategy:
- Take Profit: Profit β₯ 25%
- Logic Invalidation: ESPN prob reversal β₯ 20%
- Hard Stop Loss: Loss β₯ 50%Example Logs:
β
[Paper Trading] Buy LA Clippers x11.63 @$0.8600 (Ask price, cost: $10.00)
Order ID: ORD000001, Confidence: 95.0%, Balance: $990.00
Battle: Score 85-82, Q2 3:45, ESPN prob 72%
π [Paper Trading] Close LA Clippers @$0.9500
P&L: +$10.47 (+121.88%), Balance: $1010.47
Exit Reason: Take Profit
- π README - Project overview (English)
- π δΈζζζ‘£ - Project overview (Chinese)
- π‘ API Documentation - REST API & WebSocket (English)
- π‘ API ζ₯ε£ζζ‘£ - REST API & WebSocket (Chinese)
Issues and Pull Requests are welcome!
ISC License
