Automated Kalshi sports prediction market scanner
Buy YES contracts at 88–99¢ on games that are already decided. Collect $1 at settlement.
![]() A project by Brent Rager — founder of Smoo AI AI agents, dev tools, CRM, support & campaigns that integrate with everything you build. Let's talk. |
The scanner watches live sports games across NBA, NHL, MLB, NFL, MLS, Premier League, La Liga, NCAA, and UFC — and buys YES contracts on Kalshi when:
- ESPN confirms the game is in its final minutes — 4th quarter <5min, 9th inning, final period, etc.
- The leading team has a comfortable margin — 8+ pts NBA, 2+ goals NHL/soccer, 10+ pts NFL, etc.
- Kalshi YES price is 88–99¢ — market already expects this outcome
- Sufficient liquidity — 50+ volume, active bid
The edge: Kalshi prices lag behind live game state. A team up 15 points with 2 minutes left in the 4th quarter is a near-certainty, but the YES contract might still be at 92¢. We buy at 92¢, collect $1 at settlement. Small margins, high win rate.
graph TD
ESPN["🏈 ESPN API<br/>scores, periods, clocks · 10s poll"]
KWS["⚡ Kalshi WebSocket<br/>real-time prices + settlements"]
KREST["📡 Kalshi REST API<br/>discover markets, place orders · 5s"]
SCANNER["🐍 Scanner<br/>Python / asyncio"]
DB["🗄️ SQLite on EFS<br/>trades, balance, opportunities"]
API["🚀 FastAPI<br/>/api/*"]
DASH["🖥️ Next.js Dashboard<br/>getrich.rager.tech"]
ESPN --> SCANNER
KWS --> SCANNER
SCANNER --> DB
SCANNER --> KREST
DB --> API
API --> DASH
style ESPN fill:#1a1a2e,stroke:#FF0000,color:#fff
style KWS fill:#1a1a2e,stroke:#64748b,color:#fff
style KREST fill:#1a1a2e,stroke:#64748b,color:#fff
style SCANNER fill:#1a1a2e,stroke:#F59E0B,color:#fff
style DB fill:#1a1a2e,stroke:#003B57,color:#fff
style API fill:#1a1a2e,stroke:#009688,color:#fff
style DASH fill:#1a1a2e,stroke:#fff,color:#fff
| Layer | Source | Method | Purpose |
|---|---|---|---|
| Prices | Kalshi WebSocket | ticker channel, real-time |
Live YES/NO bid-ask streaming |
| Settlements | Kalshi WebSocket | market_lifecycle_v2 |
Instant win/loss detection |
| Markets | Kalshi REST API | Poll every 5s | Discover new markets, place orders |
| Game State | ESPN API | Poll every 10s | Score, period, clock verification |
| Dashboard | FastAPI → Next.js | REST | P&L tracking, live games, analytics |
Five shadow strategies run in parallel — evaluating every market against different price thresholds, timing windows, and lead requirements. Each tracks hypothetical P&L so we can backtest parameter changes before risking real capital.
Python 3.12 |
FastAPI |
SQLite |
Next.js 16 |
React 19 |
Tailwind CSS |
Docker |
AWS ECS |
SST v3 |
Cloudflare |
WebSockets |
ESPN API |
The dashboard at getrich.rager.tech shows:
- Account Value — step chart tracking balance over time with each win/loss
- Live Games — real-time ESPN game state with Kalshi market prices, color-coded by betting criteria
- Scanner Config — live view of all trading parameters and per-sport rules
- Trade History — every bet placed with P&L
- Strategy Comparison — side-by-side what-if analysis across 5 parameter sets
- Stats — win rate, realized P&L, open positions
- SST v3 + pnpm
- uv (Python package manager)
- AWS account (us-east-2)
- Cloudflare account (DNS)
- Kalshi API keys
SST secrets must be set before first deploy:
npx sst secret set KalshiApiKey "your-kalshi-api-key-id"
npx sst secret set KalshiPrivateKey "-----BEGIN RSA PRIVATE KEY-----\n..."
npx sst secret set DashboardPassword "your-dashboard-password"
npx sst secret set ApiToken "your-api-bearer-token"SST provisions everything in sst.config.ts:
| Resource | What | Notes |
|---|---|---|
| VPC | Networking with NAT | EC2-based NAT to save cost |
| ECS Cluster | Container orchestration | Single service runs API + Scanner |
| EFS | Persistent storage | SQLite database lives here |
| S3 Bucket | DB backups | Periodic SQLite snapshots |
| Next.js (SST) | Dashboard | Lambda + CloudFront via OpenNext |
| Cloudflare DNS | Domain routing | getrich.rager.tech + getrich-api.rager.tech |
# Deploy to AWS (requires AWS creds + Cloudflare token in .envrc)
pnpm sst:deploy
# Local development (Docker Compose)
pnpm dev
# Run API locally (without Docker)
pnpm dev:api
# Tear down all infrastructure
pnpm sst:removeA react-ink TUI for managing the scanner from the terminal:
# View current config (interactive TUI)
pnpm cli config
# Update a config value
pnpm cli config set trading.min_yes_price 90
# View stats / trades
pnpm cli stats
pnpm cli trades
# JSON output for piping / automation
pnpm cli stats --json
pnpm cli trades --json | jq '.trades[0]'Requires API_TOKEN env var (or --token flag). Set GETRICH_API_URL to override the default API endpoint.
The scanner reads config from the database at startup. Update settings via the CLI above or the API directly:
# Update a config value (requires API_TOKEN)
curl -X PUT https://getrich-api.rager.tech/api/config \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"key": "trading.min_yes_price", "value": "92"}'
# View current config
curl https://getrich-api.rager.tech/api/config \
-H "Authorization: Bearer $API_TOKEN"# Format + lint Python
uv run ruff format . && uv run ruff check . && uv run ty check
# Format + lint TypeScript
cd dashboard && pnpm oxfmt . && pnpm oxlint
# Build dashboard
cd dashboard && pnpm buildA pre-commit hook runs automatically: formats Python (ruff) and TypeScript (oxfmt), then type-checks with ty.
Built by Brent Rager at Smoo AI with Claude Code
