Producing a live orderbook

Introduction

The Live Orderbook will lie at the heart of almost any real-world trading bot or algorithm.

In this tutorial, we illustrate the steps to hook a LiveOrderbook instance up to a message feed and to start extracting information from it.

Configuration

The LiveOrderbook class configuration object has only one required field: product. Since an orderbook represents a single trading pair, any message with a product ID other than product will be filtered out.

The other config options are logger, to pass a global Logger object to the orderbook; and strictMode, a boolean, that determines the behavior when a skipped message is detected.

LiveOrderbook is a writable stream, so once you have a feed message stream, and applied whatever filters and transformers to it that you like, you can pipe it straight in and the orderbook will update itself automatically.

A standard template for creating and configuring a LiveOrderbook looks like this:

import * as CBPTT from 'coinbase-pro-trading-toolkit';

const products = ['LTC-USD'];
const logger = CBPTT.utils.ConsoleLoggerFactory({ level: 'debug' });
CBPTT.Factories.CoinbasePro.FeedFactory(logger, products).then((feed: CoinbaseProFeed) => {
    // Configure the live book object
    const config: LiveBookConfig = {
        product: product,
        logger: logger
    };
    const book = new LiveOrderbook(config);
    feed.pipe(book);
    ...
});

Orderbook messages

LiveOrderbook emits an array of messages in response to certain events.

Event tag Emitted when Data provided
LiveOrderbook.update The orderbook changes due to a new order, a cancelled order, or a trade A descendant of OrderbookMessage causing the change
LiveOrderbook.trade a trade occurs A TradeMessage describing the trade
LiveOrderbook.ticker Whenever the ticker is updated A TickerMessage instance
LiveOrderbook.snapshot When an orderbook snapshot is received, usually immediately after subscribing to the message feed. The SnapshotMessage
LiveOrderbook.skippedMessage if a sequencing error in the message feed is detected. A SkippedMessageEvent
error An unrecoverable error occurs The Error object

Orderbook methods and properties

All the orderbook methods and properties are near-realtime. You can query the last update time with book.timeSinceTickerUpdate or book.timeSinceOrderbookUpdate which returns the time (is seconds) since the last update to the orderbook.

Some of the handy properties available on LiveOrderbook are:

Property Type Description
sourceSequence number The last known sequence number from the underlying feed, if it exists. Some websocket feeds don’t supply sequences, for example.
sequence number The sequence number as generated by the connected feed source. It may or may not correspond to sourceSequence
numAsks number The number of orders on the sell side of the book
numBids number The number of orders on the buy side of the book
asksTotal BigJS The total value of the sell side of the book (in base currency)
bidsTotal BigJS The total value of the buy side of the book (in base currency)
book BookBuilder Provides access to the underlying book builder. Note: Modifying the book will cause the LiveOrderbook to go out of sync.
ticker Ticker Returns the latest ticker information about the book
baseCurrency string The base currency for the trading pair (e.g. BTC in BTC-USD)
quoteCurrency string The quote currency for the trading pair (e.g. USD in BTC-USD)

ordersForValue

ordersForValue is probably the most useful method in liveOrderbook. It allows you to query the orderbook to determine which orders would be filled for a given trade.

This comes in useful for calculating things like average price, slippage and various other metrics for liquidity that are important for algorithmic trading.

The function takes a side (‘buy’ or ‘sell’) and a value and returns the orders that would be filled by that trade. However, it also returns the cumulative size and cost/value of the trade at each level, from which all the liquidity metrics can be derived.

The 3rd and 4th arguments to ordersForValue modify the interpretation of the trade. useQuote instructs the method to interpret value in quote currency rather than base currency.

And startingPoint allows you to specify an offset value (by default it is zero). This allows you to calculate order tranches (the first 100 BTC, the second 100 BTC etc).

Try it out

Given a running LiveOrderbook instance, the following snippet

console.log(`Number of bids: ${book.numBids} asks: ${book.numAsks}`);
console.log(`Total ${book.baseCurrency} liquidity: ${book.bidsTotal.toFixed(3)} asks: ${book.asksTotal.toFixed(3)}`);
let orders: CumulativePriceLevel[] = book.ordersForValue('buy', 100, false);
console.log(`Cost of buying 100 ${book.baseCurrency}: ${orders[orders.length - 1].cumValue.toFixed(2)} ${book.quoteCurrency}`);
orders = book.ordersForValue('sell', 1000, true);
console.log(`Need to sell ${orders[orders.length - 1].cumSize.toFixed(3)} ${book.baseCurrency} to get 1000 ${book.quoteCurrency}`);

would produce output similar to

Number of bids:         1849    asks: 2297
Total LTC liquidity:    4004381.314     asks: 172100.402
Cost of buying 100 LTC: 4299.23 USD
Need to sell 23.278 LTC to get 1000 USD

To respond to orderbook messages, simple add an event listener to the book. To respond to ticker events you can do something like

book.on('LiveOrderbook.ticker', (ticker: Ticker) => {
    console.log(ticker);
});

And to track total trading volume since connecting, you would have

let tradeVolume: number = 0;
book.on('LiveOrderbook.trade', (trade: TradeMessage) => {
   tradeVolume += +(trade.size);
});

A full working example can be found in the repository, in tutorials/t002_liveOrderbook.ts. It can be executed by running

$ ts-node src/tutorials/t002_liveOrderbook.ts

to obtain output similar to

2017-08-15T20:35:56.317Z - info: Creating new Coinbase Pro Websocket connection to wss://ws-feed.pro.coinbase.com
2017-08-15T20:35:57.661Z - debug: Connection to wss://ws-feed.pro.coinbase.com  has been established.
2017-08-15T20:35:57.662Z - debug: Sending subscribe message to WS server
2017-08-15T20:35:58.697Z - info: Snapshot received by LiveOrderbook Demo
Price:      42.89 | Bid:      42.89 | Ask:      42.90 | sequence: 4282257


 Orderbook 266
   1.3168    1.3168  $  42.89           $  42.90    3.8946     3.8946
  67.3168   66.0000  $  42.88           $  42.91    1.0826     4.9772
  67.3268    0.0100  $  42.87           $  42.92    1.0000     5.9772
  97.3919   30.0652  $  42.86           $  42.94    0.1392     6.1164
 117.4271   20.0352  $  42.85           $  42.95    0.9465     7.0629
 160.4720   43.0449  $  42.84           $  42.96   52.4897    59.5527
 182.9364   22.4644  $  42.83           $  42.97    5.8179    65.3706
 182.9713    0.0349  $  42.82           $  42.98   11.7460    77.1166
 223.0063   40.0349  $  42.81           $  42.99    7.4361    84.5527
1225.8337  1002.8275  $  42.80          $  43.00  213.4548   298.0075

Number of bids:         1846    asks: 2300
Total LTC liquidity:    4025988.612     asks: 171302.649
Cost of buying 100 LTC: 4296.80 USD
Need to sell 1000.000 LTC to get 1000 USD
2017-08-15T20:36:03.704Z - info: Cumulative trade volume: 27.9451


 Orderbook 409
   1.3168    1.3168  $  42.89           $  42.90    3.8946     3.8946
  26.3168   25.0000  $  42.88           $  42.91    1.0826     4.9772
  56.3268   30.0100  $  42.87           $  42.92    1.0000     5.9772
 ...