A developer shares how to build a Polymarket trading bot that captures price fluctuations in the BTC 15-minute market, turning $1,000 into $1,869 within a few days, with a backtested return of 86%. The article details the bot’s construction logic, backtesting methods, and limitations.
(Background: Leading prediction market Polymarket announces its own L2, is Polygon’s flagship gone?)
(Additional context: How to achieve an annualized 40% yield through Polymarket arbitrage?)
Table of Contents
Bot Construction Logic
Backtesting
Limitations of Backtesting
Infrastructure
A few weeks ago, I decided to build my own Polymarket bot. The full version took me several weeks to develop.
I was willing to invest this effort because inefficiencies do exist on Polymarket. Although some bots are already exploiting these inefficiencies for profit, it’s still far from enough—there are more opportunities than there are bots in this market.
Bot Construction Logic
The bot’s logic is based on a set of strategies I previously executed manually. To improve efficiency, I automated it. The bot runs on the “BTC 15-minute UP/DOWN” market.
It runs a real-time monitoring program that automatically switches to the current BTC 15-minute round, streams the best bid/ask prices via WebSocket, displays a fixed terminal UI, and allows full control through text commands.
In manual mode, you can place orders directly.
buy up / buy down : buy a specific dollar amount.
buyshares up / buyshares down : purchase an exact number of shares, using user-friendly LIMIT (limit order) + GTC (good-till-canceled) orders, executed at the current best ask price.
In automatic mode, it runs a repeating two-leg cycle.
First, it only observes price movements within windowMin minutes after each round starts. If either side drops fast enough (a decline of at least movePct within about 3 seconds), it triggers “Leg 1,” buying the side that crashes.
After completing Leg 1, the bot will never buy the same side again. It waits for “Leg 2 (hedge)” and only triggers when the following condition is met: leg1EntryPrice + oppositeAsk <= sumTarget.
When this condition is satisfied, it buys the opposite side. After Leg 2 completes, the cycle ends, and the bot returns to observing state, waiting for the next crash signal with the same parameters.
If the round changes during the cycle, the bot abandons that open cycle and restarts with the same settings in the next round.
The parameters for auto mode are set as follows: auto on [sum=0.95] [move=0.15] [windowMin=2]
windowMin: duration in minutes from each round start to allow Leg 1 execution.
Backtesting
The bot’s logic is simple: wait for a violent sell-off, buy the side that just dropped, then wait for the price to stabilize and hedge by buying the opposite side, ensuring: priceUP + priceDOWN < 1.
But this logic needs testing. Is it truly effective over the long term? More importantly, the bot has many parameters (shares, sum, movePct, windowMin). Which combination is optimal and maximizes profit?
My first idea was to run the bot live for a week and observe results. The problem is that this takes too long and only tests one parameter set at a time, while I need to test many.
My second idea was to use historical data from the Polymarket CLOB API for backtesting. Unfortunately, for the BTC 15-minute UP/DOWN market, the historical data endpoint always returns an empty dataset. Without historical ticks, I cannot detect “drop within about 3 seconds,” so Leg 1 can’t be triggered regardless of parameters, resulting in zero cycles and 0% ROI.
Further investigation revealed that other users also encountered the same issue when retrieving historical data for certain markets. I tested markets that do return historical data and concluded: for this specific market, historical data is not retained at all.
Due to this limitation, the only reliable way to backtest this strategy is to record real-time best ask prices during bot operation to create my own historical dataset.
The recorder snapshots data to disk, including:
Timestamp
Round slug
Seconds remaining
UP/DOWN token ID
UP/DOWN best ask price
Later, the “recorded backtest” replays these snapshots and deterministically applies the same auto logic. This ensures high-frequency data needed to detect crashes and hedge conditions.
Over 4 days, I collected 6 GB of data. I could record more, but I believe this is sufficient to test different parameter sets.
I started testing this parameter set:
Initial balance: $1,000
20 shares per trade
sumTarget = 0.95
Crash threshold = 15%
windowMin = 2 minutes
I also applied a constant 0.5% fee rate and 2% spread to keep the scenario conservative.
Backtest results showed an ROI of 86%, turning $1,000 into $1,869 in just a few days.
Then I tested a more aggressive parameter set:
Initial balance: $1,000
20 shares per trade
sumTarget = 0.6
Crash threshold = 1%
windowMin = 15 minutes
Result: after 2 days, ROI was -50%.
This clearly shows that parameter choice is crucial. It can make you a lot of money or lead to significant losses.
Limitations of Backtesting
Even with fees and spreads included, backtesting has its limitations:
First, it only uses a few days of data, which may not provide a comprehensive market view.
It relies on recorded best ask snapshots; in reality, orders may partially fill or execute at different prices. Order book depth and available volume are not modeled.
It does not capture sub-second micro-movements (data sampled once per second). While timestamps are per second, many events can happen between seconds.
Slippage is fixed in backtest; it does not simulate variable delays (e.g., 200–1500 ms) or network peaks.
Each trade is assumed to execute “instantaneously” (no order queuing, no pending orders).
Fees are uniformly applied, whereas in reality, fees depend on market/token, maker/taker, fee tiers, or conditions.
To stay conservative, I applied a rule: if Leg 2 does not execute before market close, Leg 1 is considered a total loss.
This is deliberately cautious but not always realistic:
Sometimes Leg 1 can close early,
Sometimes it ends ITM and wins,
Sometimes losses are partial rather than total.
While losses may be overestimated, this provides a practical “worst-case” scenario.
Most importantly, backtesting cannot simulate the impact of your large orders on the order book or how other traders might react to your presence. In reality, your orders can:
Disrupt the order book,
Attract or repel other traders,
Cause nonlinear slippage.
Backtesting assumes you are a pure liquidity taker, with no impact.
Finally, it does not simulate rate limits, API errors, order rejections, pauses, timeouts, reconnections, or missed signals due to bot busy-ness.
Backtesting is extremely valuable for identifying good parameter ranges, but it’s not 100% guaranteed, as some real-world effects cannot be modeled.
Infrastructure
I plan to run the bot on a Raspberry Pi to avoid consuming my main machine’s resources and to keep it running 24/7.
However, there is still significant room for improvement:
Using Rust instead of JavaScript will provide much better performance and handling times.
Running a dedicated Polygon RPC node will further reduce latency.
Deploying on a VPS close to Polymarket’s servers will also significantly lower latency.
There are surely other optimizations I have yet to discover. Currently, I am learning Rust because it is becoming an indispensable language in Web3 development.
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
I built a bot to "earn passive income" on Polymarket: Here's my setup logic
A developer shares how to build a Polymarket trading bot that captures price fluctuations in the BTC 15-minute market, turning $1,000 into $1,869 within a few days, with a backtested return of 86%. The article details the bot’s construction logic, backtesting methods, and limitations.
(Background: Leading prediction market Polymarket announces its own L2, is Polygon’s flagship gone?)
(Additional context: How to achieve an annualized 40% yield through Polymarket arbitrage?)
Table of Contents
A few weeks ago, I decided to build my own Polymarket bot. The full version took me several weeks to develop.
I was willing to invest this effort because inefficiencies do exist on Polymarket. Although some bots are already exploiting these inefficiencies for profit, it’s still far from enough—there are more opportunities than there are bots in this market.
Bot Construction Logic
The bot’s logic is based on a set of strategies I previously executed manually. To improve efficiency, I automated it. The bot runs on the “BTC 15-minute UP/DOWN” market.
It runs a real-time monitoring program that automatically switches to the current BTC 15-minute round, streams the best bid/ask prices via WebSocket, displays a fixed terminal UI, and allows full control through text commands.
In manual mode, you can place orders directly.
buy up / buy down : buy a specific dollar amount.
buyshares up / buyshares down : purchase an exact number of shares, using user-friendly LIMIT (limit order) + GTC (good-till-canceled) orders, executed at the current best ask price.
In automatic mode, it runs a repeating two-leg cycle.
First, it only observes price movements within windowMin minutes after each round starts. If either side drops fast enough (a decline of at least movePct within about 3 seconds), it triggers “Leg 1,” buying the side that crashes.
After completing Leg 1, the bot will never buy the same side again. It waits for “Leg 2 (hedge)” and only triggers when the following condition is met: leg1EntryPrice + oppositeAsk <= sumTarget.
When this condition is satisfied, it buys the opposite side. After Leg 2 completes, the cycle ends, and the bot returns to observing state, waiting for the next crash signal with the same parameters.
If the round changes during the cycle, the bot abandons that open cycle and restarts with the same settings in the next round.
The parameters for auto mode are set as follows: auto on [sum=0.95] [move=0.15] [windowMin=2]
Backtesting
The bot’s logic is simple: wait for a violent sell-off, buy the side that just dropped, then wait for the price to stabilize and hedge by buying the opposite side, ensuring: priceUP + priceDOWN < 1.
But this logic needs testing. Is it truly effective over the long term? More importantly, the bot has many parameters (shares, sum, movePct, windowMin). Which combination is optimal and maximizes profit?
My first idea was to run the bot live for a week and observe results. The problem is that this takes too long and only tests one parameter set at a time, while I need to test many.
My second idea was to use historical data from the Polymarket CLOB API for backtesting. Unfortunately, for the BTC 15-minute UP/DOWN market, the historical data endpoint always returns an empty dataset. Without historical ticks, I cannot detect “drop within about 3 seconds,” so Leg 1 can’t be triggered regardless of parameters, resulting in zero cycles and 0% ROI.
Further investigation revealed that other users also encountered the same issue when retrieving historical data for certain markets. I tested markets that do return historical data and concluded: for this specific market, historical data is not retained at all.
Due to this limitation, the only reliable way to backtest this strategy is to record real-time best ask prices during bot operation to create my own historical dataset.
The recorder snapshots data to disk, including:
Later, the “recorded backtest” replays these snapshots and deterministically applies the same auto logic. This ensures high-frequency data needed to detect crashes and hedge conditions.
Over 4 days, I collected 6 GB of data. I could record more, but I believe this is sufficient to test different parameter sets.
I started testing this parameter set:
I also applied a constant 0.5% fee rate and 2% spread to keep the scenario conservative.
Backtest results showed an ROI of 86%, turning $1,000 into $1,869 in just a few days.
Then I tested a more aggressive parameter set:
Result: after 2 days, ROI was -50%.
This clearly shows that parameter choice is crucial. It can make you a lot of money or lead to significant losses.
Limitations of Backtesting
Even with fees and spreads included, backtesting has its limitations:
To stay conservative, I applied a rule: if Leg 2 does not execute before market close, Leg 1 is considered a total loss.
This is deliberately cautious but not always realistic:
While losses may be overestimated, this provides a practical “worst-case” scenario.
Most importantly, backtesting cannot simulate the impact of your large orders on the order book or how other traders might react to your presence. In reality, your orders can:
Backtesting assumes you are a pure liquidity taker, with no impact.
Finally, it does not simulate rate limits, API errors, order rejections, pauses, timeouts, reconnections, or missed signals due to bot busy-ness.
Backtesting is extremely valuable for identifying good parameter ranges, but it’s not 100% guaranteed, as some real-world effects cannot be modeled.
Infrastructure
I plan to run the bot on a Raspberry Pi to avoid consuming my main machine’s resources and to keep it running 24/7.
However, there is still significant room for improvement:
There are surely other optimizations I have yet to discover. Currently, I am learning Rust because it is becoming an indispensable language in Web3 development.
#####