1
0 Comments

I Built a TWAP-Based Mean-Reversion Bot for Polymarket Crypto Markets

I've been experimenting with automated strategies for Polymarket's short-duration crypto markets, and one idea I've been testing is TWAP-based mean reversion.

The interesting part isn't simply betting against BTC momentum.

It's comparing the current Polymarket price with a probability model built around the 60-second TWAP (twap_60s).

The idea

Imagine BTC suddenly moves higher:

BTC:       $100,000 → $101,000

UP price:  $0.50 → $0.64

The market now prices UP around 64%.

But our model estimates:

Model probability: 55%
Market price:      64%

Difference:         9%

Instead of blindly chasing the move, the bot asks:

Is UP overpriced, and does DOWN now offer enough edge?

The basic flow is:

BTC
 ↓
twap_60s
 ↓
Probability Model
 ↓
Polymarket Order Book
 ↓
Edge Calculation
 ↓
Risk Check
 ↓
Trade

Getting the 60-second TWAP

For this strategy, I'm specifically using twap_60s.

Polymarket provides Chainlink-computed TWAP data through its real-time infrastructure. The 60-second value represents a 60-second lookback window. ([Indie Hackers][1])

A simplified Python subscription:

import asyncio

from polymarket import AsyncPublicClient
from polymarket.streams import CryptoPricesChainlinkTwapSpec


async def main():

    async with AsyncPublicClient() as client:

        async with await client.subscribe(
            CryptoPricesChainlinkTwapSpec(
                window_seconds=60,
                symbols=["btc/usd"],
            )
        ) as stream:

            async for event in stream:
                print(event.payload.value)


asyncio.run(main())

The important parameter is:

window_seconds=60

I prefer consuming the Chainlink-computed value rather than trying to recreate the TWAP from exchange candles.

You can find the current implementation details in the Polymarket documentation.

Turning TWAP Into a Signal

One simple feature is the distance between BTC and the 60-second TWAP:

def twap_distance(btc_price, twap_60s):
    return (btc_price - twap_60s) / twap_60s

For example:

BTC:       $101,000
TWAP:      $100,500

Distance:  +0.50%

This isn't automatically a sell signal.

It's an input to a probability model.

A simple prototype could look like:

import math

def sigmoid(x):
    return 1 / (1 + math.exp(-x))


def estimate_probability(
    price_distance,
    twap_distance
):
    score = (
        5 * price_distance
        + 8 * twap_distance
    )

    return sigmoid(score)

The coefficients are just examples. In a real system, I'd train and calibrate the model using historical data.

Comparing Probability With the Market

Suppose the model gives:

P(UP) = 55%
UP Ask = $0.64

The theoretical edge is:

edge = 0.55 - 0.64

Which gives:

-9%

So the model doesn't want to buy UP.

Now:

P(DOWN) = 45%
DOWN Ask = $0.38

Then:

45% - 38% = +7%

That gives us a potential DOWN trade.

But I wouldn't trade purely on that number.

Execution Matters

One lesson I've learned while building trading bots is that the displayed price isn't necessarily the price you can execute at.

For example:

DOWN bid = $0.37
DOWN ask = $0.39

If we're taking liquidity, $0.39 matters more than the $0.38 midpoint.

So the real calculation should be:

Model Probability
        ↓
Executable Price
        ↓
Fees + Slippage
        ↓
Net Edge

Then apply risk controls.

What I Would Filter

Before sending an order, the bot should check:

  • Is twap_60s fresh?
  • Is the order book fresh?
  • Is there enough liquidity?
  • Is the spread reasonable?
  • Is BTC volatility too high?
  • How much time is left?
  • Is the position within limits?

For example:

def should_trade(
    edge,
    volatility,
    time_remaining
):

    if edge < 0.03:
        return False

    if volatility > 0.08:
        return False

    if time_remaining < 15:
        return False

    return True

The exact thresholds should come from backtesting, not guesswork.

What I Want to Test

The next important step isn't adding more indicators.

It's collecting enough historical data to answer:

Does the TWAP-based probability model actually produce an edge after execution costs?

I'd collect:

BTC price
twap_60s
Polymarket bid/ask
Time remaining
Model probability
Final outcome

Then evaluate:

  • Win rate
  • Average edge
  • PnL
  • Drawdown
  • Slippage
  • Calibration

That's where I think the real work is.

Why I Find This Interesting

The strategy isn't simply:

BTC goes up → buy DOWN.

It's:

BTC movement
     ↓
60-second TWAP
     ↓
Probability estimate
     ↓
Polymarket price
     ↓
Executable edge
     ↓
Risk management
     ↓
Trade

That distinction is important.

TWAP isn't the trading signal by itself.

It's a reference input that can help a model determine whether the current prediction-market price looks reasonable.

I've been building this type of infrastructure in my Polymarket Trading bot Python V2 repository.

For the underlying market infrastructure, the official Polymarket documentation is the best place to start.

Educational only — not financial advice.

on August 14, 2026