Discovery bounds
The price band and the max-move circuit breaker, the two bounds that decide which orders and which marks the venue accepts.
The Exchange runs two independent bounds on price discovery. Both are armed, both are per-market parameters, and they answer different questions:
Price band
Is this order priced sanely against the mark?
The order, at submission
Max-move breaker
Has the mark moved further than a market should in a window?
Halts the market
The price band: an order-versus-mark collar
An order whose limit price sits further than price_band_bps from the current mark price is refused at submission with PriceBandExceeded. It is a collar on your order, not a clamp. The venue rejects the order rather than repricing it, so a fat-fingered limit does not silently become a different order.
Deployed values, and where they come from:
Market
price_band_bps
Sized from
BTC-USDX-PERP · ETH-USDX-PERP
500 (5%)
Binance USDⓈ-M futures' PERCENT_PRICE filter
SOL-USDX-PERP
800 (8%)
Hyperliquid's default slippage tolerance, as an altcoin-appropriate proxy
Across the full 32-market configured set the band is 500 or 800 bps. The tables on this page list the three tradable markets. NDQ-USDX-PERP is still configured, with the same band and breaker values as BTC and ETH, but it is dormant and not listed as tradable. See Parameter changes.
The comparison is against the mark, and it is strict:
deviation_bps = |order_limit_price − mark| / mark × 10,000
reject ⟺ deviation_bps > price_band_bpsA deviation exactly equal to the band passes. Only a strictly larger one is refused.
Worked example: the band
BTC-USDX-PERP, price_band_bps = 500, tick_size = 0.5, mark at 60,000.
The band is 60,000 × 0.05 = 3,000 wide either side, so acceptable limit prices run 57,000.0 to 63,000.0 inclusive.
63,000.0
500.000 bps
Accepted. Equal to the band, not greater
63,000.5 (one tick higher)
500.083 bps
PriceBandExceeded
64,000.0
666.667 bps
PriceBandExceeded
56,999.5
500.083 bps
PriceBandExceeded
The rejection message names all three inputs: Order price {order_price} deviates from mark {mark} by more than {band_bps} bps. A client can therefore tell a banded order apart from any other rejection and reprice against the mark it failed on.
The denominator is the mark, not your order price. As the mark moves, the same resting limit price can move from inside the band to outside it, but the band is only evaluated when an order enters the book. A resting order is not cancelled when the mark drifts away from it.
The band can also cancel, not only reject.
PriceBandExceededappears as a cancellation reason as well as a submission error. A non-reduce-only trigger order that fires into an out-of-band price is cancelled with that reason rather than filled. A reduce-only trigger is exempt and fills. Its limit price was already clamped into the band when it was converted on fire, which makes the exemption safe.
Three cases the band does not apply to
These are exemptions in the engine, not gaps, and each has a reason:
Liquidation orders. A liquidation must be able to cross the book to close a distressed position; banding it would leave the position open and the loss growing.
A fired reduce-only trigger. A stop that has triggered is already a decision to exit, and the band must not keep you in the position.
Orders with no limit price. A market order has no price to compare, and is bounded instead by its own slippage cap. Likewise, if the market has no mark price yet, there is nothing to band against and the order passes.
A comment in the engine records why the exemption list is narrow. An earlier per-type carve-out let an order that derives its own price reach the exemption roughly 100% away from the mark. Deriving a price does not bound it.
The max-move breaker: a halt, not a rejection
Separately, the venue tracks how far the mark price has moved across a rolling window. If the move exceeds max_mark_move_bps_per_window within max_mark_move_window_s, the market halts.
BTC · ETH
1000 bps (10%)
3600 s (60 min)
CME's dynamic circuit breaker for crypto futures
SOL
1500 bps (15%)
3600 s (60 min)
Widened from the CME figure. No direct CME precedent for SOL
A halt is a market state, not an account state: it stops new risk-increasing activity in that market and is visible on GET /markets/{market_id}/status, which also reports the reason. max_move_breached is 1 of the 7 halt reasons the venue defines. The others are operator action, ADL exhaustion, an uncovered system loss, a mid-fill position-apply failure, a risk-actor restart, and a market-parameter divergence after commit. See Liquidations for the ones that come out of the liquidation waterfall.
A halt does not freeze the mark price. The oracle keeps ingesting prints and the market's mark keeps tracking them for the whole halt, so margin, unrealised PnL and funding keep moving while trading is shut. This is by design. When trading resumes, it re-anchors on a real price rather than releasing the whole move at once. Do not confuse this with the stale-mark policy in Liquidations, which is the opposite case: there the engine refuses to act on a price it no longer trusts. See the Math Engine's oracle chapter for the full statement, including what happens to liquidations that become due mid-halt.
How the window is anchored, and what it misses
Each new mark is compared against the oldest sample still inside the window, not against the window's high or low:
This trips on deviation from the window's start, not peak-to-trough. A spike that reverts inside the window can go undetected. This is a documented design choice, not an oversight. If you rely on the breaker as a backstop, know that it catches a sustained dislocation, not a round trip.
Worked example: the breaker
BTC-USDX-PERP, max_mark_move_bps_per_window = 1000, max_mark_move_window_s = 3600.
t = 0 s
60,000
60,000
0 bps
Trading
t = 600 s
63,000
60,000
500 bps
Trading
t = 1200 s
66,000
60,000
1000 bps
Trading. Equal, not greater
t = 1800 s
66,300
60,000
1050 bps
Halt, max_move_breached
And the blind spot, on the same market and parameters:
t = 0 s
60,000
60,000
0 bps
Trading
t = 1500 s
65,400
60,000
900 bps
Trading
t = 3000 s
60,000
60,000
0 bps
Trading
The mark rose 900 bps and fell 826 bps back, which is 1,726 bps of movement inside one window. The breaker never tripped, because the mark was never more than 900 bps from the anchor at any single measurement. Resume after a halt is manual in either case; the breaker does not self-clear.
Why two bounds rather than one
The two bounds cover opposite failures. A price band catches one bad order against a good mark. A breaker catches a bad mark, from an oracle problem or a genuine dislocation, where every individual order looks reasonable relative to it. A venue with only the first would keep matching a book that had walked 30% on a broken feed. A venue with only the second would let a single mispriced limit trade against resting depth long before the window tripped.
For how the mark itself is constructed and guarded, see Price oracles. For what happens once a position is past its maintenance requirement, see Liquidations.
Last updated

