Skip to main content
Trading on Polymarket involves a complete workflow from setting up authentication to executing trades and managing balances. This guide covers the essential concepts and practical steps for successful trading.

Trading Workflow

1

Setup and Authentication

Initialize the CLOB client with authentication credentials.
import { ClobClient, SignatureType } from "@polymarket/clob-client";
import { Wallet } from "ethers";

const wallet = new Wallet(process.env.PRIVATE_KEY!);
const host = "https://clob.polymarket.com";

// Create or derive API credentials
const tempClient = new ClobClient(host, 137, wallet);
const creds = await tempClient.createOrDeriveApiKey();

// Initialize authenticated client
const client = new ClobClient(
  host,
  137,
  wallet,
  creds,
  SignatureType.EOA,
  undefined,
  undefined,
  true  // useServerTime
);
2

Check Balances and Allowances

Verify you have sufficient USDC and token allowances.
import { AssetType } from "@polymarket/clob-client";

// Check USDC balance
const collateral = await client.getBalanceAllowance({
  asset_type: AssetType.COLLATERAL,
});

console.log(`USDC Balance: ${collateral.balance}`);
console.log(`USDC Allowance: ${collateral.allowance}`);

// Update allowance if needed
if (parseFloat(collateral.allowance) < parseFloat(collateral.balance)) {
  await client.updateBalanceAllowance({
    asset_type: AssetType.COLLATERAL,
  });
}
3

Select Market and Get Configuration

Choose a market and retrieve its trading parameters.
const tokenID = "71321045679252212594626385532706912750332728571942532289631379312455583992563";

// Get market configuration
const tickSize = await client.getTickSize(tokenID);
const negRisk = await client.getNegRisk(tokenID);
const feeRate = await client.getFeeRateBps(tokenID);

// Analyze current market
const book = await client.getOrderBook(tokenID);
const midpoint = await client.getMidpoint(tokenID);
4

Create and Submit Order

Build your order and submit it to the CLOB.
import { Side, OrderType } from "@polymarket/clob-client";

const response = await client.createAndPostOrder(
  {
    tokenID,
    price: 0.65,
    size: 100,
    side: Side.BUY,
  },
  { tickSize, negRisk },
  OrderType.GTC
);

console.log(`Order ID: ${response.orderID}`);
console.log(`Status: ${response.status}`);
5

Monitor and Manage

Track your orders and manage your positions.
// Get open orders
const openOrders = await client.getOpenOrders({ asset_id: tokenID });

// Get trade history
const trades = await client.getTrades({ asset_id: tokenID });

// Cancel if needed
if (openOrders.length > 0) {
  await client.cancelOrder({ orderID: openOrders[0].id });
}

Order Matching

The CLOB uses a price-time priority matching engine:

Matching Priority

  1. Price Priority: Best prices match first
    • BUY orders: Highest price first
    • SELL orders: Lowest price first
  2. Time Priority: Among orders at the same price, earlier orders match first

Matching Scenarios

Order matches against existing liquidity in the book.
// Market has asks at 0.66
// You place buy order at 0.67
const response = await client.createAndPostOrder(
  {
    tokenID: "123...",
    price: 0.67,  // Crosses the spread
    size: 100,
    side: Side.BUY,
  },
  { tickSize: "0.01" },
  OrderType.GTC
);

// Result: Immediately matched at 0.66 (better price for you)
console.log(response.status); // "MATCHED"

Fees

Fee Structure

Fees are charged in basis points (bps) where 1 bps = 0.01%:
// Example: Market with 100 bps (1%) fee
const feeRateBps = await client.getFeeRateBps(tokenID);
console.log(feeRateBps); // 100

// Fee calculation for a trade
const price = 0.65;
const size = 100;
const proceeds = price * size; // 65 USDC
const fee = proceeds * (feeRateBps / 10000); // 65 * 0.01 = 0.65 USDC
const net = proceeds - fee; // 64.35 USDC received

Maker vs Taker Fees

Maker Orders

Orders that add liquidity to the book. Generally receive better fee rates or rebates.Examples:
  • GTC orders that rest in the book
  • Post-only orders
  • Limit orders away from current price

Taker Orders

Orders that remove liquidity from the book. Pay standard fee rates.Examples:
  • Market orders (FOK/FAK)
  • Limit orders that immediately match
  • Aggressive orders crossing the spread

Fee Application

// Fees are automatically applied by the SDK
const order = await client.createOrder(
  {
    tokenID,
    price: 0.65,
    size: 100,
    side: Side.BUY,
    // feeRateBps is auto-populated from market
  },
  { tickSize: "0.01" }
);

// Manual fee specification (must match market rate)
const orderWithFee = await client.createOrder(
  {
    tokenID,
    price: 0.65,
    size: 100,
    side: Side.BUY,
    feeRateBps: 100,  // Must match market fee rate
  },
  { tickSize: "0.01" }
);
If you specify a feeRateBps that doesn’t match the market’s fee rate, order creation will fail.

Balance Management

Checking Balances

import { AssetType } from "@polymarket/clob-client";

// USDC (collateral) balance
const usdc = await client.getBalanceAllowance({
  asset_type: AssetType.COLLATERAL,
});

console.log(`Balance: ${usdc.balance}`);
console.log(`Allowance: ${usdc.allowance}`);

// Conditional token balance
const yesToken = await client.getBalanceAllowance({
  asset_type: AssetType.CONDITIONAL,
  token_id: "71321045679252212594626385532706912750332728571942532289631379312455583992563",
});

console.log(`YES tokens: ${yesToken.balance}`);

Managing Allowances

Allowances control how much the CLOB can spend on your behalf:
// Check current allowance
const balance = await client.getBalanceAllowance({
  asset_type: AssetType.COLLATERAL,
});

if (parseFloat(balance.allowance) === 0) {
  console.log("No allowance set - updating...");
  
  // Update allowance to maximum
  await client.updateBalanceAllowance({
    asset_type: AssetType.COLLATERAL,
  });
  
  console.log("Allowance updated!");
}

// Update token allowance
await client.updateBalanceAllowance({
  asset_type: AssetType.CONDITIONAL,
  token_id: "71321045679252212594626385532706912750332728571942532289631379312455583992563",
});
You need to set allowances before trading. This is a one-time operation per asset unless you want to revoke access.

Trading Strategies

Limit Order Strategy

import { Side, OrderType } from "@polymarket/clob-client";

// Place buy orders at multiple price levels
const buyLevels = [0.60, 0.62, 0.64];

for (const price of buyLevels) {
  const order = await client.createOrder(
    {
      tokenID,
      price,
      size: 50,
      side: Side.BUY,
    },
    { tickSize: "0.01" }
  );
  
  await client.postOrder(order, OrderType.GTC);
  console.log(`Buy order placed at ${price}`);
}

Market Order Strategy

// Calculate market price before execution
const marketPrice = await client.calculateMarketPrice(
  tokenID,
  Side.BUY,
  100,  // Amount in USDC for BUY
  OrderType.FOK
);

console.log(`Market price for 100 USDC: ${marketPrice}`);

// Execute if price is acceptable
if (marketPrice <= 0.70) {
  const response = await client.createAndPostMarketOrder(
    {
      tokenID,
      amount: 100,
      side: Side.BUY,
      orderType: OrderType.FOK,
    },
    { tickSize: "0.01" },
    OrderType.FOK
  );
  
  console.log(`Executed at: ${response.status}`);
}

Post-Only Strategy (Maker Only)

// Ensure you only add liquidity (never take)
const order = await client.createOrder(
  {
    tokenID,
    price: 0.65,
    size: 100,
    side: Side.BUY,
  },
  { tickSize: "0.01" }
);

// Post-only flag ensures rejection if order would immediately match
await client.postOrder(
  order,
  OrderType.GTC,
  false,  // deferExec
  true    // postOnly - ensures maker fees only
);

Trading History

Query Trades

// Get all trades
const allTrades = await client.getTrades();

// Filter trades by market
const marketTrades = await client.getTrades({
  market: "0x1234...",  // condition ID
});

// Filter trades by token
const tokenTrades = await client.getTrades({
  asset_id: "71321045679252212594626385532706912750332728571942532289631379312455583992563",
});

// Filter trades by time range
const recentTrades = await client.getTrades({
  after: "2024-01-01T00:00:00Z",
  before: "2024-01-31T23:59:59Z",
});

// Paginated trades
const { trades, next_cursor } = await client.getTradesPaginated({
  asset_id: tokenID,
});

Trade Information

interface Trade {
    id: string;
    taker_order_id: string;
    market: string;              // Condition ID
    asset_id: string;            // Token ID
    side: Side;                  // BUY or SELL
    size: string;                // Trade size
    fee_rate_bps: string;        // Fee rate applied
    price: string;               // Execution price
    status: string;              // Trade status
    match_time: string;          // When trade occurred
    outcome: string;             // "Yes" or "No"
    owner: string;               // Your address
    maker_address: string;       // Counterparty address
    transaction_hash: string;    // Onchain settlement tx
    trader_side: "TAKER" | "MAKER";  // Your role
}

Risk Management

Order Cancellation Strategies

// Cancel all orders (emergency stop)
await client.cancelAll();

// Cancel orders for specific market
await client.cancelMarketOrders({
  market: "0x1234...",
});

// Cancel stale orders (older than 1 hour)
const openOrders = await client.getOpenOrders();
const oneHourAgo = Date.now() - 3600000;

for (const order of openOrders) {
  if (order.created_at < oneHourAgo) {
    await client.cancelOrder({ orderID: order.id });
    console.log(`Cancelled stale order: ${order.id}`);
  }
}

Heartbeat Mechanism

Maintain a heartbeat to prevent automatic order cancellation:
// Start heartbeat (send every 5-8 seconds)
let heartbeatId: string | null = null;

const sendHeartbeat = async () => {
  const response = await client.postHeartbeat(heartbeatId);
  heartbeatId = response.heartbeat_id;
  console.log(`Heartbeat sent: ${heartbeatId}`);
};

// Initial heartbeat
await sendHeartbeat();

// Continue sending (every 5 seconds)
const interval = setInterval(sendHeartbeat, 5000);

// If heartbeat stops for >10 seconds, all orders are cancelled
// Stop heartbeat when done
clearInterval(interval);
If you start sending heartbeats and then stop, all your orders will be automatically cancelled after 10 seconds.

Example: Complete Trading Session

import { ClobClient, Side, OrderType, AssetType, SignatureType } from "@polymarket/clob-client";
import { Wallet } from "ethers";

async function tradingSession() {
  // 1. Setup
  const wallet = new Wallet(process.env.PRIVATE_KEY!);
  const tempClient = new ClobClient("https://clob.polymarket.com", 137, wallet);
  const creds = await tempClient.createOrDeriveApiKey();
  
  const client = new ClobClient(
    "https://clob.polymarket.com",
    137,
    wallet,
    creds,
    SignatureType.EOA,
    undefined,
    undefined,
    true
  );

  // 2. Check balance
  const balance = await client.getBalanceAllowance({
    asset_type: AssetType.COLLATERAL,
  });
  
  console.log(`USDC Balance: ${balance.balance}`);
  
  if (parseFloat(balance.balance) < 100) {
    throw new Error("Insufficient balance");
  }

  // 3. Select market
  const tokenID = "71321045679252212594626385532706912750332728571942532289631379312455583992563";
  const tickSize = await client.getTickSize(tokenID);
  const negRisk = await client.getNegRisk(tokenID);

  // 4. Analyze market
  const book = await client.getOrderBook(tokenID);
  const midpoint = await client.getMidpoint(tokenID);
  
  console.log(`Best bid: ${book.bids[0]?.price}`);
  console.log(`Best ask: ${book.asks[0]?.price}`);
  console.log(`Midpoint: ${midpoint.mid}`);

  // 5. Place order
  const targetPrice = parseFloat(midpoint.mid) - 0.02; // Buy below midpoint
  
  const response = await client.createAndPostOrder(
    {
      tokenID,
      price: targetPrice,
      size: 50,
      side: Side.BUY,
    },
    { tickSize, negRisk },
    OrderType.GTC
  );

  console.log(`Order placed: ${response.orderID}`);
  console.log(`Status: ${response.status}`);

  // 6. Monitor
  const openOrders = await client.getOpenOrders({ asset_id: tokenID });
  console.log(`Open orders: ${openOrders.length}`);

  // 7. Cleanup (if needed)
  // await client.cancelMarketOrders({ asset_id: tokenID });
}

tradingSession().catch(console.error);

Best Practices

Use Server Time

Enable useServerTime: true to avoid timestamp-related errors due to clock skew.

Check Configuration

Always fetch tick size, neg-risk, and fee rate before creating orders.

Monitor Balances

Regularly check balances and allowances to ensure trades can execute.

Handle Errors

Implement proper error handling for failed orders and network issues.

Use Batch Operations

Use postOrders() to submit multiple orders efficiently.

Track Order IDs

Store order IDs returned from submissions for later management.

Orders

Deep dive into order types and lifecycle

Markets

Understanding market structure and data

Authentication

Setting up secure API access

Trading API

Complete API reference for trading methods

Build docs developers (and LLMs) love