Polymarket Bot Tutorial · Chapter 10 of 32
Polymarket order types explained for bot builders: Fill-or-Kill (FOK), Fill-and-Kill (FAK), Good-til-Cancelled (GTC), and limit-vs-market trade-offs. With production-grade decision rules.
What this chapter covers
Order-type confusion is the single most expensive class of bug for new bot builders. Sending FOK when GTC was needed produces missed entries; sending GTC when FOK was needed leaves resting orders that fill at terrible prices hours later. This chapter is the decision tree and the production defaults that have held up across thousands of orders.
- Quick decision tree
- FOK: when you must fill or skip
- FAK: when partials are acceptable
- GTC: when you want to rest on the book
- Limit vs market and the spread tax
- Our production defaults (FOK buys, GTC sells)
- Code: place each order type
Quick decision tree
Three questions decide every order placement.
- Do you need a guaranteed fill right now, and not at all if you cannot get it now? → FOK.
- Do you want as much fill as you can get right now, accept partials, no resting order? → FAK.
- Do you want to rest on the book at your price and wait for someone to come to you? → GTC.
That's it. Most bot bugs around order types come from picking #1 when you wanted #3 (a "buy" turns into "no position because the spread was too wide") or picking #3 when you wanted #1 (a "buy" turns into a resting order that fills hours later at the wrong moment).
FOK: when you must fill or skip
Fill-or-Kill matches the entire order at the requested price or better, instantly. If the full size cannot be filled instantly, the order is rejected and nothing happens. No resting, no partial.
Use FOK for: news-arbitrage entries (you only want in at the news price, not at where the market is in 30s); take-profit exits at a specific target where partials would muddy bookkeeping; any time the strategy assumes atomic execution.
The trade-off: FOK rejects more often than other order types, especially on illiquid books. Always have a fallback path - re-evaluate the strategy condition and retry if still valid, or move on.
FAK: when partials are acceptable
Fill-and-Kill (also called "immediate or cancel") matches as much as it can right now, then cancels the unfilled remainder. You may get the full size, a partial, or zero.
Use FAK for: market-buy with a specific price ceiling (lift the ask up to N cents above mid); sweep-the-book sells when reducing inventory urgently; any strategy where "some position is better than none."
Operationally trickier than FOK because the bot has to know whether it got 100% or 30% before deciding the next step. The fill response includes a filled_size field - always read it.
GTC: when you want to rest on the book
Good-til-Cancelled rests on the book at your price until filled or you cancel. No timeout (other order types in the v2 API include GTD with an expiry).
Use GTC for: take-profit sells at +Nc above entry; stop-loss sells at -Nc below entry (with caveats - see below); market-making both-sided quotes; any position where the bot is willing to wait for a better price.
The hard rule: GTC requires ≥ 5 shares. Orders below 5 shares are rejected by the CLOB with Size (X) lower than the minimum: 5. A bot that posts a 4-share GTC sell silently fails to set the exit and rides the position to resolution. Always check inventory ≥ 5 before posting GTC; fall back to FAK or ride-to-resolve if smaller.
Limit vs market and the spread tax
Every Polymarket order is technically a limit order - even what bots call a "market buy" specifies a price ceiling. The distinction is whether that price is at the best ask (effectively a market order, will fill against the book) or below it (will rest on the book).
This is where the maker and taker roles matter. A taker sends an order that fills immediately against orders already resting in the book; a maker posts an order that waits in the book for someone else to trade against it. Takers pay the spread (and the taker fee); makers collect the spread (and earn a rebate). The spread tax is the cost a taker pays for crossing the book: with a bid of 0.45, an ask of 0.47, and a mid of 0.46, a round trip that buys at the ask and sells at the bid loses 2 cents per share. On a 60% win-rate strategy with +3c/-4c targets, that 2-cent spread is the difference between profit and loss.
The maker pattern (post a GTC order at the bid or below and wait to be hit) collects that spread instead of paying it. The price you pay is an uncertain fill: you may never get hit. For high-conviction trades, pay the spread and take. For patient accumulation, rest on the book and make.
After you submit: status and on-chain settlement
A fill is not the end. An order moves through live (resting), matched (executed on placement), delayed (held briefly on markets that apply a taker delay) or unmatched (left the delay window without matching). Some markets hold a marketable order for a ~250 ms taker delay before final validation - a real consideration for short-timeframe bots racing the clock.
Once matched, the trade then settles on-chain through its own lifecycle - so a bot must never treat "matched" as "done":
| Trade status | Meaning |
|---|---|
MATCHED | Sent to the executor for on-chain submission (not final). |
MINED | Transaction included in a block (not yet final). |
CONFIRMED | Terminal, successful - it is real money now. |
RETRYING | On-chain submission failed; being resubmitted. |
FAILED | Terminal failure - the trade did not happen. |
Wait for CONFIRMED before you update your position or place a dependent order, and handle RETRYING / FAILED explicitly. Assuming a match settled when it later FAILED is how a bot ends up trading a position it does not actually hold.
Our production defaults (FOK buys, GTC sells)
The pattern most of our production bots converge on:
- Entries: FOK at ask + 0-2 cents. If the bot decided to buy, it should buy now or skip. Resting an entry order is rarely worth it - the situation that triggered the buy decision changes faster than the order will rest.
- Take-profit exits: GTC at target price. Posted immediately after entry fills. We let the market come to us; we don't chase the bid down. With ≥ 5 shares.
- Stop-loss: case-by-case. GTC works for slow strategies where price changes are bounded. For fast-moving markets a GTC stop won't fill if price flies through it; we ride to resolution in option-D fashion (memory: trader-gtc-sell.md).
The pattern is conservative - fewer fills, less slippage. A more aggressive variant uses FAK entries and FAK exits, accepting partial fills. Pick one and stay consistent; mixing per-trade decisions invites confusion.
Code: place each order type
Reference order placement in Python with the current py-clob-client-v2 (the old 0.34.x line is archived - see the SDK chooser).
from py_clob_client_v2 import (ClobClient, OrderArgs, OrderType,
PartialCreateOrderOptions, Side)
c = ClobClient(host="https://clob.polymarket.com", chain_id=137,
key=PRIVATE_KEY, creds=creds, # creds in the constructor (no set_api_creds in V2)
signature_type=2, funder=PROXY)
opts = PartialCreateOrderOptions(tick_size="0.01", neg_risk=False) # read both off the market
# FOK buy: fill 10 shares at 0.45 or skip
buy = OrderArgs(token_id=TOKEN, price=0.45, side=Side.BUY, size=10)
resp = c.create_and_post_order(order_args=buy, options=opts, order_type=OrderType.FOK)
# FAK buy: take as much as you can at 0.45 or below
resp = c.create_and_post_order(order_args=buy, options=opts, order_type=OrderType.FAK)
# GTC sell: rest a sell at 0.85 for 10 shares
sell = OrderArgs(token_id=TOKEN, price=0.85, side=Side.SELL, size=10)
resp = c.create_and_post_order(order_args=sell, options=opts, order_type=OrderType.GTC)
Same operations in Node with @polymarket/clob-client-v2: replace OrderType.FOK with clob.OrderType.GTC etc.; the method is createAndPostOrder. The negRisk flag (chapter 11) must be set in the second argument for multi-outcome markets - missing it routes to the wrong exchange contract.














