Moonbase Docs

Orderbook Channel

Real-time orderbook updates with bids and asks

Overview

The book channel provides real-time orderbook updates for a specific trading product. You'll receive a snapshot of the current orderbook upon subscription, followed by incremental updates as the orderbook changes.

Channel: book Authentication: Not required (Public) Products: BTC-VND, ETH-VND, SOL-VND, XRP-VND

Subscribe

Request:

{
  "op": "sub",
  "channel": "book",
  "product": "BTC-VND"
}

Friendly format:

sub book BTC-VND

Parameters:

  • op (string, required) - Must be "sub"
  • channel (string, required) - Must be "book"
  • product (string, required) - Trading pair (e.g., "BTC-VND")

Responses

Subscription Confirmation

Upon successful subscription:

{
  "channel": "book",
  "product": "BTC-VND",
  "type": "subscribed"
}

Snapshot Message

Immediately after subscription, you'll receive a complete snapshot of the current orderbook:

{
  "channel": "book",
  "product": "BTC-VND",
  "type": "snapshot",
  "data": {
    "bids": [
      ["3123300000", "0.079"],
      ["3123250000", "0.150"],
      ["3123200000", "0.200"]
    ],
    "asks": [
      ["3123340000", "0.008"],
      ["3123400000", "0.050"],
      ["3123500000", "0.100"]
    ]
  }
}

Update Messages

After the snapshot, you'll receive incremental updates when the orderbook changes:

{
  "channel": "book",
  "product": "BTC-VND",
  "type": "update",
  "data": {
    "bids": [
      ["3123300000", "0.060"]
    ],
    "asks": []
  }
}

Data Structure

Each price level is represented as a tuple [price, size]:

type PriceLevel = [string, string];

interface OrderbookData {
  bids: PriceLevel[];  // Buy orders, sorted by price (highest first)
  asks: PriceLevel[];  // Sell orders, sorted by price (lowest first)
}

Price Level Fields:

  • price (string) - Price in quote currency (VND)
  • size (string) - Total quantity available at this price level

Understanding Updates

Price Level Updates

When you receive an update:

  1. New price level - If the price doesn't exist in your local orderbook, add it
  2. Size update - If the price exists, update the size
  3. Price removal - If size is "0" or the price is not included, remove that price level

Example: Processing Updates

class OrderbookManager {
  constructor() {
    this.bids = new Map(); // price -> size
    this.asks = new Map();
  }

  handleMessage(message) {
    if (message.type === 'snapshot') {
      // Reset orderbook with snapshot
      this.bids.clear();
      this.asks.clear();

      message.data.bids.forEach(([price, size]) => {
        this.bids.set(price, size);
      });

      message.data.asks.forEach(([price, size]) => {
        this.asks.set(price, size);
      });
    } else if (message.type === 'update') {
      // Apply incremental updates
      message.data.bids.forEach(([price, size]) => {
        if (parseFloat(size) === 0) {
          this.bids.delete(price);
        } else {
          this.bids.set(price, size);
        }
      });

      message.data.asks.forEach(([price, size]) => {
        if (parseFloat(size) === 0) {
          this.asks.delete(price);
        } else {
          this.asks.set(price, size);
        }
      });
    }
  }

  getBestBid() {
    const prices = Array.from(this.bids.keys()).map(Number);
    const bestPrice = Math.max(...prices);
    return { price: bestPrice, size: this.bids.get(bestPrice.toString()) };
  }

  getBestAsk() {
    const prices = Array.from(this.asks.keys()).map(Number);
    const bestPrice = Math.min(...prices);
    return { price: bestPrice, size: this.asks.get(bestPrice.toString()) };
  }
}

Unsubscribe

To stop receiving orderbook updates:

Request:

{
  "op": "unsub",
  "channel": "book",
  "product": "BTC-VND"
}

Friendly format:

unsub book BTC-VND

Response:

{
  "type": "unsubscribed",
  "channel": "book",
  "product": "BTC-VND"
}

Complete Example

const WebSocket = require('ws');

const ws = new WebSocket('wss://ws.dev.mbhq.net/ws');
const orderbook = new OrderbookManager();

ws.on('open', () => {
  console.log('Connected to WebSocket');

  // Subscribe to BTC-VND orderbook
  ws.send(JSON.stringify({
    op: 'sub',
    channel: 'book',
    product: 'BTC-VND'
  }));
});

ws.on('message', (data) => {
  const message = JSON.parse(data);

  if (message.channel === 'book') {
    if (message.type === 'subscribed') {
      console.log('Subscribed to orderbook');
    } else if (message.type === 'snapshot' || message.type === 'update') {
      orderbook.handleMessage(message);

      const bestBid = orderbook.getBestBid();
      const bestAsk = orderbook.getBestAsk();
      const spread = bestAsk.price - bestBid.price;

      console.log(`Best Bid: ${bestBid.price} (${bestBid.size})`);
      console.log(`Best Ask: ${bestAsk.price} (${bestAsk.size})`);
      console.log(`Spread: ${spread}`);
    }
  }
});

Use Cases

  1. Market Making - Monitor bid-ask spread and adjust orders
  2. Price Display - Show real-time orderbook depth in your application
  3. Trading Signals - Detect large orders or liquidity changes
  4. Market Analysis - Analyze order flow and market depth

Best Practices

  1. Maintain Local State - Keep a local copy of the orderbook and apply updates incrementally
  2. Handle Reconnections - Re-subscribe after disconnection to get a fresh snapshot
  3. Validate Data - Check for price/size consistency and handle edge cases
  4. Optimize Performance - Use efficient data structures (Map, SortedMap) for price levels
  5. Monitor Latency - Track message timestamps to detect delays