# Order Patterns All orders on Polymarket are expressed as limit orders. Market orders are limit orders with a marketable price that execute immediately. ## Order Types | Type | Behavior | Use Case | |------|----------|----------| | **GTC** | Good-Til-Cancelled — rests on book until filled or cancelled | Default for limit orders | | **GTD** | Good-Til-Date — active until expiration timestamp (UTC seconds), unless filled or cancelled first | Auto-expire before known events | | **FOK** | Fill-Or-Kill — fill entirely immediately or cancel | All-or-nothing market orders | | **FAK** | Fill-And-Kill — fill what's available, cancel rest | Partial-fill market orders | ## Tick Sizes Price must conform to the market's tick size or the order is rejected. | Tick Size | Precision | Example Prices | |-----------|-----------|----------------| | `0.1` | 1 decimal | 0.1, 0.2, 0.5 | | `0.01` | 2 decimals | 0.01, 0.50, 0.99 | | `0.001` | 3 decimals | 0.001, 0.500, 0.999 | | `0.0001` | 4 decimals | 0.0001, 0.5000, 0.9999 | Get tick size: `client.getTickSize(tokenID)` (TS) / `client.get_tick_size(token_id)` (Python). Also available as `minimum_tick_size` on market objects. ## Limit Order (GTC) ```typescript // TypeScript — one-step const response = await client.createAndPostOrder( { tokenID: "TOKEN_ID", price: 0.50, size: 10, side: Side.BUY }, { tickSize: "0.01", negRisk: false }, OrderType.GTC ); ``` ```python # Python — one-step response = client.create_and_post_order( OrderArgs(token_id="TOKEN_ID", price=0.50, size=10, side=BUY), options={"tick_size": "0.01", "neg_risk": False}, order_type=OrderType.GTC, ) ``` ### Two-step (sign then submit) ```typescript // TypeScript const signedOrder = await client.createOrder( { tokenID: "TOKEN_ID", price: 0.50, size: 10, side: Side.BUY }, { tickSize: "0.01", negRisk: false } ); const response = await client.postOrder(signedOrder, OrderType.GTC); ``` ```python # Python signed_order = client.create_order( OrderArgs(token_id="TOKEN_ID", price=0.50, size=10, side=BUY), options={"tick_size": "0.01", "neg_risk": False}, ) response = client.post_order(signed_order, OrderType.GTC) ``` ## Market Order (FOK / FAK) - **BUY**: `amount` = dollar amount to spend - **SELL**: `amount` = number of shares to sell - `price` = worst-price limit (slippage protection), not target execution price ```typescript // TypeScript — FOK BUY: spend exactly $100 or cancel const buyOrder = await client.createMarketOrder( { tokenID: "TOKEN_ID", side: Side.BUY, amount: 100, price: 0.50 }, { tickSize: "0.01", negRisk: false } ); await client.postOrder(buyOrder, OrderType.FOK); // One-step convenience const response = await client.createAndPostMarketOrder( { tokenID: "TOKEN_ID", side: Side.BUY, amount: 100, price: 0.50 }, { tickSize: "0.01", negRisk: false }, OrderType.FOK ); ``` ```python # Python — FOK BUY buy_order = client.create_market_order( token_id="TOKEN_ID", side=BUY, amount=100, price=0.50, options={"tick_size": "0.01", "neg_risk": False}, ) client.post_order(buy_order, OrderType.FOK) ``` ## GTD Order (Expiring) Expiration = UTC seconds timestamp. Security threshold: add 60 seconds minimum. **Effective lifetime of N seconds: `now + 60 + N`** ```typescript // TypeScript — expire in 1 hour const expiration = Math.floor(Date.now() / 1000) + 60 + 3600; const response = await client.createAndPostOrder( { tokenID: "TOKEN_ID", price: 0.50, size: 10, side: Side.BUY, expiration }, { tickSize: "0.01", negRisk: false }, OrderType.GTD ); ``` ```python # Python — expire in 1 hour import time expiration = int(time.time()) + 60 + 3600 response = client.create_and_post_order( OrderArgs(token_id="TOKEN_ID", price=0.50, size=10, side=BUY, expiration=expiration), options={"tick_size": "0.01", "neg_risk": False}, order_type=OrderType.GTD, ) ``` ## Post-Only Orders Guarantee maker status. If order would cross spread, it's rejected (not executed). ```typescript // TypeScript const response = await client.postOrder(signedOrder, OrderType.GTC, true); ``` ```python # Python response = client.post_order(signed_order, OrderType.GTC, post_only=True) ``` - Only works with GTC and GTD - Rejected if combined with FOK or FAK ## Batch Orders Up to **15 orders** in a single request. ```typescript // TypeScript const orders: PostOrdersArgs[] = [ { order: await client.createOrder( { tokenID: "TOKEN_ID", price: 0.48, side: Side.BUY, size: 500 }, { tickSize: "0.01", negRisk: false } ), orderType: OrderType.GTC, }, { order: await client.createOrder( { tokenID: "TOKEN_ID", price: 0.52, side: Side.SELL, size: 500 }, { tickSize: "0.01", negRisk: false } ), orderType: OrderType.GTC, }, ]; const response = await client.postOrders(orders); ``` ```python # Python response = client.post_orders([ PostOrdersArgs( order=client.create_order( OrderArgs(price=0.48, size=500, side=BUY, token_id="TOKEN_ID"), options={"tick_size": "0.01", "neg_risk": False}, ), orderType=OrderType.GTC, ), PostOrdersArgs( order=client.create_order( OrderArgs(price=0.52, size=500, side=SELL, token_id="TOKEN_ID"), options={"tick_size": "0.01", "neg_risk": False}, ), orderType=OrderType.GTC, ), ]) ``` ## Cancel Orders All cancel endpoints require L2 authentication. ```typescript // TypeScript await client.cancelOrder("0xORDER_ID"); // single await client.cancelOrders(["0xID_1", "0xID_2"]); // multiple await client.cancelAll(); // all orders await client.cancelMarketOrders({ market: "0xCONDITION_ID" }); // by market await client.cancelMarketOrders({ // by token market: "0xCONDITION_ID", asset_id: "TOKEN_ID", }); ``` ```python # Python client.cancel(order_id="0xORDER_ID") client.cancel_orders(["0xID_1", "0xID_2"]) client.cancel_all() client.cancel_market_orders( market="0xCONDITION_ID", asset_id="TOKEN_ID", # optional ) ``` ### Onchain Cancellation (fallback) If the API is unavailable, cancel directly on the Exchange contract by calling `cancelOrder(Order order)` onchain with the full signed order struct. Use the `CTFExchange` or `NegRiskCTFExchange` contract depending on the market type. See [Contract Addresses](/resources/contract-addresses) for addresses. ## Heartbeat If a valid heartbeat is not received within **10 seconds** (with up to a 5-second buffer), **all of your open orders will be cancelled**. ```typescript // TypeScript let heartbeatId = ""; setInterval(async () => { const resp = await client.postHeartbeat(heartbeatId); heartbeatId = resp.heartbeat_id; }, 5000); ``` ```python # Python import time heartbeat_id = "" while True: resp = client.post_heartbeat(heartbeat_id) heartbeat_id = resp["heartbeat_id"] time.sleep(5) ``` - First request: use empty string for `heartbeat_id` - If you send an invalid or expired `heartbeat_id`, the server responds with a `400 Bad Request` and provides the correct `heartbeat_id` in the response ## Error Codes | Error | Description | |-------|-------------| | `INVALID_ORDER_MIN_TICK_SIZE` | Price doesn't conform to the market's tick size | | `INVALID_ORDER_MIN_SIZE` | Order size is below the minimum threshold | | `INVALID_ORDER_DUPLICATED` | Identical order has already been placed | | `INVALID_ORDER_NOT_ENOUGH_BALANCE` | Funder doesn't have sufficient balance or allowance | | `INVALID_ORDER_EXPIRATION` | Expiration timestamp is in the past | | `INVALID_ORDER_ERROR` | System error while inserting order | | `INVALID_POST_ONLY_ORDER_TYPE` | Post-only flag used with a market order type (FOK/FAK) | | `INVALID_POST_ONLY_ORDER` | Post-only order would cross the book | | `EXECUTION_ERROR` | System error while executing trade | | `ORDER_DELAYED` | Order placement delayed due to market conditions | | `DELAYING_ORDER_ERROR` | System error while delaying order | | `FOK_ORDER_NOT_FILLED_ERROR` | FOK order couldn't be fully filled | | `MARKET_NOT_READY` | Market is not yet accepting orders | ## Insert Statuses | Status | Description | |--------|-------------| | `matched` | Order placed and matched with a resting order | | `live` | Order placed and resting on the book | | `delayed` | Order is marketable but subject to a matching delay | | `unmatched` | Order is marketable but failed to delay — placement still successful | ## Trade Statuses ``` MATCHED → MINED → CONFIRMED ↓ ↑ RETRYING ───┘ ↓ FAILED ``` | Status | Terminal | Description | |--------|----------|-------------| | `MATCHED` | No | Matched and sent to the executor service for onchain submission | | `MINED` | No | Observed as mined on the chain, no finality threshold yet | | `CONFIRMED` | Yes | Achieved strong probabilistic finality — trade successful | | `RETRYING` | No | Transaction failed (revert or reorg) — being retried by the operator | | `FAILED` | Yes | Trade failed permanently and is not being retried | ## Prerequisites Before placing orders, the funder address must approve the Exchange contract: - **Buying**: the funder must have set a **USDC.e** allowance greater than or equal to the spending amount. - **Selling**: the funder must have set a **conditional token** allowance greater than or equal to the selling amount. Max order size = `balance - sum(openOrderSize - filledAmount)` ## Sports Markets - Outstanding limit orders auto-cancelled when game begins - Marketable orders have 3-second placement delay - Game start times can shift — monitor accordingly