After the TWAP upgrade, I started testing a different approach for Polymarket's BTC 5-minute Up/Down markets.
The main thing I noticed was that short-term prices became much more active.
Instead of continuously taking the current price, I'm experimenting with placing limit orders on both outcomes and trying to capture temporary price movement.
This post explains the idea and the engineering behind it.
This is an experimental strategy, not a guaranteed-profit system.
The Basic Idea
A BTC 5-minute market has two outcomes:
UPDOWN
For example:
UP best ask = 0.60
DOWN best ask = 0.40
Instead of buying at the current ask, the bot places orders below it.
For example, with an 0.08 offset:
UP order = 0.60 - 0.08 = 0.52
DOWN order = 0.40 - 0.08 = 0.32
So the bot places:
BUY UP @ 0.52
BUY DOWN @ 0.32
The goal is to have both orders filled during short-term market movement.
Why Both Sides?
The interesting part is the combined position.
Suppose both orders fill:
UP = 0.52
DOWN = 0.32
Total = 0.84
If both outcomes are held through settlement, the combined position has a $1 settlement value.
So the basic calculation is:
combined_cost = up_price + down_price
potential_edge = 1.00 - combined_cost
In this example:
potential_edge = 1.00 - 0.84
= 0.16
This is only a gross theoretical edge. Real execution needs to account for fees, partial fills, timing, and other costs.
The Real Problem: Partial Fills
This is where the strategy gets interesting.
Suppose:
UP → FILLED @ 0.52
DOWN → NOT FILLED
Now the bot isn't holding a paired position.
It has directional exposure to UP.
Therefore, the bot needs to track inventory separately:
UP inventory
DOWN inventory
Paired inventory
Unpaired inventory
For example:
UP = 100
DOWN = 70
Paired = 70
Unpaired UP = 30
This distinction should be part of the position manager.
Dynamic Order Distance
I don't want to hardcode:
best_ask - 0.08
forever.
Market activity changes, so the order distance should be configurable and potentially dynamic.
A simple first version could use:
Low movement → smaller offset
Medium movement → medium offset
High movement → larger offset
The bot can measure short-term movement:
price_change_1s
price_change_3s
price_change_5s
and use that information when calculating the next order price.
The exact values should come from historical/live testing.
Chainlink / TWAP Data
The order book isn't the only input.
The bot also needs the reference price information:
Chainlink price
TWAP
UP price
DOWN price
Time remaining
A simplified flow is:
Chainlink / TWAP
↓
Market state
↓
Order book
↓
Strategy engine
↓
Limit orders
This helps the bot understand the market state instead of reacting only to the latest order-book update.
Order Lifecycle
The execution loop can be kept relatively simple.
1. Read market data
UP bid/ask
DOWN bid/ask
Chainlink/TWAP
time remaining
inventory
2. Calculate target prices
UP target
DOWN target
combined cost
potential edge
3. Submit orders
BUY UP
BUY DOWN
4. Monitor
Watch:
- order status
- fills
- price movement
- inventory
5. Reprice
If the market moves enough:
cancel old orders
calculate new prices
submit new orders
6. Manage inventory
If only one side fills, the risk manager decides what to do with the unpaired position.
Avoiding Stale Orders
Fast markets make stale orders dangerous.
For example:
UP ask = 0.60
Our bid = 0.52
A few seconds later:
UP ask = 0.72
The original order may no longer make sense relative to the current market state.
The bot therefore needs an order-refresh mechanism.
Conceptually:
if market_move > repricing_threshold:
cancel_order()
calculate_new_price()
submit_order()
The threshold should be tested rather than chosen arbitrarily.
Time Remaining
The same strategy behaves differently depending on how much time is left in the market.
For example:
4 minutes remaining
gives the bot considerably more time to complete a pair than:
10 seconds remaining
So time_remaining should be a first-class strategy variable.
A simple approach:
More time
→ normal quoting
Less time
→ reduce new exposure
Very close to settlement
→ strict inventory controls
Architecture
For the implementation, I would separate the system into several components:
┌─────────────────────┐
│ Market Data │
│ │
│ Order Book │
│ Chainlink / TWAP │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Strategy Engine │
│ │
│ Price calculation │
│ Volatility │
│ Pair calculation │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Order Manager │
│ │
│ Place │
│ Cancel │
│ Reprice │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Position Manager │
│ │
│ UP inventory │
│ DOWN inventory │
│ Paired inventory │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Risk Manager │
└─────────────────────┘
Keeping these components separate makes it much easier to test the strategy without connecting the execution layer immediately.
Start With a Simulator
Before running this with real capital, I would first replay live order-book data.
For every market update, store:
timestamp
Chainlink price
TWAP
time remaining
UP bid
UP ask
DOWN bid
DOWN ask
simulated UP order
simulated DOWN order
UP fill
DOWN fill
Then test different offsets:
0.05
0.06
0.07
0.08
0.09
0.10
The important metrics are:
Two-sided fill rate
One-sided fill rate
Average paired cost
Average time to pair
Maximum unpaired inventory
This is more useful than looking only at total P&L.
The Core Calculation
The first version of the strategy can be reduced to:
up_target = up_best_ask - offset
down_target = down_best_ask - offset
combined_cost = up_target + down_target
potential_edge = 1.0 - combined_cost
But the execution engine should not automatically trade just because potential_edge is positive.
It also needs to consider:
fill probability
time remaining
inventory
market movement
fees
order-book liquidity
The goal is not simply to find a cheap theoretical pair.
The goal is to determine whether the pair can actually be executed.
What I Want to Measure
The main questions I'm testing are:
- How often do both orders fill?
- How often does only one side fill?
- How long does it take to complete a pair?
- Which offset gives the best fill behavior?
- How does TWAP movement affect fills?
- How does the strategy behave near settlement?
- How much unpaired inventory accumulates?
These measurements should tell us whether the strategy is actually viable as an execution system.
Final Thoughts
The interesting part of this strategy isn't simply:
"Buy both sides."
The difficult engineering problem is managing everything that happens between the two fills.
Market data
↓
Calculate quote
↓
Place both orders
↓
One order fills
↓
Track exposure
↓
Wait / reprice / manage
↓
Second order fills
↓
Paired position
That's the part I'm currently experimenting with after the TWAP upgrade.
I'll be testing the strategy using live market data first, then refining the execution and risk-management logic based on actual fill behavior.
Resources
I'm documenting my Polymarket bot research and experiments here:
GitHub:
https://github.com/Benjam1nCup/Polymarket-trading-bot-python-V2
The repository contains educational material, strategy research, and implementation experiments around automated Polymarket trading.
Contact:
https://telegram.me/BenjaminCup
If you're interested in discussing Polymarket bot development, trading infrastructure, or strategy research, feel free to reach out.
Top comments (0)