This guide covers setting up your environment to run the Polymarket Bot, including directory structure, logging configuration, data persistence, and production deployment considerations.
Prerequisites
System Requirements
Node.js: v18.0.0 or higher (ESM support required)
Package manager: pnpm (recommended) or npm
OS: Linux, macOS, or Windows (WSL recommended)
Disk space: 1-5 GB for logs and tick data (grows over time)
Network: Stable internet connection for WebSocket feeds
Installing Node.js
macOS (Homebrew)
Ubuntu/Debian
Using nvm (all platforms)
Installing pnpm
Project Setup
Clone and Install
# Clone repository
git clone < repository-ur l >
cd polymarket-bot
# Install dependencies
pnpm install
# Verify installation
pnpm start --help
Dependencies
The bot has minimal dependencies (see package.json):
{
"dependencies" : {
"chalk" : "^5.6.2" , // Terminal colors
"ws" : "^8.19.0" // WebSocket client
},
"devDependencies" : {
"@types/ws" : "^8.18.1" // TypeScript types
}
}
Directory Structure
The bot expects the following directory structure:
polymarket-bot/
├── src/ # Source code
├── config/ # Configuration files
│ └── default.json # Main config (version controlled)
├── data/ # Auto-created data directory
│ ├── history.json # Interval history (persistent)
│ ├── logs/ # Daily log files
│ │ ├── bot-2026-03-01.log
│ │ ├── bot-2026-03-02.log
│ │ └── ...
│ └── ticks/ # Raw tick data (optional)
│ ├── ticks-2026-03-01.jsonl
│ └── ...
└── package.json
Auto-Created Directories
The bot automatically creates directories as needed:
// From src/logger/logger.js
const LOG_DIR = join ( __dirname , '..' , '..' , 'data' , 'logs' )
mkdirSync ( LOG_DIR , { recursive: true })
Directories created on first run:
data/ — Root data directory
data/logs/ — Log files
data/ticks/ — Tick data (if tick logging is enabled)
You don’t need to manually create these directories. The bot handles it automatically.
Configuration
Configuration File Location
Configuration is loaded from config/default.json (see src/config.js:6):
const config = JSON . parse (
readFileSync ( join ( __dirname , '..' , 'config' , 'default.json' ), 'utf8' )
)
Key points:
No environment variable overrides (all config in JSON)
Single source of truth: config/default.json
Changes require bot restart (no hot reloading)
Customizing Configuration
Edit config/default.json directly: {
"engine" : {
"abstention" : {
"minEV" : 0.03 ,
"minMargin" : 0.10
}
},
"bot" : {
"loopIntervalMs" : 500
}
}
Restart the bot to apply changes: Option 1: Version control different config filesconfig/
├── default.json # Base config
├── production.json # Production overrides
└── staging.json # Staging overrides
Modify src/config.js to load based on NODE_ENV: const env = process . env . NODE_ENV || 'default'
const configFile = join ( __dirname , '..' , 'config' , ` ${ env } .json` )
const config = JSON . parse ( readFileSync ( configFile , 'utf8' ))
Option 2: Use environment-specific symlinksln -sf production.json config/default.json
Initial Bankroll
Update the bankroll to match your trading capital:
{
"risk" : {
"bankroll" : 100 // Change to your starting capital in USD
}
}
The bot does not connect to your Polymarket account. It only simulates bet sizing. You must manually execute trades on Polymarket based on the bot’s recommendations.
Logging
Log Structure
The bot uses a dual-logging system:
Console output: Real-time display (stdout/stderr)
File output: Daily rotating log files in data/logs/
Logs use structured format with ISO timestamps (see src/logger/logger.js:14-17):
[2026-03-04T10:23:45.123Z] [INFO] WebSocket connected {"feed":"chainlink"}
[2026-03-04T10:23:46.234Z] [WARN] Market price stale {"age":12.5}
[2026-03-04T10:23:47.345Z] [ERROR] API request failed {"status":503,"url":"https://..."}
Format: [timestamp] [level] message {context}
Log Levels
Level Purpose Output INFONormal operations, predictions, bets stdout + file WARNRecoverable issues (stale data, spikes) stderr + file ERRORErrors requiring attention stderr + file
Accessing Logs
# View today's log
tail -f data/logs/bot- $( date +%Y-%m-%d ) .log
# Search for errors
grep ERROR data/logs/bot-2026-03-04.log
# Filter by context
grep 'abstained' data/logs/bot-2026-03-04.log | jq
Log Rotation
Logs are automatically rotated daily based on filename:
function getLogPath () {
const date = new Date (). toISOString (). split ( 'T' )[ 0 ]
return join ( LOG_DIR , `bot- ${ date } .log` )
}
Behavior:
New file created at midnight (UTC)
No automatic deletion (manual cleanup required)
No size-based rotation (files grow unbounded per day)
Managing Log Growth
Manual cleanup
Compress old logs
Cron job (daily cleanup)
# Delete logs older than 30 days
find data/logs -name 'bot-*.log' -mtime +30 -delete
Production recommendation: Set up a cron job to compress logs older than 7 days and delete logs older than 90 days.
Data Persistence
Interval History
The bot stores all resolved intervals in data/history.json (configurable via storage.historyPath).
Format: JSON array of interval objects:
[
{
"intervalNumber" : 1 ,
"targetPrice" : 64232.50 ,
"finalPrice" : 64315.20 ,
"outcome" : "UP" ,
"prediction" : {
"probability" : 0.72 ,
"direction" : "UP" ,
"capturedAt" : "2026-03-04T10:24:30Z"
},
"marketPrice" : 0.68 ,
"ev" : 0.059 ,
"betSize" : 2.50 ,
"accuracy" : true ,
"brierScore" : 0.078 ,
"..." : "30+ fields total"
}
]
History File Management
history.json grows continuously. A typical day generates 288 intervals (24h × 12 per hour). At ~1KB per interval, expect ~100KB per day or ~3MB per month.
Backup strategy:
# Daily backup (add to cron)
cp data/history.json data/backups/history- $( date +%Y-%m-%d ) .json
# Compress old backups
gzip data/backups/history- * .json
Archiving old data:
# Archive intervals older than 90 days to separate file
node scripts/archive-history.js --days 90
Tick Data (Optional)
Raw tick data can be logged to JSONL files for backtesting:
{ "timestamp" : 1709550123456 , "price" : 64232.50 , "source" : "chainlink" }
{ "timestamp" : 1709550124567 , "price" : 64233.12 , "source" : "chainlink" }
Enable tick logging:
Tick logging is not included in the default configuration. To enable, modify src/feeds/chainlink.js to import and use src/logger/tick-logger.js.
Only enable tick logging if you plan to backtest or analyze tick-level data. It generates ~50MB per day at 1 tick/second.
Running the Bot
Development Mode
# Start with auto-reload on file changes
pnpm dev
# Equivalent to:
node --watch src/index.js
Use case: Local development, testing config changes
Production Mode
# Start normally
pnpm start
# Equivalent to:
node src/index.js
Background Execution
Using nohup
Using screen
Using systemd (recommended)
nohup pnpm start > output.log 2>&1 &
# Check process
ps aux | grep node
# Stop
kill < pi d >
Monitoring
Health checks:
# Check if process is running
pgrep -f 'node src/index.js'
# Check last log entry (should be recent)
tail -n 1 data/logs/bot- $( date +%Y-%m-%d ) .log
# Check WebSocket connection
grep 'WebSocket' data/logs/bot- $( date +%Y-%m-%d ) .log | tail -n 5
Alert on errors:
# Count errors in last hour
grep ERROR data/logs/bot- $( date +%Y-%m-%d ) .log | tail -n 100 | wc -l
# Alert if > 10 errors
if [ $( grep ERROR data/logs/bot- $( date +%Y-%m-%d ) .log | tail -n 100 | wc -l ) -gt 10 ]; then
echo "High error rate detected" | mail -s "Bot Alert" [email protected]
fi
Memory Usage
Expected memory footprint:
Base: 50-80 MB
With 300-tick momentum buffer: +5-10 MB
With Platt scaler (200+ samples): +2-5 MB
Total: ~100 MB RSS
The bot is designed for low memory usage. If you see >200 MB, check for memory leaks in custom modifications.
CPU Usage
Expected CPU usage:
Idle (waiting for ticks): <1%
Active (processing ticks): 2-5%
Prediction calculation: <1ms per tick
Bottlenecks:
WebSocket parsing (unavoidable)
JSON serialization for logging (reduce log verbosity if needed)
Momentum buffer operations (reduce bufferSize if needed)
Network
Bandwidth usage:
Chainlink WebSocket: ~1 KB/s (continuous)
Polymarket API polls: ~5 KB/5s (intermittent)
Total: <100 MB per day
Production Checklist
Before deploying to production:
Troubleshooting
Bot won’t start
# Check Node.js version
node --version # Should be v18.0.0 or higher
# Check dependencies
pnpm install
# Check config file syntax
node -e "console.log(JSON.parse(require('fs').readFileSync('config/default.json')))"
WebSocket connection issues
# Check logs for connection errors
grep 'WebSocket' data/logs/bot- $( date +%Y-%m-%d ) .log
# Test connectivity
curl -I https://ws-live-data.polymarket.com
# Check firewall (port 443 must be open)
telnet ws-live-data.polymarket.com 443
High abstention rate
# Analyze abstention reasons
grep 'abstained' data/logs/bot- $( date +%Y-%m-%d ) .log | jq -r .reason | sort | uniq -c
# Common fixes:
# - Too many dead_zone: Decrease engine.abstention.deadZone
# - Too many insufficient_margin: Decrease engine.abstention.minMargin
# - Too many anomalous_regime: Increase engine.abstention.sigmaMultiplier
Poor prediction accuracy
# Generate daily report
node src/reporter/daily.js 2026-03-04
# Check Brier Score (should be < 0.22)
# If Brier > 0.22: Tune logit weights (see Tuning Parameters guide)
# If accuracy < 70%: Increase abstention thresholds
Next Steps
Config Reference Complete reference for all configuration parameters.
Tuning Parameters Learn how to optimize the bot for your market conditions.