fix(client): round protected market BUY shares down so the order can cross at max_price - #292
Open
naruto11eth wants to merge 1 commit into
Open
fix(client): round protected market BUY shares down so the order can cross at max_price#292naruto11eth wants to merge 1 commit into
naruto11eth wants to merge 1 commit into
Conversation
…cross at max_price The exchange prices a BUY as makerAmount / takerAmount and only matches when that is at or above the ask. Rounding the share count up put the price a hair below max_price, so a FAK or FOK sent at the touch could never lift an ask resting there. Rounding down keeps it at or fractionally above max_price and below the next tick, the same construction the unprotected path and the legacy clients use. Maker amount, SELL orders and unprotected BUYs are unchanged.
naruto11eth
force-pushed
the
feature/dev-639-protected-buy-rounding
branch
from
September 7, 2026 05:20
1ef77b5 to
6a77970
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A market BUY with
max_priceis signed as two amounts: collateral to spend and shares to receive. The exchange derives the order price asmakerAmount / takerAmountand only matches a BUY when that price is at or above the resting ask. Whenamount / max_pricewas not exactly representable, the SDK rounded the share count up, which placed the derived price a hair belowmax_price. An ask resting exactly atmax_price, the normal target for a FAK or FOK at the touch, could never be lifted, and the venue simply reported the order as not filled. Most prices are affected: withamount=1on a 0.01-tick market, every inexact division lands below the cap.This change rounds the share count down instead, which is the construction the unprotected path and the legacy
py-clob-clientalready use. The derived price now sits at or fractionally abovemax_priceand always below the next tick, so the order lifts an ask atmax_price, fills at the resting price, and cannot reach a higher tick. Maker amount stays the cent-roundedamount, so spend never exceeds it and the venue's whole-cent and$1minimum rules are met wheneveramount >= 1.What changes on the wire
takerAmountdrops by one precision step when the division is inexact.1 @ 0.07signs14.2857shares instead of14.2858;100 @ 0.55signs181.8181instead of181.8182.makerAmountis unchanged.protect_priceflag is removed; SELL amounts never reached the rounding branch (2dp shares times the tick price always fits the amount precision), so one code path now covers both modes.takerAmountas a minimum-shares floor at the cost of marketability. The documented contract, fill atmax_priceor better, is kept; the previously signed floor was never fillable at the cap.Docs and tests
create_market_order/place_market_orderdocstrings (sync and async) now state whatmax_priceandmin_pricebound and why the signed price may sit fractionally abovemax_price.max_priceand below the next tick, maker never exceedsamount, maker on whole cents, taker on the amount tick; the$1minimum case; exact division and SELL unchanged.max_spend+max_pricetest, neither of which existed.The TypeScript client has the same rounding and gets a matching change separately.
Fixes #291
Note
Medium Risk
Changes signed order amounts for protected market BUYs, which affects fill behavior at exact max_price; logic is well-tested but trading-path regressions would show up as missed or altered fills.
Overview
Fixes protected market BUY orders with
max_pricethat could fail to fill when liquidity sat exactly at the cap.Amount encoding:
_compute_market_order_amountsnow always floors the share (taker) amount after precision trimming, instead of rounding shares up whenprotect_price=True. The exchange prices BUYs asmaker / takerand only matches at or above the ask; rounding shares up pushed the derived price slightly belowmax_price, so touch liquidity was unreachable. Flooring keeps the encoded price at or fractionally abovemax_price(within one tick). Theprotect_priceflag is removed so protected and unprotected paths share one rounding rule; SELL behavior is unchanged.Wire impact: Inexact divisions lower
takerAmountby one precision step (e.g.100 @ 0.55→181_818_100vs181_818_200);makerAmountis unchanged.Docs & tests:
create_market_order/place_market_orderdocstrings (sync + async) document price bounds, sub-tick encoded price, and possible unspent dust. New parametrized unit tests on amount math,max_spend+max_price, and a sync protected-BUY assertion.Reviewed by Cursor Bugbot for commit 6a77970. Bugbot is set up for automated code reviews on this repo. Configure here.