Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/joicodev/polymarket-bot/llms.txt

Use this file to discover all available pages before exploring further.

Architecture

The Polymarket Bot prediction engine combines multiple mathematical models to generate probabilistic forecasts for binary market outcomes. The system operates in real-time on streaming price data and produces calibrated probability estimates.

Core Components

The prediction pipeline consists of five interconnected modules:

Black-Scholes

Binary option probability from geometric Brownian motion assumptions

Volatility

EWMA estimator producing per-second volatility from tick stream

Momentum

ROC and mean-reversion signals across multiple time windows

Calibration

Platt sigmoid scaling for post-hoc probability refinement

Prediction Flow

1. Data Ingestion

Price ticks are fed into both the volatility estimator and momentum analyzer:
feedTick({ timestamp, price }) {
  this._volatility.update(price, timestamp)
  this._momentum.addTick({ timestamp, price })
}

2. Base Probability Calculation

The Black-Scholes binary option model produces a base probability using current volatility:
const sigma = this._volatility.getVolatility()
const baseProb = binaryCallProbability({
  currentPrice,
  strikePrice,
  volatility: sigma,
  timeRemainingSeconds,
  riskFreeRate: 0,
})
Volatility Units: The EWMA estimator produces per-second volatility (σ), matching the time units expected by the Black-Scholes formula.

3. Signal Extraction

Momentum and mean-reversion signals are computed from the tick buffer:
const { combined: momentumFactor } = this._momentum.getMomentum()
const { signal: reversionFactor } = this._momentum.getMeanReversion()

4. Logit-Space Fusion

Signals are combined using log-odds arithmetic to preserve probability bounds:
const logitBase = logit(baseProb)
const logitAdj = logitBase
  + config.engine.prediction.logitMomentumWeight * momentumFactor
  + config.engine.prediction.logitReversionWeight * reversionFactor
finalProb = sigmoid(logitAdj)
Logit-space combination is critical — naive probability addition/multiplication can produce values outside [0,1]. The logit transform maps (0,1) → ℝ, allowing safe additive adjustments.

5. Platt Calibration

When ≥200 prediction/outcome pairs have been collected, the scaler automatically activates:
if (this._scaler.canFit()) {
  if (!this._scaler.getStats().fitted) {
    this._scaler.fit()
  }
  finalProb = this._scaler.calibrate(finalProb)
}

Abstention Conditions

The engine abstains (refuses to predict) under six conditions:
ConditionTriggerReason
Insufficient Dataσ = 0 or ticks < minTicksEWMA not warmed up
Dead Zone|p - 0.5| < deadZoneNo edge detected
Anomalous Regimeσ > sigmaMultiplier × mean(σ)Volatility spike
Cold StreakRecent accuracy < minAccuracyModel underperforming
Low EVEV < minEVExpected value too small (post-prediction filter)
Thin Marginmargin < minMarginInsufficient Kelly bet size (post-prediction filter)
Conditions 5-6 are implemented as post-prediction filters in src/index.js because they require market price data external to the model.

Near-Expiry Guard

When ≤5 seconds remain until market close, momentum/reversion adjustments are skipped:
if (timeRemainingSeconds <= config.engine.prediction.nearExpiryGuardSec) {
  finalProb = baseProb  // Use only Black-Scholes probability
} else {
  // Apply logit-space adjustments
}
This prevents late-stage signal noise from corrupting the base probability estimate.

Mathematical Foundations

Logit Transform

The logit (log-odds) function maps probability space to the real line: logit(p)=log(p1p):(0,1)(,+)\text{logit}(p) = \log\left(\frac{p}{1-p}\right) \quad : \quad (0,1) \to (-\infty, +\infty) Its inverse, the sigmoid (logistic) function: σ(z)=11+ez:(,+)(0,1)\sigma(z) = \frac{1}{1 + e^{-z}} \quad : \quad (-\infty, +\infty) \to (0,1)

Why Logit-Space Combination?

Probabilities are constrained to [0,1], making naive arithmetic problematic:
  • Addition: 0.6 + 0.5 = 1.1 (invalid)
  • Multiplication: 0.9 × 0.9 = 0.81 (compression bias)
Log-odds are additive on the real line: padj=σ(logit(pbase)+w1f1+w2f2+)p_{\text{adj}} = \sigma\left(\text{logit}(p_{\text{base}}) + w_1 f_1 + w_2 f_2 + \cdots\right) This ensures:
  1. Adjusted probabilities always ∈ (0,1)
  2. Symmetric treatment of probabilities near 0 and 1
  3. Natural interpretation as additive evidence

Implementation Details

Safety Clamping

The logit function is undefined at 0 and 1. Inputs are clamped to [10⁻⁷, 1-10⁻⁷]:
function logit(p) {
  const safe = clamp(p, 1e-7, 1 - 1e-7)
  return Math.log(safe / (1 - safe))
}
Final predictions are clamped to [0.01, 0.99] before returning:
finalProb = clamp(finalProb, 0.01, 0.99)

Outcome Tracking

The engine maintains a rolling window of recent outcomes for cold-streak detection:
recordOutcome(correct, predictedProb) {
  const window = config.engine.abstention.minAccuracyWindow
  this._recentOutcomes.push(correct)
  while (this._recentOutcomes.length > window) {
    this._recentOutcomes.shift()
  }
  // Also feed Platt scaler
  this._scaler.collect(predictedProb, correct ? 1 : 0)
}

State Management

The engine offers two reset modes:
// Full reset: clear volatility + momentum + calibration
reset() {
  this._volatility.reset()
  this._momentum.reset()
  this._recentOutcomes = []
  this._scaler = new PlattScaler()
}

// Partial reset: preserve volatility estimate across epochs
resetMomentum() {
  this._momentum.reset()
}
When to use which reset:
  • reset(): Market close, context switch, or emergency flush
  • resetMomentum(): New interval start (volatility should persist)

Configuration Parameters

Key engine settings from config.js:
engine: {
  ewma: {
    lambda: 0.94  // EWMA decay factor
  },
  momentum: {
    bufferSize: 300  // Max ticks retained
  },
  prediction: {
    logitMomentumWeight: 2.0,     // Momentum signal weight in logit space
    logitReversionWeight: 1.5,    // Reversion signal weight in logit space
    nearExpiryGuardSec: 5         // Disable adjustments when t ≤ 5s
  },
  abstention: {
    minTicks: 5,                  // Minimum ticks before predicting
    deadZone: 0.05,               // ±5% around 0.5
    sigmaMultiplier: 3.0,         // Anomaly threshold (3× mean volatility)
    minAccuracyWindow: 20,        // Rolling accuracy window size
    minAccuracy: 0.50             // Cold-streak threshold
  }
}

Next Steps

Black-Scholes Model

Deep dive into binary option probability calculation

Volatility Estimation

EWMA algorithm and per-second variance normalization

Momentum Signals

ROC windows and mean-reversion detection

Predictor Integration

Complete prediction engine API and usage examples

Build docs developers (and LLMs) love