Skip to main content

What is Polymarket Bot?

A real-time BTC price prediction engine for Polymarket 5-minute binary markets. The system monitors live BTC prices, predicts market outcomes using mathematical models, compares predictions against market prices to find positive Expected Value opportunities, and sizes bets using fractional Kelly criterion with sophisticated risk management.
This is a prediction engine, not a simple price tracker. It combines Black-Scholes option pricing, EWMA volatility estimation, and momentum analysis in logit space to produce calibrated probability forecasts.

System Workflow

The engine operates in a continuous loop (1-second intervals) processing real-time data through multiple stages:

Core Components

Chainlink WebSocket (src/feeds/chainlink.js:8)
  • Connects to wss://ws-live-data.polymarket.com
  • Streams BTC/USD price updates every ~1 second
  • NaN guard and 10% spike filter protect against bad data
  • Auto-reconnect with 3-second delay
Vatic Trading API (src/feeds/vatic.js:4)
  • Fetches strike prices aligned to 5-minute boundaries
  • Cached per epoch to minimize requests
  • Endpoint: https://api.vatic.trading/api/v1/targets/timestamp
Polymarket Gamma + CLOB (src/feeds/polymarket.js:10-12)
  • Gamma API discovers active markets via slug patterns
  • CLOB /price endpoint provides live market probabilities
  • Rate-limited to 1 request per second
  • Polled every 5 seconds (configurable)
The PredictionEngine class (src/engine/predictor.js:37) orchestrates:
  1. Volatility Estimation - EWMA tracker with λ=0.94
  2. Base Probability - Black-Scholes binary call probability N(d₂)
  3. Momentum Signals - Weighted ROC across 10s/30s/60s windows
  4. Mean Reversion - 2-minute SMA deviation detection
  5. Logit-Space Combination - Mathematically sound probability fusion
  6. Platt Calibration - Sigmoid recalibration after 200+ samples
Output: { probability, direction, volatility, momentum, reversion }
Position Sizer (src/risk/position-sizer.js:7)
  • Maps Brier Score to fractional Kelly alpha (0 → 0.40)
  • Computes optimal bet size: f = α × (p-q)/(1-q) × bankroll
  • Caps at 5% of bankroll per trade
  • Minimum bet: $1 USD
Drawdown Tracker (src/risk/drawdown-tracker.js:7)
  • Tracks 4 risk levels from high-water mark
  • Green (0-10%): Full sizing
  • Yellow (10-20%): Half alpha, higher EV threshold
  • Red (20-30%): Trading suspended
  • Critical (30%+): Trading suspended
  • Cold-streak detection: 5 consecutive misses → force yellow
7-Condition Safety Filter (src/engine/predictor.js:82-180)The system abstains when:
  1. Insufficient data (< 50 ticks)
  2. Dead zone (probability within 10% of 50%)
  3. Anomalous volatility (σ > 2× mean)
  4. Cold streak (accuracy < 40% in 20-interval window)
  5. Insufficient EV (< 5%)
  6. Insufficient margin (< 15pp)
  7. Drawdown suspension (red/critical level)
Prevents trading when the model has no statistical edge.

Data Flow

Per-Tick Processing (1000ms interval)

// 1. Ingest price tick
engine.feedTick({ timestamp: Date.now(), price: 64232.02 })
volatility.update(price, timestamp)  // EWMA σ²
momentum.addTick({ timestamp, price }) // ROC buffer

// 2. Generate prediction
const result = engine.predict({
  currentPrice: 64232.02,
  strikePrice: 64355.45,
  timeRemainingSeconds: 176
})
// → baseProb = N(d₂)  [Black-Scholes]
// → momentumFactor = 0.5×ROC₁₀ + 0.3×ROC₃₀ + 0.2×ROC₆₀
// → reversionFactor = -deviation (if |dev| > 0.3%)
// → finalProb = sigmoid(logit(baseProb) + 150×momentum + 80×reversion)

// 3. Check abstention conditions
if (abstained) return { reason: 'dead_zone', ... }

// 4. Compare to market
const ev = calculateEV(p=0.85, q=0.10)
// → evYes = p/q - 1 = 0.85/0.10 - 1 = +7.50 (750% EV!)
// → evNo = (1-p)/(1-q) - 1 = -0.83 (-83% EV)
// → bestSide = 'YES', bestEV = 7.50

// 5. Size position
const bet = positionSizer.calculateBet({
  p: 0.85, q: 0.10,
  bankroll: 100,
  brierScore: 0.19,
  predictionCount: 150
})
// → alpha = 0.25 (tier 3: Brier < 0.22)
// → fullKelly = (0.85-0.10)/(1-0.10) = 0.833
// → fractionalKelly = 0.25 × 0.833 = 0.208
// → bet = 0.208 × $100 = $20.80 → capped to $5.00 (5% max)

Per-Interval Lifecycle

1

Epoch Start

  • Detect 5-minute boundary (timestamp % 300 = 0)
  • Reset momentum analyzer (preserve volatility state)
  • Fetch strike price from Vatic API
  • Discover active market from Gamma API
2

Monitoring Phase (0-240s)

  • Stream price ticks continuously
  • Update EWMA volatility every tick
  • Buffer momentum data (300-tick rolling window)
  • Generate predictions every second
  • Record predictions at T-60s and T-30s
3

Near Expiry (240-300s)

  • Disable momentum/reversion adjustments (T < 5s)
  • Pure Black-Scholes probability only
  • Prevents gaming with late-stage volatility spikes
4

Interval Close

  • Capture final price from Chainlink oracle
  • Determine outcome: UP if close > strike, else DOWN
  • Evaluate prediction accuracy (early & final)
  • Feed outcome to Platt calibration tracker
  • Update drawdown tracker if bet was placed
  • Persist to data/history.json

Configuration

All parameters are defined in config/default.json:
{
  "engine": {
    "ewma": { "lambda": 0.94 },
    "momentum": { "bufferSize": 300 },
    "abstention": {
      "minTicks": 50,
      "deadZone": 0.10,
      "sigmaMultiplier": 2.0,
      "minAccuracy": 0.40,
      "minAccuracyWindow": 20,
      "minEV": 0.05,
      "minMargin": 0.15
    },
    "prediction": {
      "logitMomentumWeight": 150,
      "logitReversionWeight": 80,
      "nearExpiryGuardSec": 5
    }
  },
  "risk": {
    "bankroll": 100,
    "maxBetPct": 0.05,
    "kellyFraction": 0.25,
    "brierTiers": [
      { "maxBrier": 1.0, "minPredictions": 100, "alpha": 0.10 },
      { "maxBrier": 0.26, "minPredictions": 100, "alpha": 0.20 },
      { "maxBrier": 0.22, "minPredictions": 100, "alpha": 0.25 },
      { "maxBrier": 0.18, "minPredictions": 100, "alpha": 0.40 }
    ]
  }
}
Changing logitMomentumWeight or logitReversionWeight directly affects probability adjustments. A 1% price move with weight=150 shifts log-odds by ~0.15, equivalent to ~4pp probability change around p=0.5.

Performance Metrics

The system tracks multiple accuracy and calibration metrics:
MetricTargetWhat It Measures
Early 1m Accuracy> 80%Real trading performance (T-60s predictions)
Brier Score< 0.20Calibration quality (0=perfect, 0.25=coin flip)
BSS (Brier Skill Score)> 0Improvement over random baseline
Log Loss< 0.60Penalizes confident wrong predictions heavily
Expected Value> 5%Minimum edge vs market to justify trading
Edge (p - q)> 15ppSafety margin between model and market

Next Steps

How Predictions Work

Deep dive into Black-Scholes, EWMA, momentum, and logit-space combination

Data Sources

Chainlink WebSocket, Vatic API, Polymarket Gamma/CLOB integration details

Risk Management

Kelly criterion, Brier-tiered alpha, drawdown tracking

Abstention System

The 7 conditions that prevent trading when no edge exists

Build docs developers (and LLMs) love