6 · The book Draft

Ticks, lots, and depth

4 min read

On this page

Tick size, lot size, and why a price is an integer

A venue does not accept any price. Each instrument has a tick size, the smallest step a price may take, and for BTC-USDT-PERP in this chapter it is $0.01: $68,123.45 is a legal price and $68,123.455 is not. It also has a lot size, the smallest step of quantity, here 0.001 BTC: 0.512 BTC is legal and 0.5125 is not. The venue rejects anything else. So every price is a whole number of ticks, every spread at least one tick, and every quantity a whole number of lots, and that is how the program holds them. $68,123.45 is 6,812,345 ticks; 0.512 BTC is 512 lots.

The alternative, a floating-point number, fails in a way that is easy to miss. A binary float cannot hold 0.01 exactly, any more than a decimal can hold one third; the nearest f64 to $68,123.45 is 68,123.449999999997089616954326629638671875. The error is tiny and usually harmless for display, but a book compares prices for equality millions of times a second (“is this update for a level I already have?”, “is this the best price?”), and equality is exactly what floats do not give. Take the book drawn below: its best ask is $68,123.48 and its best bid $68,123.45, a spread of three ticks. In f64, (68,123.48 − 68,123.45) / 0.01 is 2.9999999998835847, which truncated to an integer is 2. A program that counted its spread that way would believe the market was tighter than it is. With integers, equality is exact and costs one instruction, and an i64 counts to 9.2 × 10¹⁸, enough for any price in ticks. A software decimal type would also be exact, at the cost of a function call where a compare should be.

pub type Ticks = i64;   // price as a count of ticks: $68,123.45 is 6,812,345
pub type Lots  = i64;   // quantity as a count of lots: 0.512 BTC is 512

// The instrument's tick ($0.01) and lot (0.001 BTC) are the only decimals in the
// program: the decoder uses them on the way in, a log line on the way out.

Depth and the top of the book

Depth is how much is waiting beyond the best price: the levels behind the best on each side and the quantity at each. A deep book can absorb a large order with little change in price; a thin one cannot. The top of book is the best bid and the best ask with their quantities, four numbers in all, and it is what most decisions use. A feed that publishes only those four numbers is called a top-of-book or L1 feed; a feed that publishes every level, or many of them, is a full-depth feed. The L-numbers here are the market-data trade’s levels of detail and have nothing to do with the L1, L2 and L3 caches of Chapter 1.

Here is a book for the running instrument at one instant, five levels a side, prices in ticks and dollars, quantities in lots:

A local order book: five levels per side

A local order book: five levels per side. Select the diagram to open it at full size.

The spread is 3 ticks, so the mid falls on a half tick. A program that needs the mid as an integer keeps twice it, the sum 13,624,693, and halves only when printing.

A trade, a fill, maker and taker

A trade happens when an arriving order meets a waiting one at a price both accept, and the price is always the waiting order’s. If somebody sends “sell 0.2 BTC at no less than $68,123.40” into the book above, it meets the best bid, $68,123.45, and trades there: the seller asked for at least $68,123.40 and gets more, because the buyer had already promised to pay $68,123.45. Each side then receives a fill, the venue’s message to an order’s owner saying that so many lots of the order traded at such a price. A fill for less than the whole order is a partial fill, and the rest of a limit order goes on waiting. The waiting order is the maker: it made a price available to the market. The arriving order is the taker: it took that price. Venues commonly charge the taker a higher fee than the maker, and some pay makers a rebate, because waiting orders are what make a market worth coming to.