# Poly Weather — Polymarket Weather Arbitrage Bot ## Project Overview Automated weather prediction market trading bot that exploits pricing inefficiencies in Polymarket's daily temperature markets. Compares professional weather forecast model probabilities (GFS/ECMWF/ICON ensembles) against market-implied probabilities and trades the delta. ### Five Strategies - **TAIL_NO** — Buy NO on overpriced tail buckets (e.g. "56°F or higher") where model prob < 5% but market prices YES > 3¢ - **LATENCY_ARB** — Trade when new weather model data shifts probabilities >5% but market hasn't repriced yet - **PARITY_ARB** — Exploit when sum of all YES prices in an event deviates from 100¢ (pure arbitrage) - **OBSERVED_YES** — Buy YES on observed temperature bucket during peak hours (11:00–14:00 local) when market hasn't priced in the known high - **OBSERVED_NO** — Buy NO on the bucket below observed daily high (valid all day, highest confidence since temp can only go up) ## Architecture TypeScript monorepo using Turborepo + pnpm workspaces. ``` packages/ core/ — Shared types, utils, Prisma schema, constants weather-engine/ — GFS/ECMWF/ICON forecast fetching via Open-Meteo, HKO forecast for Hong Kong, ensemble probability calculation market-engine/ — Polymarket Gamma API scanning, CLOB orderbook reads, authenticated order placement strategy/ — Signal generation (Tail NO, Latency Arb, Parity Arb, Observed YES/NO), risk management, Kelly sizing, portfolio tracking trading-engine/ — Main bot loop, pipeline orchestration, health-check server dashboard/ — Next.js App Router dashboard with live market data, signals, manual order placement ``` ### Dependency Graph ``` core (leaf — no internal deps) ↑ weather-engine, market-engine (depend on core) ↑ strategy (depends on core + weather-engine + market-engine) ↑ trading-engine (depends on all above) dashboard (depends on core, reads DB directly) ``` ## Tech Stack - **Runtime**: Node.js ≥ 20, TypeScript (ES2022, NodeNext modules, strict) - **Monorepo**: pnpm workspaces + Turborepo - **Database**: PostgreSQL 16 (Prisma ORM) + Redis 7 - **Dashboard**: Next.js 14 (App Router), Tailwind CSS, next-themes (light/dark), Radix UI primitives - **Market APIs**: Polymarket Gamma API (market discovery), CLOB API (orderbook + trading) - **Weather APIs**: Open-Meteo (GFS, ECMWF, ICON models), Hong Kong Observatory (HKO) API, wttr.in (live temps) - **Trading**: @polymarket/clob-client, ethers (wallet signing) ## Key Commands ```bash pnpm install # Install all deps pnpm build # Build all packages (turbo) pnpm dev # Dev mode all packages pnpm docker:up # Start PostgreSQL + Redis pnpm db:push # Push Prisma schema to DB pnpm db:studio # Open Prisma Studio pnpm bot:start # Start trading bot pnpm dashboard:dev # Start dashboard on :3000 pnpm derive-keys # Derive L2 API keys from PRIVATE_KEY pnpm test # Run all tests ``` ## Database Schema (Prisma) Located at `packages/core/prisma/schema.prisma`. - **WeatherForecast** — Stored model forecasts (city, date, model, predictedHigh, stdDev) - **MarketSnapshot** — Point-in-time market prices (yesPrice, noPrice, volume, tokenIds) - **Signal** — Detected trading signals (type, city, bucket, edge, modelProb, marketPrice, executed) - **Trade** — Order records (side, tokenId, price, size, costBasis, status, dryRun, pnl) - **DailyMetrics** — Aggregated daily performance (trades, winRate, netPnl, maxDrawdown) ## Dashboard Structure ``` src/app/ layout.tsx — Root layout with Sidebar + ThemeProvider page.tsx — Dashboard home (summary cards, recent signals) signals/ — Signal history with strategy filters + manual order placement markets/ — Live market data from Polymarket Gamma API + orderbook viewer pnl/ — PnL charts and performance metrics api/ dashboard/ — GET summary stats (bot status, uptime, PnL, win rate) signals/ — GET signal history with pagination/filtering markets/ — GET DB market snapshots with bucket details live-markets/ — GET live Polymarket data via Gamma API orderbook/ — GET merged YES/NO orderbook from CLOB prices/ — POST fetch latest prices for selected signals orders/ — POST place orders (dry run or live) bot-control/ — GET/POST bot status + auto-execute toggle current-temps/ — POST fetch live temps via wttr.in (5-min cache) trade-sync/ — POST sync pending CLOB orders for settlement pnl/ — GET historical P&L data + performance metrics ``` ### Dashboard Features - **Light/Dark theme** via next-themes with `class` attribute strategy - **Neg-risk orderbook merging**: YES bids = direct YES bids ∪ (1 − NO asks) - **Manual order placement**: Multi-select signals → fetch live prices → choose limit/market → confirm → execute (works in DRY_RUN) - **Polymarket deep links**: Direct links to specific market events using city slug mapping - **City clocks**: Live local time for all 24 cities with daytime/nighttime indicators + weather history links - **Compact market cards**: Per-city live temps, forecast highs, bucket price bars, responsive 6-column grid - **Bot control panel**: Real-time bot status (LIVE badge), auto-execute toggle in sidebar - **Order panel**: Multi-step workflow (review → pricing → confirm → result) with batch ordering support ## Important Conventions - **DRY_RUN=true by default** — All signal detection and DB recording works, but no real orders are placed - **Temperature units** — US cities (NYC/Chicago/Miami/Dallas/Atlanta/Seattle) = °F, all others = °C - **24 cities across 3 tiers** — P0 (NYC/London/Chicago), P1 (Paris/Seoul/Tokyo/Miami), P2 (20+ global cities) - **Maker orders only** — Limit orders (GTC) on Polymarket to pay zero fees - **All timestamps UTC** — Local timezone only in dashboard display - **Error isolation** — One city/market failing must not block others - **City slug overrides** — `new_york` → `nyc` in Polymarket URLs (see `citySlugOverrides` in queries.ts) ## CSS Theme System Uses Tailwind `darkMode: 'class'` with custom component classes in `globals.css`: - `.card`, `.badge-*`, `.table-header`, `.table-cell`, `.btn-*` — All have light + `.dark` variants - `.text-heading`, `.text-body`, `.text-sub`, `.text-dim` — Theme-aware text colors - Always use `dark:` prefix for dark-mode-specific colors inline ## Environment Variables See `.env.example` for all required variables. Key ones: - `DATABASE_URL` — PostgreSQL connection - `PRIVATE_KEY` + `FUNDER_ADDRESS` — Polymarket wallet (for live trading) - `DRY_RUN` — `true` (default) to simulate, `false` for real orders - `SIGNATURE_TYPE` — `1` for POLY_PROXY (standard Polymarket exported wallets) ## Ensemble Weights (4 models) ``` ecmwf: 30%, gfs: 25%, hko: 25%, icon: 20% ``` HKO is the official settlement source for Hong Kong markets. Updates 4x daily. ## Risk Management - **Kelly criterion** position sizing with configurable fraction (default 25%) - **Position limits**: per-market ($50), per-city ($200), total exposure ($1000), daily loss cap ($100) - **Min liquidity**: $500 required in orderbook before placing orders - **Portfolio tracker**: In-memory position tracking, PnL calculation, daily metrics aggregation ## Bot Health Check Server (port 3001) - `/health` — Basic status (running/stopped, uptime, dryRun mode) - `/metrics` — Detailed metrics (open positions, daily PnL, last cycle results) - `/auto-execute` — Toggle endpoint for enable/disable ## Polymarket Integration Notes - Gamma API (`gamma-api.polymarket.com`) — Market discovery, no auth needed - CLOB API (`clob.polymarket.com`) — Orderbook reads (no auth), order placement (requires wallet auth) - WebSocket feed (`wss://ws-subscriptions-clob.polymarket.com`) — Real-time price updates with auto-reconnect - Neg-risk markets have separate YES and NO token IDs; orderbooks must be merged for accurate pricing - Market URL pattern: `https://polymarket.com/event/highest-temperature-in-{city-slug}-on-{month}-{day}-{year}`