For the complete documentation index, see llms.txt. This page is also available as Markdown.

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:

Bound
Question
Rejects

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_bps

A 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.

Your limit price
Deviation
Result

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. PriceBandExceeded appears 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.

Market
Max move
Window
Sized from

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.

Time
Mark
Anchor (oldest in window)
Move
Result

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:

Time
Mark
Anchor
Move
Result

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