# Polymarket Trading Bot — Build Guide

> Before you begin, be sure to create your FRESH Polymarket account via THIS link: https://polymarket.com?via=alphastack-eymx
>
> **How to use this file:** Drop it into an empty folder, open it in
> Claude Code, and say *"Read this file and build everything."*
> Fund a Polygon wallet with USDC. Run `node bot.js`. Done.

---

# Part 1: The Big Picture

## What This Bot Does

An autonomous trading bot that detects mispriced Polymarket markets
using Truth Machine's free AI edge detection API, then places orders
on Polymarket's CLOB (Central Limit Order Book).

```
 YOUR BOT
 ════════════════════════════════════════════════════
 Every 15 minutes:

 ┌──────────┐     ┌──────────────┐     ┌──────────┐
 │  DETECT  │────►│   FILTER &   │────►│  TRADE   │
 │  EDGES   │     │   SIZE BET   │     │          │
 └──────────┘     └──────────────┘     └──────────┘
       │                 │                    │
       ▼                 ▼                    ▼
  Truth Machine    Kelly Criterion      Polymarket
  FREE API         Position Sizing      CLOB Orders
 ════════════════════════════════════════════════════
```

## System Architecture

```
┌─────────────────────────────────────────────────────────────┐
│                     YOUR BOT (Node.js)                       │
│                                                              │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐  │
│  │ edge-        │  │ wallet.js    │  │ trader.js        │  │
│  │ detector.js  │  │              │  │                  │  │
│  │              │  │ Connect to   │  │ Resolve market   │  │
│  │ Poll for     │  │ Polygon via  │  │ to token IDs,    │  │
│  │ edges every  │  │ seed phrase  │  │ get live price,   │  │
│  │ 15 minutes   │  │              │  │ generate order    │  │
│  │              │  │ Check USDC   │  │                  │  │
│  │ Filter by    │  │ balance      │  │ Execute on CLOB  │  │
│  │ min edge,    │  │              │  │ (or log signal)   │  │
│  │ confidence   │  │ Approve      │  │                  │  │
│  │              │  │ exchange     │  │                  │  │
│  └──────┬───────┘  └──────┬───────┘  └────────┬─────────┘  │
│         │                 │                    │             │
│  ┌──────┴─────────────────┴────────────────────┴──────────┐ │
│  │                    bot.js (orchestrator)                 │ │
│  │  • Load config from .env                                │ │
│  │  • Connect wallet on startup                            │ │
│  │  • Poll → Filter → Size → Trade loop                    │ │
│  │  • Daily loss limit circuit breaker                     │ │
│  └─────────────────────────────────────────────────────────┘ │
└──────────────────────────┬──────────────────────────────────┘
                           │
           ┌───────────────┼───────────────┐
           ▼               ▼               ▼
    ┌────────────┐  ┌────────────┐  ┌────────────┐
    │ Truth      │  │ Polymarket │  │ Polygon    │
    │ Machine    │  │ APIs       │  │ Network    │
    │            │  │            │  │            │
    │ GET /api/  │  │ Gamma API  │  │ USDC       │
    │ best-bets  │  │ (metadata) │  │ contract   │
    │            │  │            │  │            │
    │ FREE       │  │ CLOB API   │  │ Exchange   │
    │ No auth    │  │ (orders)   │  │ contract   │
    │ 15m cache  │  │            │  │            │
    └────────────┘  └────────────┘  └────────────┘
```

## Data Flow: From Edge Detection to Trade

```
Step 1: EDGE DETECTION
══════════════════════════════════════════════════

  Truth Machine AI Pipeline:
  ┌─────────┐   ┌──────────┐   ┌─────────┐
  │ 21 Data │──►│ Gemini   │──►│ Edge =  │
  │ Sources │   │ AI       │   │ AI% -   │
  │ (news,  │   │ Analysis │   │ Market% │
  │ finance,│   │          │   │         │
  │ sports) │   │          │   │ e.g.    │
  └─────────┘   └──────────┘   │ +8.4%   │
                               └────┬────┘
                                    │
                     GET /api/best-bets
                                    │
                                    ▼
Step 2: FILTERING
══════════════════════════════════════════════════

  Your Bot Criteria:
  ┌─────────────────────────────────────────┐
  │  Edge > 5%?         ──► YES ✓           │
  │  Confidence high?   ──► YES ✓           │
  │  Not expired?       ──► YES ✓           │
  │  Daily limit ok?    ──► YES ✓           │
  │                                         │
  │  ──► PASS: Proceed to sizing            │
  └─────────────────────────────────────────┘
                                    │
                                    ▼
Step 3: POSITION SIZING (Kelly Criterion)
══════════════════════════════════════════════════

  Quarter-Kelly Formula:
  ┌─────────────────────────────────────────┐
  │  p = AI probability (e.g. 0.72)         │
  │  b = payout odds (1/price - 1)          │
  │  kelly = (p*b - (1-p)) / b              │
  │  bet = min(MAX_BET, kelly * balance / 4)│
  │                                         │
  │  Example:                               │
  │  p=0.72, price=0.64, balance=$100       │
  │  b = 1/0.64 - 1 = 0.5625               │
  │  kelly = (0.72*0.5625 - 0.28) / 0.5625 │
  │  kelly = 0.22 (22% of bankroll)         │
  │  quarter = 0.055 (5.5%)                 │
  │  bet = min($10, $5.50) = $5.50          │
  └─────────────────────────────────────────┘
                                    │
                                    ▼
Step 4: ORDER EXECUTION
══════════════════════════════════════════════════

  ┌──────────┐   ┌───────────┐   ┌──────────┐
  │ Resolve  │──►│ Get live  │──►│ Place    │
  │ market   │   │ price     │   │ order    │
  │ slug to  │   │ from CLOB │   │ on CLOB  │
  │ token ID │   │ midpoint  │   │          │
  │ (Gamma)  │   │           │   │ BUY YES  │
  │          │   │ 64c       │   │ $5.50    │
  └──────────┘   └───────────┘   └──────────┘
```

## What It Costs

| Service | Cost | Notes |
|---------|------|-------|
| Truth Machine API | **Free** | No auth, 15-min cache |
| Polymarket trading | Gas fees only | ~$0.01 per order on Polygon |
| USDC capital | Your trading capital | Start with $50-100 |

## What You Need

| Requirement | Where | Notes |
|-------------|-------|-------|
| Node.js 18+ | [nodejs.org](https://nodejs.org) | Runtime |
| Polygon wallet | MetaMask or any wallet | With seed phrase |
| USDC on Polygon | Bridge from Ethereum or buy | Your trading capital |
| Polymarket account | [polymarket.com](https://polymarket.com?via=alphastack-eymx) | Must be approved for trading |

---

# Part 2: Setup

## Create the Project

```bash
mkdir polymarket-bot && cd polymarket-bot
npm init -y
npm install ethers@6 axios dotenv
```

## Project Structure

```
polymarket-bot/
├── .env                 ← Wallet seed phrase + config (NEVER commit)
├── .gitignore           ← Excludes .env and node_modules
├── bot.js               ← Main entry point + orchestration loop
├── edge-detector.js     ← Truth Machine API client
├── wallet.js            ← Polygon wallet connection + USDC approvals
└── trader.js            ← Market resolution + order placement
```

## Environment File

```env
# .env — NEVER commit this file
SEED_PHRASE="your twelve word seed phrase goes here"
MIN_EDGE=0.05              # 5% minimum edge to trade
MAX_BET_USD=10             # $10 max per position
POLL_INTERVAL=900000       # 15 minutes in milliseconds
DAILY_LOSS_LIMIT=50        # Stop trading after $50 daily loss
TRUTH_MACHINE_URL=https://truthmachine.live
```

## .gitignore

```
.env
node_modules/
```

---

# Part 3: The Code

## edge-detector.js — Fetch Edges from Truth Machine

```
┌─────────────────────────────────────────────────┐
│              TRUTH MACHINE API                   │
│                                                  │
│  GET /api/best-bets?limit=10&minEdge=0.05       │
│                                                  │
│  Returns:                                        │
│  {                                               │
│    markets: [                                    │
│      {                                           │
│        slug: "fed-rate-hike-2026",               │
│        question: "Fed rate hike in 2026?",       │
│        side: "NO",        ← which side to bet    │
│        edge: 0.084,       ← 8.4% edge            │
│        ai_probability: 0.12,                     │
│        market_price: 0.20,                       │
│        confidence: "high"                        │
│      }                                           │
│    ]                                             │
│  }                                               │
└─────────────────────────────────────────────────┘
```

```javascript
const axios = require('axios');

const TM_URL = process.env.TRUTH_MACHINE_URL || 'https://truthmachine.live';

/**
 * Fetch markets with detected edges from Truth Machine.
 * Free API, no auth required. Cached for 15 minutes.
 */
async function getEdges(minEdge = 0.05, limit = 10) {
  const { data } = await axios.get(
    `${TM_URL}/api/best-bets?limit=${limit}&minEdge=${minEdge}`
  );

  return (data.markets || []).map(m => ({
    slug: m.slug,
    question: m.question,
    side: m.side,              // "YES" or "NO"
    edge: m.edge,              // e.g. 0.08 = 8% edge
    aiProbability: m.ai_probability,
    marketPrice: m.market_price,
    confidence: m.confidence,  // "high", "medium", "low"
    yesPrice: m.yes_price,
    noPrice: m.no_price,
  }));
}

module.exports = { getEdges };
```

## wallet.js — Connect to Polygon

```
┌─────────────────────────────────────────────────┐
│              WALLET CONNECTION FLOW               │
│                                                  │
│  Seed Phrase ──► ethers.Wallet ──► Polygon RPC   │
│                                                  │
│  Then:                                           │
│  1. Read USDC balance (balanceOf)                │
│  2. Check exchange approval (allowance)          │
│  3. Approve if needed (approve → MaxUint256)     │
│                                                  │
│  Contracts:                                      │
│  ├── USDC:     0x2791Bca1f2de4661ED88A30C99A7a.. │
│  ├── Exchange: 0x4bFb41d5B3570DeFd03C39a9A4D8.. │
│  └── CTF:      0x4D97DCd97eC945f40cF65F87097A.. │
└─────────────────────────────────────────────────┘
```

```javascript
const { ethers } = require('ethers');

// Polygon mainnet
const RPC_ENDPOINTS = [
  'https://polygon-rpc.com',
  'https://rpc-mainnet.maticvigil.com',
  'https://polygon.llamarpc.com',
];

// Contract addresses
const USDC = '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174';
const EXCHANGE = '0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E';
const CTF = '0x4D97DCd97eC945f40cF65F87097ACe5EA0476045';

const ERC20_ABI = [
  'function balanceOf(address) view returns (uint256)',
  'function allowance(address,address) view returns (uint256)',
  'function approve(address,uint256) returns (bool)',
];

async function connectWallet() {
  // Try RPC endpoints until one works
  let provider;
  for (const rpc of RPC_ENDPOINTS) {
    try {
      const p = new ethers.JsonRpcProvider(rpc);
      await p.getBlockNumber(); // test connection
      provider = p;
      break;
    } catch {}
  }
  if (!provider) throw new Error('All Polygon RPCs failed');

  const wallet = ethers.Wallet.fromPhrase(process.env.SEED_PHRASE).connect(provider);
  const usdc = new ethers.Contract(USDC, ERC20_ABI, wallet);

  // Check balance
  const balance = await usdc.balanceOf(wallet.address);
  const balanceUSD = Number(balance) / 1e6;
  console.log(`Wallet: ${wallet.address}`);
  console.log(`USDC: $${balanceUSD.toFixed(2)}`);

  // Approve exchange if needed (one-time)
  const allowance = await usdc.allowance(wallet.address, EXCHANGE);
  if (Number(allowance) < 1000e6) {
    console.log('Approving USDC for Polymarket exchange...');
    const tx = await usdc.approve(EXCHANGE, ethers.MaxUint256);
    await tx.wait();
    console.log('Approved');
  }

  return { wallet, provider, usdc, balance: balanceUSD };
}

module.exports = { connectWallet };
```

## trader.js — Place Orders

```
┌─────────────────────────────────────────────────┐
│              ORDER PLACEMENT FLOW                │
│                                                  │
│  1. Resolve slug ──► Gamma API ──► token IDs     │
│                                                  │
│  2. Get price ──► CLOB midpoint ──► current c    │
│                                                  │
│  3. Calculate:                                   │
│     shares = amount / price                      │
│     e.g. $10 / 0.64 = 15.6 shares               │
│                                                  │
│  4. Place order ──► CLOB /order endpoint         │
│     (requires HMAC-SHA256 auth for production)   │
└─────────────────────────────────────────────────┘
```

```javascript
const axios = require('axios');

const GAMMA_URL = 'https://gamma-api.polymarket.com';
const CLOB_URL = 'https://clob.polymarket.com';

/**
 * Get the token ID for a market outcome.
 */
async function getTokenId(slug, side) {
  const { data } = await axios.get(`${GAMMA_URL}/events?slug=${slug}`);
  const market = data[0]?.markets?.[0];
  if (!market) throw new Error(`Market not found: ${slug}`);

  const tokenIds = JSON.parse(market.clobTokenIds || '[]');
  return side === 'YES' ? tokenIds[0] : tokenIds[1];
}

/**
 * Get current midpoint price for a token.
 */
async function getPrice(tokenId) {
  const { data } = await axios.get(`${CLOB_URL}/midpoint?token_id=${tokenId}`);
  return parseFloat(data.mid);
}

/**
 * Generate a trade signal. In production, replace with actual
 * CLOB order using HMAC-SHA256 authentication.
 *
 * See: https://docs.polymarket.com/#create-order
 */
async function placeBet(wallet, slug, side, amountUSD) {
  const tokenId = await getTokenId(slug, side);
  const price = await getPrice(tokenId);
  const shares = amountUSD / price;

  const signal = {
    action: 'BUY',
    slug,
    side,
    tokenId,
    price: price.toFixed(4),
    amount: amountUSD.toFixed(2),
    shares: shares.toFixed(2),
    wallet: wallet.address,
    timestamp: new Date().toISOString(),
  };

  console.log(`SIGNAL: ${JSON.stringify(signal)}`);
  return signal;
}

module.exports = { placeBet };
```

## bot.js — Main Entry Point

```
┌─────────────────────────────────────────────────┐
│              BOT LIFECYCLE                       │
│                                                  │
│  STARTUP                                         │
│  ├── Load .env config                            │
│  ├── Connect wallet to Polygon                   │
│  ├── Verify USDC balance                         │
│  └── Start poll loop                             │
│                                                  │
│  EACH POLL (every 15 min)                        │
│  ├── Check daily loss limit                      │
│  ├── Fetch edges from Truth Machine              │
│  ├── For each edge:                              │
│  │   ├── Skip if low confidence                  │
│  │   ├── Calculate Kelly bet size                 │
│  │   ├── Skip if bet < $1                        │
│  │   └── Place bet (or log signal)               │
│  └── Log session stats                           │
│                                                  │
│  SAFETY                                          │
│  ├── Quarter Kelly (never full)                   │
│  ├── Max $10 per bet                             │
│  ├── Daily $50 loss limit                        │
│  └── Skip low confidence markets                 │
└─────────────────────────────────────────────────┘
```

```javascript
require('dotenv').config();
const { getEdges } = require('./edge-detector');
const { connectWallet } = require('./wallet');
const { placeBet } = require('./trader');

const MIN_EDGE = parseFloat(process.env.MIN_EDGE || '0.05');
const MAX_BET = parseFloat(process.env.MAX_BET_USD || '10');
const INTERVAL = parseInt(process.env.POLL_INTERVAL || '900000');
const DAILY_LOSS_LIMIT = parseFloat(process.env.DAILY_LOSS_LIMIT || '50');

let dailyPnL = 0;
let tradesExecuted = 0;

async function run() {
  console.log('\n=== POLYMARKET EDGE BOT ===');
  console.log(`Min edge: ${(MIN_EDGE * 100).toFixed(0)}%`);
  console.log(`Max bet: $${MAX_BET}`);
  console.log(`Poll interval: ${INTERVAL / 60000} min`);
  console.log(`Daily loss limit: $${DAILY_LOSS_LIMIT}\n`);

  // Connect wallet
  const { wallet, balance } = await connectWallet();
  if (balance < MAX_BET) {
    console.log('ERROR: Insufficient USDC. Fund your wallet.');
    return;
  }

  async function poll() {
    console.log(`\n[${new Date().toISOString()}] Scanning...`);

    // Daily loss limit check
    if (dailyPnL < -DAILY_LOSS_LIMIT) {
      console.log(`Daily loss limit hit ($${Math.abs(dailyPnL).toFixed(2)}). Pausing.`);
      return;
    }

    try {
      const edges = await getEdges(MIN_EDGE, 5);
      console.log(`Found ${edges.length} markets with >${(MIN_EDGE * 100).toFixed(0)}% edge`);

      for (const m of edges) {
        // Skip low confidence
        if (m.confidence === 'low') continue;

        // Kelly criterion position sizing
        const p = m.aiProbability; // estimated true probability
        const price = m.side === 'YES' ? m.yesPrice : m.noPrice;
        const b = (1 / price) - 1; // decimal odds
        const kelly = (p * b - (1 - p)) / b;

        // Quarter Kelly for safety
        const betSize = Math.min(MAX_BET, Math.max(0, kelly * balance * 0.25));

        if (betSize < 1) continue; // Skip tiny bets

        console.log(`\n  ${m.side} "${m.question.slice(0, 50)}..."`);
        console.log(`    Edge: +${(m.edge * 100).toFixed(1)}% | AI: ${(m.aiProbability * 100).toFixed(0)}% | Market: ${(m.marketPrice * 100).toFixed(0)}%`);
        console.log(`    Kelly: ${(kelly * 100).toFixed(1)}% | Bet: $${betSize.toFixed(2)}`);

        const result = await placeBet(wallet, m.slug, m.side, betSize);
        tradesExecuted++;
      }
    } catch (err) {
      console.error('Error:', err.message);
    }

    console.log(`\nSession: ${tradesExecuted} trades | PnL: $${dailyPnL.toFixed(2)}`);
  }

  await poll();
  setInterval(poll, INTERVAL);
  console.log(`\nBot running. Ctrl+C to stop.`);
}

run().catch(console.error);
```

---

# Part 4: Running & Deploying

## Start Locally

```bash
node bot.js
```

Expected output:
```
=== POLYMARKET EDGE BOT ===
Min edge: 5%
Max bet: $10
Poll interval: 15 min
Daily loss limit: $50

Wallet: 0x742d35Cc6634C0532925a3b844Bc9...
USDC: $87.50

[2026-03-24T12:00:00Z] Scanning...
Found 3 markets with >5% edge

  NO "Will the US invade Cuba in 2026?..."
    Edge: +21.0% | AI: 4% | Market: 26%
    Kelly: 18.2% | Bet: $10.00
SIGNAL: {"action":"BUY","side":"NO",...}

Session: 1 trades | PnL: $0.00
Bot running. Ctrl+C to stop.
```

## Deploy 24/7 with PM2

```bash
npm install -g pm2
pm2 start bot.js --name polymarket-bot
pm2 save
pm2 startup
```

## Monitor

```bash
pm2 logs polymarket-bot    # View logs
pm2 monit                  # Dashboard
pm2 restart polymarket-bot # Restart
pm2 stop polymarket-bot    # Stop
```

---

# Part 5: Safety & Upgrades

## Risk Management

```
┌─────────────────────────────────────────────────┐
│              SAFETY CHECKLIST                    │
│                                                  │
│  ✓ Quarter Kelly sizing (never full Kelly)       │
│  ✓ $10 max per individual bet                   │
│  ✓ $50 daily loss limit (circuit breaker)        │
│  ✓ Skip low confidence markets                   │
│  ✓ 15-min poll interval (matches API cache)      │
│  ✓ Seed phrase in .env (never in code)           │
│  ✓ .gitignore excludes .env                      │
│                                                  │
│  NEVER:                                          │
│  ✗ Full Kelly (too aggressive)                   │
│  ✗ Set and forget (monitor daily)                │
│  ✗ All-in on one market                          │
│  ✗ Commit .env to git                            │
│  ✗ Share your seed phrase                        │
└─────────────────────────────────────────────────┘
```

## Upgrading to Live CLOB Trading

The MVP generates trade **signals**. To place actual orders:

```
┌─────────────────────────────────────────────────┐
│  UPGRADE PATH                                    │
│                                                  │
│  1. Get Polymarket CLOB API credentials          │
│     └── Apply at polymarket.com                  │
│                                                  │
│  2. Implement HMAC-SHA256 order signing           │
│     └── See docs.polymarket.com                  │
│                                                  │
│  3. Replace placeBet() signal with actual POST   │
│     └── POST /clob/order with signed headers     │
│                                                  │
│  4. Add order tracking + fill monitoring         │
│     └── Poll /clob/orders for status             │
│                                                  │
│  Python alternative:                             │
│     pip install py-clob-client                    │
│     (handles auth + signing automatically)       │
└─────────────────────────────────────────────────┘
```
