Skip to main content
WSS
/
ws
Messages
Order Book Depth Query
type:object

Get current market depth for a specific cryptocurrency pair

Subscribe to Order Book Depth
type:object

Subscribe to real-time order book depth updates

Unsubscribe from Depth
type:object

Stop receiving order book depth updates

Depth Response
type:object

Order book depth with bids and asks

Subscribe Response
type:object

Subscription confirmation

Depth Update
type:object

Real-time order book depth update (full reload or partial)

Overview

The Market Depth channel provides real-time order book data with support for both one-time queries and subscription-based updates.

Update Behavior

After successful subscription, the server sends a full snapshot of the order book as the first depth_update message. All subsequent messages are incremental updates containing only the changes since the last update.
Public depth updates exclude Retail Price Improvement (RPI) orders. The depth_update stream includes only regular order book liquidity.Private active order endpoints and private WebSocket streams return RPI orders. The exchange UI order book (web and mobile) displays RPI orders.

Understanding Updates

  • First message (past_update_id is absent): Full order book snapshot with all price levels
  • Subsequent messages (past_update_id is present): Only changed price levels
  • Removed levels: Shown as [price, "0"] - when amount is “0”, remove the price level from the local order book
If 10 seconds elapse without any order book changes, the server pushes a full snapshot again as a keepalive mechanism.

Maintaining a Local Order Book

To maintain an accurate local order book, follow the algorithm below:
  1. Subscribe to depth updates
  2. Receive the first message (full snapshot) - initialize the order book
  3. For each incremental update:
    • If amount is “0” → remove the price level
    • If amount is not “0” → update existing level or insert new level at the correct sorted position
  4. Keep order book sorted: asks ascending, bids descending
  5. Truncate to the desired limit after each update

Code Examples

type IDepth = [string, string];

interface OrderBook {
    asks: IDepth[];
    bids: IDepth[];
}

const ws = new WebSocket("wss://api.whitebit.com/ws");
const orderBook: OrderBook = { asks: [], bids: [] };
const LIMIT = 100;

ws.addEventListener("open", () => {
    ws.send(
        JSON.stringify({
            id: 1,
            method: "depth_subscribe",
            params: ["ETH_BTC", LIMIT, "0", true]
        }),
    );
});

ws.addEventListener("message", (event: MessageEvent) => {
    const message = JSON.parse(event.data.toString());

    if (message.method === "depth_update") {
        const updateData = message.params[0] as Partial<OrderBook & { past_update_id?: number }>;
        const isFirstMessage = !updateData.past_update_id;

        if (isFirstMessage) {
            // First message or keepalive snapshot is a full snapshot - replace order book
            orderBook.asks = updateData.asks ?? [];
            orderBook.bids = updateData.bids ?? [];
        } else {
            // Subsequent messages are incremental updates
            applyUpdates(orderBook.asks, updateData.asks, "ask");
            applyUpdates(orderBook.bids, updateData.bids, "bid");
            truncateOrderBook(orderBook.asks);
            truncateOrderBook(orderBook.bids);
        }
    }
});

function applyUpdates(orderBookSide: IDepth[], updates: IDepth[] | undefined, side: "ask" | "bid") {
    if (updates === undefined) return;
    for (const [price, amount] of updates) {
        // Find the index of an entry in orderBookSide matching the given price.
        const priceIndex = orderBookSide.findIndex((level) => level[0] === price);

        // If the amount is '0', remove the price level from the orderBookSide.
        if (amount === "0") {
            if (priceIndex !== -1) {
                // Remove the existing price level since the amount is '0'.
                orderBookSide.splice(priceIndex, 1);
            }
        } else {
            // If the amount is not '0', either update the existing price level or add a new one.
            if (priceIndex === -1) {
                // Find the position where the new price level should be inserted.
               const insertIndex = orderBookSide.findIndex((level) =>
                    side === "ask" ? level[0] > price : level[0] < price
                );

                if (insertIndex === -1) {
                    // Add to the end if there's no higher price level.
                    orderBookSide.push([price, amount]);
                } else {
                    // Insert at the correct sorted position.
                    orderBookSide.splice(insertIndex, 0, [price, amount]);
                }
            } else {
                // Update the amount for the existing price level.
                orderBookSide[priceIndex][1] = amount;
            }
        }
    }
}

function truncateOrderBook(orderBookSide: IDepth[]) {
    if (orderBookSide.length > LIMIT) {
        // Only truncate if the length exceeds the LIMIT
        orderBookSide.splice(LIMIT);
    }
}