Polymarket US Markets, Risk and Execution
This page covers the settings that decide where and how much a platform: polymarket_us Bot trades: the market selector, the three risk keys Polymarket US accepts (max_position, price_floor and price_ceiling), loop.interval, and the deployment risk limits you set when you deploy. It also explains how the Bot prices entries and exits, and the checks every order has to pass. For every accepted key at a glance, see All Polymarket US parameters; for the keys Studio rejects, see Not available on Polymarket US.
Market selection
A Polymarket US Bot always trades exactly one market. Set one selector. If you set more than one, slug wins, then event_slug, then query.
All three pass Studio's checks and can be deployed. Studio's AI is set up to save a concrete market.slug, which is the predictable choice.
market.slug
The exact market the Bot trades, for example a daily temperature contract. Nothing is looked up at startup: every order, position read and open-order read uses this slug.
- The slug isn't checked when you save. A wrong slug shows up once the Bot runs, as "market ... is not active" or "market missing" errors.
- No rollover. When a dated market closes, the Bot stays pointed at it and every order is refused. To trade the next market in a series, update the slug and redeploy.
market.event_slug
When the Bot starts, it searches open events, takes the one whose slug matches exactly, and trades that event's first active market. It chooses once and keeps that market until it restarts.
- In an event with several markets, "first" depends on how Polymarket US orders them, and may not be the outcome you meant.
- If no open market matches, the Bot logs a configuration error and stops before its first loop.
- On regular Polymarket,
event_slugalone is rejected. On Polymarket US it's accepted.
market.query
A free-text search. At startup the Bot trades the first active market in the results. It doesn't pick the most liquid one, and results can change between restarts, so treat it as a discovery tool rather than something to deploy. If the search finds no open market, the Bot logs a configuration error and stops before its first loop.
Note: The rolling daily selector (
market.recurring) exists only for the Polymarket US leg of an arbitrage strategy. A single-venue Polymarket US strategy rejects it.
Risk and loop
risk.max_position
A whole number of contracts, 1 or more. Decimals are dropped: 2.5 becomes 2. How it's used depends on the strategy:
| Strategy | What max_position does on Polymarket US |
|---|---|
spread_capture | Stops placing quotes while you hold max_position or more. |
mean_reversion | Each entry is min(max_position, 10) contracts. |
panic_fade | Not used. fade_size sets the size. |
observation_momentum | Caps each order's size and your total position: an order is cut to what still fits. |
pre_announcement_drift | The entry is min(max_position, 5) contracts; the release exit sells what the entry bought. |
custom | A rule's size can't be larger. |
On every strategy, the running Bot also refuses a buy that would take the contracts you hold plus your resting orders in the market past the lower of max_position and the deploy dialog's Max open contracts (open contracts would be 6, exceeding risk.max_position=5). Sells are never capped.
Note: The deploy dialog's Max contracts per order caps every order. Studio pre-fills it from your strategy's largest single order: the biggest rule
size(1 when omitted),min(max_position, 10)formean_reversion,max_positionforobservation_momentum,min(max_position, 5)forpre_announcement_drift,fade_sizeforpanic_fadeand 1 forspread_capture. A larger entry is refused;sell_alland the built-in exits are split to fit.
risk.price_floor and risk.price_ceiling
price_floor defaults to 0 and must be below 1. price_ceiling must be above 0, at most 1, and above the floor. Leaving the ceiling out is an error. What they do depends on the strategy:
- Custom entries: the price of the side you buy (the YES price for
buy_yes, 1 − YES forbuy_no) is clamped into the range. The trade still happens; only the limit price moves. See Entries. mean_reversion: buys YES only whenprice_floor< price ≤entry_low, and buys NO only whenentry_high≤ price <price_ceiling.panic_fade: enters only between floor and ceiling, and exits if YES drops belowprice_floor.observation_momentumandpre_announcement_drift: enter only while the YES price is strictly between floor and ceiling. Thepre_announcement_driftexit ignores the range.spread_capture: bids are placed no lower than the floor and offers no higher than the ceiling.
If you want a price range to block a custom entry, add a price condition to the rule. Clamping alone won't stop it.
loop.interval
Seconds between runs, 10 or more. Decimals are dropped (10.9 becomes 10, and 9.9 is rejected). The Bot runs, then waits interval seconds, so the real cadence is a little longer than the interval. observation_momentum and panic_fade sample the price once per loop, so the interval also sets how their windows fill.
Deployment risk limits
When you deploy, the Risk limits section of the deploy dialog sets hard caps. They aren't part of the YAML and a strategy can't override them. Studio pre-fills them from your strategy; see Deployment risk limits. When you redeploy a Bot, the dialog starts from the limits it already runs with. An order that breaks a limit is refused, not shrunk. On Polymarket US:
| Limit | Studio pre-fills | If a deployment sets no value | On Polymarket US |
|---|---|---|---|
| Max contracts per order | The strategy's largest single order (see risk.max_position) | 1 | Checked on every order. A bigger entry is refused; sell_all and the built-in exits are split into market orders of at most min(40, this limit). |
| Max open contracts | max_position | The smaller of max_position and 3 | Checked on buys: contracts held + contracts in resting orders (both sides) + the new buy, against the lower of this and max_position. |
| Max dollars per order | The same number as Max contracts per order, in dollars | $10 | Checked on buys: price of the side you buy × contracts. |
| Max daily notional traded | The largest of the order size, max_position and 10 × the order size, in dollars | $100 | Checked on every order except sells that only reduce your position. Counts your whole Polymarket US account per UTC day, including trades made outside this Bot, from your latest 200 account activities. |
| API error limit / Error window minutes | Not taken from the strategy: 3 / 5 | 3 / 5 | After 3 API errors in 5 minutes the Bot refuses new orders for the next 5 minutes, then resumes by itself. Sells that only reduce your position keep running. |
The deploy dialog doesn't show Max daily loss for Polymarket US, because the Bot doesn't read it: nothing stops a Polymarket US Bot on losses except your sell_all rules.
For example, a max_position: 2 mean_reversion strategy pre-fills 2 contracts and $2 per order, 2 open contracts and $20 of daily notional.
Order pricing and execution
How a custom Polymarket US rule enters and exits
- Entry: limit order
- Exit: market order
Entry
- Rule fires
buy_yesorbuy_noSkipped while missing edge data blocks entries, if any order is already resting in the market, or if buying power is short. - PriceLast trade YES priceFalls back to the current price, then the book midpoint. buy_no uses 1 − YES.
- Clamp and roundInto [price_floor, price_ceiling], on a 1¢ gridYES last trade 0.40 → limit 0.40. YES 0.97 with a 0.90 ceiling → limit 0.90.
- ChecksPre-order checksTwo-sided book, contracts per order, open contracts (never above max_position), dollars per order, buying power, daily notional.
- OrderGood-till-cancel limit buyRests until it fills or something cancels it, such as a cancel_all rule.
Exit
- Rule fires
sell_allRefused if the side you hold can't be read. - QuantityYour whole position, split to fitPieces of at most min(40, Max contracts per order). Holding 50 NO with a limit of 20 → sell 20, 20, then 10 NO.
- ChecksPre-order checksNeeds a bid to sell into, not a two-sided book. A sell that reduces your position skips the open-contract, dollars-per-order, buying-power and daily-notional checks, and keeps going while the API error limit pauses new orders.
- OrderMarket sells, one after anotherNo limit price. A refused or rejected piece stops the exit until the next loop. In paper mode each fills against the whole book or is rejected; it never rests.
Holding 3 contracts when Max contracts per order is 1: the exit goes out as three 1-contract market sells. If one is refused, the rest is retried on the next loop.
Entries
Every entry (buy_yes, buy_no and the built-in strategies' buys) is a good-till-cancel limit order for a whole number of contracts:
- Price. The Bot uses the last trade price for YES. If there is none, it uses the current price, then the midpoint of the best bid and offer. For
buy_nothe NO price is 1 − that YES price. (spread_captureis the exception: it prices its quotes around the midpoint.) - Clamp. Custom entries clamp that price into
[price_floor, price_ceiling]. - Round. The price is sent on a 1-cent grid.
- Rest. The order doesn't chase the offer. If nobody trades at your limit, it rests until it fills or something cancels it.
Where a custom entry's limit price lands
- Last YES trade price (open circle)
- Limit price it sends (filled circle)
A custom entry is also skipped while any order is resting in the market, and when your buying power doesn't cover it. Pair entries with a cancel_all rule if an unfilled order should not wait forever.
Exits
sell_all and the built-in exits sell your whole position on the side you hold with market orders (panic_fade and pre_announcement_drift sell only what their own entry bought, on its side):
- A position larger than Max contracts per order is split into several orders of at most min(40, Max contracts per order) contracts, sent one after another.
- An exit needs a bid to sell into, not a two-sided book. A sell that only reduces your position isn't held back by the API error breaker or Max daily notional traded.
- If a piece is refused or rejected, the exit stops there; the rest is retried on the next loop if the rule still matches. A rejected order is logged as rejected (
order_rejected), never as placed.
cancel_all, and spread_capture's refresh, cancel only this Bot's open orders. Polymarket US orders carry no client id, so the Bot remembers the id of every order the exchange accepts from it; the memory survives restarts and redeploys, but not stopping the Bot. Orders you placed by hand and other Bots' orders are never cancelled. In paper mode every paper order is the Bot's own.
Checks before every order
Before sending any order, the Bot confirms all of the following. If one fails, the order is refused and that loop ends with an error in the Bot log.
| Check | Buys | Sells |
|---|---|---|
| The API error breaker hasn't tripped | Yes | Only if the sell doesn't simply reduce your position |
| Quantity is within Max contracts per order | Yes | Yes |
| The market is active and not closed, settled or expired | Yes | Yes |
| The book is two-sided: 0 < best bid < best offer < 1 | Yes | A bid between 0 and 1 is enough |
| The order price is strictly between 0 and 1 | Yes | Yes |
| Your position and open orders in the market can be read | Yes | Yes |
| Held + resting + new contracts within Max open contracts | Yes | No |
| Side price × contracts within Max dollars per order | Yes | No |
| Buying power covers the order | Yes | No |
| Today's traded notional stays within Max daily notional traded | Yes | Only if the sell doesn't simply reduce your position |
These checks can't be turned off.