[TOC]

Polymarket is a prediction market where each question has only two possible outcomes — Yes or No, Up or Down, Win or Lose.
Take the BTC 15-minute price contract as an example. In each round, there are two contracts: Up and Down. The bet is whether BTC’s closing price after 15 minutes will be higher or lower than the opening price.

This market has an extremely special property:
The probabilities of the two outcomes must sum to 100%. Therefore, the price of Up plus the price of Down should theoretically equal exactly 1.

This property does not exist in ordinary futures markets. It provides us with a natural deterministic anchor.
Quantitative trading essentially does one thing: find relatively certain opportunities in an uncertain market.
The binary structure of Polymarket provides such an anchor:
No matter whether BTC goes up or down during these 15 minutes, either Up or Down must settle at $1.
Using this anchor, we do not need to predict direction. We only need to find a moment when the combined cost of buying Up and Down is less than 1. Once that condition is met, the profit is locked in at the moment of purchase.
However, most of the time the market keeps the sum tightly around 1. Direct arbitrage opportunities are rare.
So where do the opportunities come from?
Opportunities arise from price imbalance during market overreaction.
Imagine this scenario:
During a 15-minute round, BTC suddenly drops sharply. Panic spreads. The Up contract is heavily sold, and its price falls from 0.50 to 0.35.
In theory, if Up drops by 0.15, Down should rise by 0.15 so that their sum remains 1. But markets do not react symmetrically in real time. Up may have already crashed to 0.35, while Down is still moving upward from 0.50 toward 0.65 and hasn’t fully caught up.
In that transitional moment, the sum may temporarily be:

This is the arbitrage window.
You spend 0.93 to buy a combination that must pay out 1 dollar. The profit is already determined at the moment of purchase.
The original idea came from Twitter blogger @the_smart_ape. It is elegant and minimal, using only four parameters:
| Parameter | Meaning |
|---|---|
SHARES |
Position size per leg |
SUM_TARGET |
Maximum combined price threshold |
MOVE_PCT |
Dump trigger percentage |
WINDOW_MIN |
Monitoring window time |
Execution logic:
During the monitoring window at the start of each round, continuously track price.
If one side drops more than the trigger threshold → buy it as Leg 1.
Wait for the opposite side’s price to fall back.
When both legs’ total price falls below the target → buy Leg 2.
Hold until settlement and collect payout.
The idea is clean and efficient. But in live trading, several problems appear.
The original idea is passive:
Our improvement is different:
At the moment of the dump, there is a high probability that Up and Down move at different speeds. We act simultaneously during this dislocation.
Execution logic:
SUM_TARGET - leg1PriceAt the moment the orders are placed, the arbitrage space is already locked. We are no longer waiting for the market to meet our condition — we let the market come to us.
Core implementation:
function executeBothLegs(symbols, dumpSide, dumpAsk) {
var leg1Symbol = (dumpSide === "Up") ? symbols.up : symbols.down
var leg2Symbol = (dumpSide === "Up") ? symbols.down : symbols.up
leg1Price = _N(dumpAsk + SLIPPAGE, 4)
leg2Price = _N(SUM_TARGET - leg1Price, 4)
var goLeg1 = exchange.Go("CreateOrder", leg1Symbol, "buy", leg1Price, SHARES)
var goLeg2 = exchange.Go("CreateOrder", leg2Symbol, "buy", leg2Price, SHARES)
var id1 = goLeg1.wait()
var id2 = goLeg2.wait()
leg1OrderId = id1
leg2OrderId = id2
state = STATE.BOTH_PENDING
}
Reality often looks like this:
You are stuck holding a single-sided position that may go to zero at settlement.
A stop-loss is mandatory.
We introduce two parameters:
function handleLeg1OnlyRisk(symbols, upBid, downBid, isLastMin) {
var holdBid = (leg1Side === "Up") ? upBid : downBid
var profitLine = leg1EntryAsk * (1 + EARLY_TAKE_PROFIT)
var stopLine = leg1EntryAsk * (1 - LAST_MIN_STOP_LOSS)
var needClose = false
var reason = ""
if (holdBid <= FLOOR_PRICE) {
needClose = true; reason = "FLOOR_PRICE"
} else if (!isLastMin && holdBid >= profitLine) {
needClose = true; reason = "EARLY_TAKE_PROFIT"
} else if (isLastMin && holdBid <= stopLine) {
needClose = true; reason = "STOP_LOSS"
}
if (needClose) {
cancelAndConfirmUntilClear(leg2OrderId)
closePosition(holdSymbol, holdBid, reason)
}
}
This caps downside and locks upside.
Near settlement, market behavior changes dramatically.
There may not be enough time for mean reversion. Volatility often increases.
We introduce LAST_MIN_S:
When entering the final phase:
LAST_MIN_STOP_LOSSvar isLastMin = (remaining <= LAST_MIN_S)
if (isLastMin) {
Log("⏰ In the last minute, cancel the pending orders that have not been executed")
cancelAllPending("the last minute")
}
Early phase = wait for recovery Late phase = avoid going to zero
Polymarket is on-chain.
Order confirmation and position updates may take several seconds.
We add:
function placeOrderAndConfirm(symbol, side, price, amount) {
var orderId = exchange.CreateOrder(symbol, side, orderPrice, amount)
var deadline = Date.now() + ORDER_TIMEOUT_S * 1000
while (Date.now() < deadline) {
var order = exchange.GetOrder(orderId)
if (order && order.Status === 1) {
return { orderId: orderId, avgPrice: order.AvgPrice }
}
if (order && (order.Status === 2 || order.Status === 4)) {
return { orderId: orderId, avgPrice: null }
}
Sleep(1000)
}
exchange.CancelOrder(orderId)
}
This ensures execution reliability in a blockchain environment.
Polymarket has a special feature:
After settlement, funds are not automatically returned to balance.
You must manually call the Redeem function to unlock capital.
If forgotten, capital remains stuck.
The strategy automatically calls Redeem each round to ensure capital recycling.
function doRedeem() {
var positions = exchange.GetPositions()
for (var i = 0; i < positions.length; i++) {
var pos = positions[i]
if (pos.Info && pos.Info.redeemable) {
var result = exchange.IO("redeem", pos.Symbol, true)
Log("Redeem :", result)
}
}
}
if (!redeemDone && elapsed >= 840) {
doRedeem()
redeemDone = true
}
The strategy includes a real-time monitoring panel displaying:

This allows full visibility during live trading.

Example: A new round begins. Down drops 18.6% from 0.43 to 0.35.
Profit exceeded expectation.
Three Honest Limitations
No opportunity in calm markets. Arbitrage depends on overreaction. In stable markets, the strategy idles.
Single-leg exposure risk. If Leg 2 never fills, Leg 1 may hit stop loss. Stop parameters are a trade-off.
SUM_TARGET threshold trade-off. High threshold → frequent but small profits. Low threshold → rare but large profits.
This defines the strategy’s style.
Integrate External BTC Price Feed. Pause entries during strong trending markets.
Mathematical Modeling. Up and Down are essentially binary options. Option pricing theory could model:
Dynamic Parameter Adjustment. Current parameters are static. Volatility changes constantly. Adaptive parameters would significantly improve robustness.
The core idea — exploiting temporary mispricing in binary markets — is not limited to Polymarket BTC contracts. Any binary-structured market with short-term pricing inefficiencies can apply similar logic. What we built is merely a framework. The truly interesting work begins from here.
Strategy Source Code: Polymarket BTC 15-Minute Dual-Leg Hedged Arbitrage Bot (Two-Sided Hedge Version) https://www.fmz.com/strategy/529152