fix(orders): reject prices that are not a multiple of the tick size#181
Merged
Conversation
Unify _resolve_price and _resolve_protected_market_price into a single validate_price_on_tick_grid helper in context.py, shared by limit and market order preparation. - Price validation no longer consults RoundingConfig: grid membership is fully determined by the tick size, matching the exchange's own check (price divided by the minimum tick must be an integer). RoundingConfig remains scoped to maker/taker amount computation. - Consolidate coverage into an exhaustive characterization suite in test_order_context.py: every on-grid price accepted and returned unchanged across all six ticks, every off-grid price at tick precision rejected on the half-step ticks, precision and range rejections.
This was referenced Jul 22, 2026
Merged
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.
What
_resolve_price(limit orders) and_resolve_protected_market_price(market orders) validated prices by decimal-place count only. Decimal count matches tick-grid membership for power-of-ten ticks, but diverges on0.005and0.0025tick markets: a price like0.007has 3 decimals, passes validation, gets signed into the EIP-712 order, and is then rejected by the exchange. The client-side guard should fail fast instead.Fixes #162.
How
Add a tick-grid membership check after the existing decimal-count check in both validators:
Purely additive: every previously valid price is a tick multiple and still passes; only off-grid prices that the exchange would reject anyway are now caught client-side.
Tests
test_order_limit.py: parametrized on/off-grid cases for_resolve_priceacross0.005,0.0025, and0.01ticks including range boundaries, plus an end-to-endprepare_limit_order_drafttest on a mocked0.005tick market.test_order_market.py: parametrized on/off-grid cases for_resolve_protected_market_price.Verification
uv run ruff format --checkanduv run ruff checkcleanuv run pyright0 errorsuv run pytest tests/unit1854 passedCredit to @GiulioDER for the report and analysis in #162.
Note
Medium Risk
Stricter pre-sign validation changes which limit prices are accepted (no silent rounding) and affects EIP-712 order construction for tick sizes like 0.005/0.0025.
Overview
Introduces shared
validate_price_on_tick_gridfor limitpriceand marketmax_price/min_price, replacing duplicated_resolve_priceand_resolve_protected_market_pricelogic.Validation now enforces the allowed [tick, 1−tick] range, decimal precision derived from the tick (not
RoundingConfig.price), andprice % tick_size == 0so off-grid values like0.007on a0.005tick fail client-side instead of being signed and rejected by the exchange. Valid prices are returned unchanged—limit order prep no longerround_normal-snaps user prices.Unit coverage in
test_order_context.pyexercises on-grid acceptance, off-grid multiples, over-precision inputs, and out-of-range values across all supported ticks.Reviewed by Cursor Bugbot for commit e090e7d. Bugbot is set up for automated code reviews on this repo. Configure here.