Fundamentals · 7 min read

How Chainlink RTDS settles 5/15-minute crypto binaries on Polymarket

How Chainlink Real-Time Data Streams drives settlement for short-dated crypto binaries, and the resolveViaGamma polling pattern used in trading bots.

What actually resolves a market

When a 5-minute BTC binary expires, Polymarket does not look at Binance. It calls a Chainlink oracle, specifically the Chainlink Real-Time Data Streams (RTDS) product, which aggregates prices across multiple exchanges and publishes a single signed price at the scheduled timestamp.

The binary question is typically "Will BTC be above $X at 14:05 UTC?" At 14:05:00 the Chainlink Data Streams report is finalized on-chain. A resolver contract reads that report, compares the price to the strike, and sets the winning outcome token to $1.00 and the losing one to $0.00.

This matters for bot design in two ways:

  1. You cannot snipe resolution by reacting faster than Binance -- Chainlink uses a median of many sources, so a single-exchange spike does not move it the same way.
  2. There is a delay between expiry and when the Gamma API reflects the settled outcome. Polling is required.

The Gamma API settlement endpoint

Polymarket exposes market state via its Gamma API. A resolved market has closed: true and one outcome token with price at or above 0.99. Bots should not trust a price of exactly 1.00 as confirmation -- the API sometimes returns 0.999 before it rounds up.

const GAMMA = 'https://gamma-api.polymarket.com'

async function resolveViaGamma(
  conditionId: string,
  timeoutMs = 300_000,
): Promise<'YES' | 'NO' | null> {
  const deadline = Date.now() + timeoutMs
  while (Date.now() < deadline) {
    const res = await fetch(`${GAMMA}/markets/${conditionId}`)
    const market = await res.json()
    if (market.closed) {
      for (const token of market.tokens) {
        if (parseFloat(token.price) >= 0.99) return token.outcome as 'YES' | 'NO'
      }
    }
    await sleep(4000)
  }
  return null // timeout -- treat as inconclusive
}

function sleep(ms: number) {
  return new Promise((r) => setTimeout(r, ms))
}

The conditionId is the market's Condition ID from the CLOB, not the token ID. You get it when you fetch the market metadata before entering a trade.

Why the poll interval matters

Chainlink RTDS typically finalizes within 30-60 seconds of the scheduled expiry. The Gamma API reflects it within another 10-30 seconds. In practice, bots see settlement 60-120 seconds after the candle closes.

Poll every 4-6 seconds, not every 100ms. Aggressive polling hammers the API and can get your IP rate-limited, which means you miss the settlement window entirely and your position sits unresolved longer than necessary.

Multi-source price vs single-exchange price

Chainlink aggregates from Binance, Coinbase, Kraken, and others. The exact weighting is not published, but the effect is that:

  • A flash wick on Binance alone does not resolve the market the way it would if Binance were the sole oracle.
  • Poly5m-v4 deliberately uses a multi-exchange median (Binance + Coinbase + Kraken) as its internal price signal for the same reason -- to avoid entering a trade based on a single-exchange deviation that Chainlink will not honour.
// Rough illustration of a multi-exchange median
function medianPrice(prices: number[]): number {
  const sorted = [...prices].sort((a, b) => a - b)
  const mid = Math.floor(sorted.length / 2)
  return sorted.length % 2 !== 0
    ? sorted[mid]
    : (sorted[mid - 1] + sorted[mid]) / 2
}

Near-resolution guard

All our bots skip markets where one outcome is already above $0.95 or below $0.05. This is because:

  1. Chainlink has already published a price that strongly favours one side.
  2. You have almost no edge -- the market is effectively resolved.
  3. Even if you are right, the profit is tiny relative to the fee.
function isNearResolved(tokens: Array<{ price: string }>): boolean {
  return tokens.some((t) => {
    const p = parseFloat(t.price)
    return p >= 0.95 || p <= 0.05
  })
}

Summary

  • Chainlink RTDS is the source of truth, not any single exchange price feed.
  • Use resolveViaGamma() with 4-6 second polling and a 5-minute timeout.
  • Mirror Chainlink's multi-source approach in your entry signals to avoid reacting to exchange-specific noise.
  • Skip near-resolved markets entirely.

Related bots