Skip to content

Learn

Exchange APIs

What is tick size? Price increments and rounding in trading APIs

Figures on this page are as of 2026-09. Fees, limits and margin tiers change — check the venue's own docs.

Tick size is the smallest price increment a venue accepts for a symbol: a limit price or trigger price has to land exactly on that grid, and an order priced between two ticks is rejected before it reaches the order book.

The concept is simple. The trouble is that almost every price a program computes — a stop from a volatility measure, a limit at the mid, an entry a percentage below the last trade — falls between ticks, and the way you move it onto the grid changes what the order means. This page covers how venues express and enforce the rule, which direction to round for each kind of order, and how to do the arithmetic without floating-point surprises.

For the wider family of per-symbol rules — minimum notional, quantity step, open-order caps — see symbol filters and minimum order sizes. This page stays on price.

Why this matters now

A rejected order is the good outcome. The bad outcome is an order that passes the check because it was rounded, and was rounded the wrong way: a passive bid nudged up until it crosses the spread, a stop nudged away from price until the loss at the stop exceeds what you sized for. Nothing rejects those orders. The tick rule is satisfied and the intent is not.

It also matters because the rejection looks different on every venue and even between products on one venue, so error handling written for one will not recognise the other.

How it actually works

  1. The venue publishes a price rule per symbol. On Binance spot it is the PRICE_FILTER in GET /api/v3/exchangeInfo, which bounds the price and defines its tick. On Hyperliquid, the tick-and-lot-size page states the rules as a limit on significant figures and a maximum number of decimals, which differ between perps and spot; read the current values there.
  2. You compute a price. It is a real number with no reason to sit on the grid.
  3. You move it onto the grid, choosing a direction.
  4. The venue checks it. Off-tick prices are refused, and how that refusal looks depends on where you sent it — see the table below.
  5. Other price rules still apply. A price exactly on the tick can still be rejected for being too far from the market: Binance’s PERCENT_PRICE filter, or Hyperliquid’s “Order price too far from oracle”. Those are separate checks, not tick errors.
WhereHow an off-tick price is rejected
Binance spot-1013 with msg “Filter failure: PRICE_FILTER”
Binance USDⓈ-M futures-4014 PRICE_NOT_INCREASED_BY_TICK_SIZE, “Price not increased by tick size.” — no “Filter failure” text
Hyperliquid“Price must be divisible by tick size.” in the order’s status; historical status tickRejected

Two details from that table catch people. Binance spot puts the specific filter in msg behind a generic code, so handlers must read the message — see Binance API error -1013. And Hyperliquid returns per-order errors inside an HTTP 200 response with "status":"ok", while some tick-size validation happens before the batch is processed and produces a single error for the whole payload. A handler that only checks the HTTP status will record a rejected order as sent.

Hyperliquid’s significant-figure rule has a further consequence: the smallest valid price step depends on how large the price is, so a single cached “tick” number is the wrong model there. Implement the rule as the page states it.

Which way to round

The honest answer is “in the direction that keeps the order’s meaning”. That is different for each kind of order.

OrderRoundWhy
Passive buy limitDownUp could make it more aggressive than intended, or cross the spread
Passive sell limitUpThe mirror case
Post-only orderAway from the spreadToward it risks a rejection such as Hyperliquid’s “Post only order would have immediately matched, bbo was {bbo}.” or, for a Binance LIMIT_MAKER, “Order would immediately match and take.”
Marketable limit meant to fillToward the aggressive sideRounding back toward the book edge can leave it resting unfilled
Stop-loss triggerToward current price (tighter)Wider means a larger loss than the position was sized for
Take-profitEither, decided onceNo risk budget depends on it, but inconsistency makes results harder to audit

For stops, the order of operations matters as much as the direction. Snap the stop to the tick first, then compute position size from the snapped stop distance. Sizing from the unsnapped stop and rounding the stop afterwards leaves the size calibrated to a price you did not send. The position size calculator assumes the stop you enter is the stop you will use; give it the rounded one.

The arithmetic: floats, decimals and strings

Floating-point numbers cannot represent most decimal fractions exactly. The two classic failures:

  • The remainder check lies. Testing price % tick == 0 on floats can report a valid price as off-grid, or the reverse.
  • The snapped value is not what you sent. Computing round(price / tick) * tick in floats can produce a long tail of digits; the venue sees the tail and your logs, which print a rounded value, do not.

The fix is to do price arithmetic in a decimal type, built from strings, and to snap by division so it works whatever the tick is:

from decimal import Decimal, ROUND_FLOOR, ROUND_CEILING

def snap_price(price: str, tick: str, direction: str) -> str:
    p, t = Decimal(price), Decimal(tick)
    mode = ROUND_FLOOR if direction == "down" else ROUND_CEILING
    snapped = (p / t).to_integral_value(rounding=mode) * t
    return format(snapped.normalize(), "f")

Three details in that function are deliberate:

  • Decimal(price) takes a string. Building a decimal from a float copies the float’s error into it.
  • Division, not “round to N decimals”. Rounding to a number of decimal places only works when the tick happens to be a power of ten. Dividing by the tick works for any tick.
  • normalize() before formatting strips trailing zeros. That is cosmetic on some venues and not on others: Hyperliquid’s signing page lists trailing zeroes on numbers among its common errors, and its tick-and-lot-size page repeats that “trailing zeroes should be removed” when implementing signing. A price that is valid on the grid can still produce a bad signature if it is serialised as 100.50 instead of 100.5.

Log the string you send, not the number you computed. When a tick rejection arrives, that string is the evidence.

Tick size vs the things it gets confused with

TermWhat it constrains
Tick sizeThe price grid
Lot size / step sizeThe quantity grid — see what is lot size
Percent-price or oracle bandHow far the price may be from a reference, regardless of grid
Display precisionHow many decimals a UI or ticker shows; not necessarily the rule the order check applies

Where the boundary should sit

  • Snap in code, after any model. A model asked for a stop or limit price returns a plausible number with no knowledge of the grid. Treat it as input to the snapping function, not as the order price.
  • Choose the direction from the order’s purpose, using a table like the one above encoded once, not decided at each call site.
  • Cache the rules, refresh on rejection. Per-symbol rules can change. A tick rejection on a price your code snapped is a signal to re-fetch, not to retry unchanged.
  • Treat a changed price as a changed order. If snapping moved the price enough to matter — a stop, a post-only bid — the approval step should see the snapped price, not the one proposed.

FAQ

What is tick size in trading?

Tick size is the smallest price increment a venue accepts for a symbol. Limit and trigger prices must fall exactly on that increment, or the order is rejected before it reaches the book. It is set per symbol, published through the venue’s instrument or exchange-information data, and can differ between spot and derivatives products.

Why was my order rejected for tick size?

The price you sent was not an exact multiple of the symbol’s tick, often because it was computed rather than typed, or because floating-point arithmetic left extra digits. Binance spot reports it as -1013 with “Filter failure: PRICE_FILTER”, Binance USDⓈ-M futures as -4014, and Hyperliquid as “Price must be divisible by tick size.”

Should I round my price up or down to the tick?

It depends on the order’s purpose. Round passive buy limits down and passive sell limits up, keep post-only orders away from the spread, and round stop-loss triggers toward the current price so the loss at the stop does not exceed what you sized for. Then size the position from the rounded stop.

Why does float rounding cause tick-size errors?

Binary floating point cannot represent most decimal fractions exactly, so snapping a price in floats can produce a value with a tail of extra digits that is not on the grid. Do price arithmetic in a decimal type built from strings, snap by dividing by the tick, and log the exact string sent.