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

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 that price level from your local order book
If 10 seconds elapse without any order book changes, the server will push a full snapshot again as a keepalive mechanism.

Maintaining a Local Order Book

To maintain an accurate local order book, follow this algorithm:
  1. Subscribe to depth updates
  2. Receive the first message (full snapshot) - initialize your order book
  3. For each incremental update:
    • If amount is “0” → remove that 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 your 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 that matches the given price.
        const priceIndex = orderBookSide.findIndex((level) => level[0] === price);

        // If the amount is '0', it means this price level should be removed 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);
    }
}