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.
Overview
This reference documents all major data structures used by the bot, including prediction results, interval records, configuration types, and API response shapes.
Core Types
Tick
Price tick from ChainlinkFeed.
interface Tick {
timestamp: number // Unix milliseconds (Date.now())
price: number // BTC/USD price
}
Source: src/feeds/chainlink.js:26
Example:
{
timestamp: 1733227533421,
price: 96842.50
}
PredictionResult
Output from PredictionEngine.predict().
interface PredictionResult {
// ── Full prediction (when abstained = false or undefined) ──
probability: number // Model probability (0.01-0.99)
direction: 'UP' | 'DOWN' // Based on probability >= 0.5
volatility: number // EWMA sigma (per-second)
momentum: number // Combined ROC factor
reversion: number // Mean-reversion signal
calibrated: boolean // Was Platt scaling applied?
// ── Abstention (when abstained = true) ──
abstained?: boolean // True when prediction withheld
reason?: string // Abstention reason code:
// 'insufficient_data', 'dead_zone',
// 'anomalous_regime', 'cold_streak',
// 'insufficient_ev', 'insufficient_margin',
// 'drawdown_suspended', 'insufficient_ev_yellow'
}
Source: src/engine/predictor.js:68-77
Example - Full prediction:
{
probability: 0.687,
direction: 'UP',
volatility: 0.00042,
momentum: 0.0023,
reversion: -0.0008,
calibrated: true
}
Example - Abstention:
{
abstained: true,
reason: 'dead_zone',
volatility: 0.00039,
probability: 0.512, // Base probability (before abstention)
direction: 'UP'
}
EVResult
Expected value calculation from calculateEV().
interface EVResult {
evYes: number // EV of buying YES: p/q - 1
evNo: number // EV of buying NO: (1-p)/(1-q) - 1
margin: number // Safety margin: |p-q| / max(p, 1-p)
bestSide: 'YES' | 'NO' // Which side has higher EV
bestEV: number // max(evYes, evNo)
ev: number // Alias for bestEV
edge: number // Model edge: p - q
}
Source: src/feeds/polymarket.js:141-150
Example:
{
evYes: 0.0818, // 8.18% expected value on YES
evNo: -0.0435, // Negative EV on NO
margin: 0.1538, // 15.38% margin
bestSide: 'YES',
bestEV: 0.0818,
ev: 0.0818,
edge: 0.045 // Model is 4.5% more bullish than market
}
BetResult
Bet sizing output from PositionSizer.
interface BetResult {
bet: number // Recommended bet size in USD
fullKelly: number // Full Kelly fraction (before alpha adjustment)
alpha: number // Fractional Kelly multiplier (0-1)
side: 'YES' | 'NO' // Which side to bet
capped: boolean // Was bet capped at maxBetPct?
}
Source: src/risk/position-sizer.js
Example:
{
bet: 2.50, // Bet $2.50
fullKelly: 0.025, // 2.5% of bankroll
alpha: 0.25, // Using 1/4 Kelly
side: 'YES',
capped: false
}
DrawdownState
Risk state from DrawdownTracker.
interface DrawdownState {
bankroll: number // Current bankroll in USD
highWaterMark: number // Historical peak bankroll
drawdownPct: number // Drawdown percentage (0-1)
coldStreak: number // Consecutive high-confidence misses
level: 'green' | 'yellow' | 'red' | 'critical' // Risk level
}
Source: src/risk/drawdown-tracker.js
Example:
{
bankroll: 94.50,
highWaterMark: 105.00,
drawdownPct: 0.10, // 10% drawdown
coldStreak: 0,
level: 'yellow'
}
IntervalRecord
Complete record of a closed 5-minute interval.
Source: src/tracker/interval.js:6-56
interface IntervalRecord {
// ── Core identity ──
index: number // Sequential 1-based index
epochTimestamp: number // 5-min epoch boundary (Unix seconds)
// ── Price data ──
strikePrice: number | null // Target price from Vatic
finalPrice: number | null // Actual price at interval close
result: 'UP' | 'DOWN' | 'ACTIVE' // Outcome (ACTIVE = not closed yet)
// ── Predictions ──
prediction: Prediction | null // Final prediction snapshot (30s mark)
predictionCorrect: boolean | null // Was final prediction correct?
earlyPrediction: Prediction | null // Early prediction snapshot (60s mark)
earlyPredictionCorrect: boolean | null // Was early prediction correct?
livePrediction?: Prediction | null // Current prediction (updates every tick)
// ── Model factors (captured at early snapshot) ──
volatility: number | null // EWMA sigma (per-second)
momentum: number | null // Combined ROC factor
reversion: number | null // Mean-reversion signal
calibrated: boolean | null // Was Platt calibration active?
// ── Market & EV (captured at early snapshot) ──
qMarket: number | null // Polymarket UP token price (0-1)
evAtCapture: number | null // Best expected value
edge: number | null // Model edge: p - q
margin: number | null // Safety margin
evSide: string | null // 'YES' | 'NO' - best EV side
// ── Bet details (captured at early snapshot) ──
betSize: number | null // USD bet from PositionSizer
fullKelly: number | null // Full Kelly fraction
alpha: number | null // Fractional Kelly alpha used
betSide: string | null // 'YES' | 'NO'
betCapped: boolean | null // Was bet capped at maxBetPct?
// ── Risk state (captured at early snapshot) ──
drawdownLevel: string | null // 'green'|'yellow'|'red'|'critical'
bankroll: number | null // Current bankroll USD
drawdownPct: number | null // Drawdown from high-water mark (0-1)
coldStreak: number | null // Consecutive high-conf misses
// ── Abstention tracking ──
abstentionReason: string | null // Why we abstained (null if traded)
// ── Timing ──
timeRemainingAtCapture: number | null // Seconds at early capture
// ── Price dynamics (computed at close) ──
priceDelta: number | null // finalPrice - strikePrice
priceMovePct: number | null // (finalPrice - strikePrice) / strikePrice * 100
closedAt: string | null // ISO timestamp of interval close
}
interface Prediction {
probability: number // Model probability (0.01-0.99)
direction: 'UP' | 'DOWN'
}
Example:
{
index: 42,
epochTimestamp: 1733227500,
strikePrice: 96842.50,
finalPrice: 96891.25,
result: 'UP',
prediction: { probability: 0.683, direction: 'UP' },
predictionCorrect: true,
earlyPrediction: { probability: 0.687, direction: 'UP' },
earlyPredictionCorrect: true,
volatility: 0.00042,
momentum: 0.0023,
reversion: -0.0008,
calibrated: true,
qMarket: 0.55,
evAtCapture: 0.0818,
edge: 0.045,
margin: 0.1538,
evSide: 'YES',
betSize: 2.50,
fullKelly: 0.025,
alpha: 0.25,
betSide: 'YES',
betCapped: false,
drawdownLevel: 'green',
bankroll: 102.50,
drawdownPct: 0,
coldStreak: 0,
abstentionReason: null,
timeRemainingAtCapture: 58,
priceDelta: 48.75,
priceMovePct: 0.0503,
closedAt: '2024-12-03T12:10:00.123Z'
}
Most fields are null until the appropriate capture window:
livePrediction: Updates every tick
earlyPrediction + model/market/bet/risk fields: Captured once at ≤60s remaining
prediction: Captured once at ≤30s remaining
finalPrice, result, price dynamics, closedAt: Set when interval closes
API Response Types
VaticResponse
Response from Vatic strike price API.
interface VaticResponse {
target_price?: number // Strike price (primary field)
target?: number // Fallback field
price?: number // Second fallback
[key: string]: any // Other metadata fields
}
Source: src/feeds/vatic.js:36
Example:
{
target_price: 96842.50,
timestamp: 1733227500,
asset: 'btc',
type: '5min'
}
PolymarketMarket
Response from discoverActiveMarket().
interface PolymarketMarket {
found: boolean
conditionId?: string // Polymarket condition ID
upTokenId?: string // CLOB token ID for UP
downTokenId?: string // CLOB token ID for DOWN
outcomePrices?: number[] // [upPrice, downPrice]
slug?: string // Market slug (e.g., 'btc-updown-5m-1733227500')
}
Source: src/feeds/polymarket.js:38-46
Example:
{
found: true,
conditionId: '0x1234...abcd',
upTokenId: '0x5678...ef01',
downTokenId: '0x9abc...def2',
outcomePrices: [0.55, 0.45],
slug: 'btc-updown-5m-1733227500'
}
Configuration Types
Config
Complete configuration schema from config/default.json.
interface Config {
engine: EngineConfig
feeds: FeedsConfig
storage: StorageConfig
bot: BotConfig
risk: RiskConfig
}
interface EngineConfig {
ewma: {
lambda: number // EWMA decay factor (0-1), default: 0.94
}
momentum: {
bufferSize: number // Max ticks to retain, default: 300
}
abstention: {
minTicks: number // Min ticks before predictions, default: 50
deadZone: number // Abstain if |p-0.5| < this, default: 0.10
sigmaMultiplier: number // Abstain if sigma > this * mean, default: 2.0
minAccuracy: number // Min recent accuracy, default: 0.40
minAccuracyWindow: number // Window size, default: 20
minEV: number // Min expected value, default: 0.05
minMargin: number // Min safety margin, default: 0.15
}
prediction: {
logitMomentumWeight: number // Momentum adjustment, default: 150
logitReversionWeight: number // Reversion adjustment, default: 80
nearExpiryGuardSec: number // Skip adjustments when < this, default: 5
}
}
interface FeedsConfig {
chainlink: {
spikeThreshold: number // Spike detection threshold, default: 0.10
}
polymarket: {
gammaBaseUrl: string // Gamma API base, default: 'https://gamma-api.polymarket.com'
clobBaseUrl: string // CLOB API base, default: 'https://clob.polymarket.com'
slugPrefix: string // Market slug prefix, default: 'btc-updown-5m-'
pollIntervalSec: number // Price poll interval, default: 5
offsetsFallback: number[] // Epoch offsets to try, default: [-300, 300]
enabled: boolean // Enable Polymarket feed, default: true
}
}
interface StorageConfig {
historyPath: string // History file path, default: 'data/history.json'
}
interface BotConfig {
loopIntervalMs: number // Main loop interval, default: 1000
}
interface RiskConfig {
bankroll: number // Initial bankroll USD, default: 100
maxBetPct: number // Max bet as fraction of bankroll, default: 0.05
minBetUsd: number // Min bet size USD, default: 1
kellyFraction: number // Base fractional Kelly, default: 0.25
drawdown: {
yellowPct: number // Yellow threshold, default: 0.10
redPct: number // Red threshold, default: 0.20
criticalPct: number // Critical threshold, default: 0.30
}
coldStreak: {
consecutiveMissThreshold: number // Threshold for cold streak, default: 5
minConfidenceForStreak: number // Min probability to count, default: 0.70
}
brierTiers: BrierTier[] // Brier score based alpha adjustments
}
interface BrierTier {
maxBrier: number | null // Max Brier for this tier (null = no max)
minPredictions: number // Min predictions to qualify
maxPredictions?: number // Max predictions for this tier
alpha: number // Fractional Kelly multiplier (0-1)
}
Source: config/default.json
Example:
import config from './config.js'
console.log(config.engine.ewma.lambda) // 0.94
console.log(config.feeds.polymarket.pollIntervalSec) // 5
console.log(config.risk.bankroll) // 100
Enums
ConnectionStatus
type ConnectionStatus = 'connected' | 'disconnected' | 'reconnecting'
Used by ChainlinkFeed’s onStatus callback.
Direction
type Direction = 'UP' | 'DOWN'
Prediction direction (price will go up or down relative to strike).
IntervalResult
type IntervalResult = 'UP' | 'DOWN' | 'ACTIVE'
Actual outcome of an interval:
'UP': Final price > strike price
'DOWN': Final price ≤ strike price
'ACTIVE': Interval not closed yet
DrawdownLevel
type DrawdownLevel = 'green' | 'yellow' | 'red' | 'critical'
Risk levels:
'green': < 10% drawdown (default)
'yellow': 10-20% drawdown (raise minEV threshold)
'red': 20-30% drawdown (suspend trading)
'critical': > 30% drawdown (emergency suspension)
AbstentionReason
type AbstentionReason =
| 'insufficient_data' // < minTicks received
| 'dead_zone' // Probability too close to 0.5
| 'anomalous_regime' // Volatility spike detected
| 'cold_streak' // Recent accuracy below threshold
| 'insufficient_ev' // Expected value < minEV
| 'insufficient_margin' // Safety margin < minMargin
| 'drawdown_suspended' // Red/critical drawdown level
| 'insufficient_ev_yellow' // EV below raised threshold (yellow level)
Reasons why the bot abstains from making a prediction or trade.
Type Guards & Validators
isPrediction()
Check if a PredictionResult is a full prediction (not abstention).
function isPrediction(result) {
return !result.abstained && result.probability != null
}
if (isPrediction(result)) {
console.log(`Prediction: ${result.direction} (${result.probability})`)
}
isValidTick()
Validate a price tick.
function isValidTick(tick) {
return (
tick &&
typeof tick.timestamp === 'number' &&
typeof tick.price === 'number' &&
Number.isFinite(tick.price) &&
tick.price > 0
)
}
Used internally by ChainlinkFeed (see src/feeds/chainlink.js:98-111).
isValidEV()
Check if EV result is tradeable.
function isValidEV(ev, minEV = 0.05, minMargin = 0.15) {
return (
ev != null &&
ev.bestEV >= minEV &&
ev.margin >= minMargin
)
}
if (prediction && isValidEV(evResult)) {
console.log(`Trade signal: ${evResult.bestSide}`)
}
JSON Serialization
All data structures are JSON-serializable by design:
import { HistoryStore } from './tracker/history.js'
const store = new HistoryStore()
const records = await store.load()
// Records are plain objects - safe to serialize
const json = JSON.stringify(records, null, 2)
console.log(json)
// Export to file
import { writeFile } from 'fs/promises'
await writeFile('export.json', json)
Special handling:
- Dates: Stored as ISO strings (e.g.,
closedAt)
- Timestamps: Unix seconds (epoch) or milliseconds (ticks)
- Null values: Explicitly stored (not undefined) for clarity
See Also