Summary
A protected BUY market order (max_price set, FAK/FOK) whose amount / max_price is not an exact 2-decimal share count gets its taker amount rounded up by _compute_market_order_amounts(..., protect_price=True). The resulting implied price makerAmount / takerAmount lands strictly below max_price. When max_price is the current best ask (the normal way to place a marketable FAK), the order can never match at the ask and is killed unfilled every time.
In our production bot this silently killed roughly two thirds of all FAK orders for a month (about 63k of 96k orders between 2026-08-04 and 2026-09-02). The failures are invisible from the client side: the venue just reports the FAK as unmatched.
Reproduction
from decimal import Decimal as D
from polymarket._internal.actions.orders.market import _compute_market_order_amounts
offered, requested = _compute_market_order_amounts(
amount=D("1"), price=D("0.07"), side="BUY", tick_size=D("0.01"), protect_price=True,
)
print(offered, requested, D(offered) / D(requested))
# 1000000 14290000 0.06999... -> implied price 0.069996 < 0.07
amount=1 / price=0.07: raw taker = 14.2857 shares, rounded up to 14.29, maker stays 1.00 USDC, implied price 0.069996. A bid of 0.069996 cannot lift an ask at 0.07, so a FAK sent with max_price=0.07 against a 0.07 ask is dead on arrival.
Same for any price where amount / price has more than 2 decimals: 0.03, 0.06, 0.07, 0.09, 0.11, 0.12 with amount=1 (all 99–100 % unmatched in our order log); 0.10, 0.13–0.30 fill normally. It also hits amounts with sub-cent decimals, because raw_maker = round_down(amount, config.size) truncates the amount to cents first (e.g. 8 shares × 0.962 on a 0.001-tick market = 7.696 → 7.69 → shares rounded up → implied 0.9619…).
Reached from the public API through create_market_order / place_market_order on the secure clients whenever side="BUY" and max_price is set (_prepare_protected_market_order_draft passes protect_price=True).
Verified on polymarket-client 0.7.1 (also present since 0.3.0 as far as we can tell; our unmatched rate jumped from ~10 % to ~70 % the day we upgraded from 0.2.0).
Why this is a bug
protect_price guarantees the effective price never exceeds max_price, which is reasonable. But rounding the taker amount up while keeping the maker amount fixed means the only prices reachable are strictly below max_price whenever the division is inexact. For a marketable order priced exactly at the touch that turns "never pay more than the ask" into "never match the ask".
Suggested fix
When amount / max_price is not an exact 2-decimal share count, three constraints cannot all hold at once:
makerAmount <= amount (never overspend),
shares * price >= $1 (the venue's minimum for market orders),
makerAmount / shares >= max_price (the order can actually match at max_price).
With amount=1, price=0.07: 14.28 shares satisfies 1 and 3 but not 2; 14.29 shares with maker 1.00 satisfies 1 and 2 but not 3; 14.29 shares with maker 1.0003 satisfies 2 and 3 but not 1. The current code picks the second option, which produces an order that is guaranteed never to fill, so honouring 1 and 2 buys nothing.
Two options that do not silently produce a dead order:
- Round the share count up and set
makerAmount = shares * max_price (1.0003 for 14.29 shares). This exceeds amount by less than 0.01 * max_price, i.e. under one cent, which is easy to document; the order matches at the ask and clears the minimum.
- Or raise a
UserInputError telling the caller that amount at this price is not representable and must be increased, so the caller decides.
Either is fine; what should not happen is the present behaviour, where the implied price lands below max_price and the venue reports the order as unmatched with no hint that it could never have matched.
Summary
A protected BUY market order (
max_priceset, FAK/FOK) whoseamount / max_priceis not an exact 2-decimal share count gets its taker amount rounded up by_compute_market_order_amounts(..., protect_price=True). The resulting implied pricemakerAmount / takerAmountlands strictly belowmax_price. Whenmax_priceis the current best ask (the normal way to place a marketable FAK), the order can never match at the ask and is killed unfilled every time.In our production bot this silently killed roughly two thirds of all FAK orders for a month (about 63k of 96k orders between 2026-08-04 and 2026-09-02). The failures are invisible from the client side: the venue just reports the FAK as unmatched.
Reproduction
amount=1/price=0.07: raw taker = 14.2857 shares, rounded up to 14.29, maker stays 1.00 USDC, implied price 0.069996. A bid of 0.069996 cannot lift an ask at 0.07, so a FAK sent withmax_price=0.07against a 0.07 ask is dead on arrival.Same for any price where
amount / pricehas more than 2 decimals: 0.03, 0.06, 0.07, 0.09, 0.11, 0.12 withamount=1(all 99–100 % unmatched in our order log); 0.10, 0.13–0.30 fill normally. It also hits amounts with sub-cent decimals, becauseraw_maker = round_down(amount, config.size)truncates the amount to cents first (e.g. 8 shares × 0.962 on a 0.001-tick market = 7.696 → 7.69 → shares rounded up → implied 0.9619…).Reached from the public API through
create_market_order/place_market_orderon the secure clients wheneverside="BUY"andmax_priceis set (_prepare_protected_market_order_draftpassesprotect_price=True).Verified on
polymarket-client0.7.1 (also present since 0.3.0 as far as we can tell; our unmatched rate jumped from ~10 % to ~70 % the day we upgraded from 0.2.0).Why this is a bug
protect_priceguarantees the effective price never exceedsmax_price, which is reasonable. But rounding the taker amount up while keeping the maker amount fixed means the only prices reachable are strictly belowmax_pricewhenever the division is inexact. For a marketable order priced exactly at the touch that turns "never pay more than the ask" into "never match the ask".Suggested fix
When
amount / max_priceis not an exact 2-decimal share count, three constraints cannot all hold at once:makerAmount <= amount(never overspend),shares * price >= $1(the venue's minimum for market orders),makerAmount / shares >= max_price(the order can actually match atmax_price).With
amount=1,price=0.07: 14.28 shares satisfies 1 and 3 but not 2; 14.29 shares with maker 1.00 satisfies 1 and 2 but not 3; 14.29 shares with maker 1.0003 satisfies 2 and 3 but not 1. The current code picks the second option, which produces an order that is guaranteed never to fill, so honouring 1 and 2 buys nothing.Two options that do not silently produce a dead order:
makerAmount = shares * max_price(1.0003 for 14.29 shares). This exceedsamountby less than0.01 * max_price, i.e. under one cent, which is easy to document; the order matches at the ask and clears the minimum.UserInputErrortelling the caller thatamountat this price is not representable and must be increased, so the caller decides.Either is fine; what should not happen is the present behaviour, where the implied price lands below
max_priceand the venue reports the order as unmatched with no hint that it could never have matched.