Order Book Fidelity in a Hot Market

How to Trust Your Order Book

Bitfinex
Bitfinex
5 min readNov 16, 2017

--

For any serious trader, monitoring the order book is critical for market analysis. Today Bitfinex is pleased to announce a new tool that will help API users keep their books in sync with a fast moving market.

This guide will provide an overview to accessing the Bitfinex order books via our APIs, and explain how to use our latest synchronization tool: checksums.

The following guide introduces how to interact with both the REST and WebSocket interfaces, and how to make use of the new checksum tool.

REST

Using the REST API “book” endpoint is great for those who need a single snapshot of the book. There are two versions of REST available, V1 and V2 (V2 is recommended):

V1

To receive the book send a GET request to: https://api.bitfinex.com/v1/book/SYMBOL

(documentation) where SYMBOL is the pair you are inquiring about, i.e., BTCUSD, ETHUSD, LTCUSD, etc.

The following is an example written in JavaScript:

const request = require('request')
request.get('https://api.bitfinex.com/v1/book/SYMBOL/book/btcusd',
function(error, response, body) {
console.log(body);
})

Which will return the following response:

{
"bids":[{
"price":"574.61",
"amount":"0.1439327",
"timestamp":"1472506127.0"
},...],
"asks":[{
"price":"574.62",
"amount":"19.1334",
"timestamp":"1472506126.0"
},...]
}

The default size of the book is 25 per side. To increase/limit asks or bids you may pass limit_asksand/or limit_bids within the url.

For example: https://api.bitfinex.com/v1/book/SYMBOL?limit_asks=100&limit_bids=100

V2

If you need the full book, it may be most useful to use v2 with different price precisions P0, P1, P2, P3. To receive book, send a GET request to: https://api.bitfinex.com/v2/book/SYMBOL/PRECISION

Where SYMBOL is the symbol you are inquiring about i.e. tBTCUSD, tETHUSD, tLTCUSD and PRECISION is the desired precision – P0 being the most precise, P3 being the least precise.

The following is an example written in JavaScript:

const request = require('request')
request.get('https://api.bitfinex.com/v2/book/tBTCUSD/P0',
function(error, response, body) {
console.log(body);
})

Which will return the following response:

[
[
PRICE,
COUNT,
AMOUNT
]
]

You will find that the response is returned in a list. Bids have a positive amount; asks have a negative amount.

WebSockets

If you feel that you need a constant stream of updates, WebSockets is the tool for the job.

To use WebSockets, start by connecting to wss://api.bitfinex.com/ws/2

const WS = require('ws')
ws = new WS('wss://api.bitfinex.com/ws/2')

On open, send an subscribe event with your favorite pair and precision:

ws.on('open', function open () {
ws.send(JSON.stringify({ event: 'subscribe', channel: 'book', pair: 'tBTCUSD', prec: 'P0' }))
})

Now a stream of updates which you can process as such:

ws.on('message', function (msg) {
console.log('New message: ', msg)
})

New Feature: WebSocket Checksums

The WebSockets v2 book API (documentation) now supports the option to request a checksum after each book change. The checksum is a CRC32 value and covers the first 25 bids and 25 asks. By calculating your own checksum and comparing it to the value provided, you can verify that your data is correct and up to date. Below we introduce the basics of requesting and applying checksums.

First, connect to the Bitfinex WebSocket:

const WS = require('ws')
const ws = new WS('wss://api.bitfinex.com/ws/2')

On open, send message with event: 'conf' and flag: 131072, with your subscribe message:

ws.on('open', function open () {
ws.send(JSON.stringify({ event: 'conf', flags: 131072 }))
ws.send(JSON.stringify({ event: 'subscribe', channel: 'book', pair: pair, prec: 'P0' }))
})

A checksum message will be sent every book iteration:

[ CHAIN_ID, 'cs', CHECKSUM ]

where CHECKSUM is a signed integer.

Finally, create a string that represents your book, use a CRC-32 library (in this case Node) to create the checksum value, and then compare it to the checksum returned by the WebSocket.

The following is a quick snippet of said steps:

const csdata = []
const bidsKeys = BOOK.psnap['bids']
const asksKeys = BOOK.psnap['asks']

for (let i = 0; i < 25; i++) {
if (bidsKeys[i]) {
const price = bidsKeys[i]
const pp = BOOK.bids[price]
csdata.push(pp.price, pp.amount)
}
if (asksKeys[i]) {
const price = asksKeys[i]
const pp = BOOK.asks[price]
csdata.push(pp.price, -pp.amount)
}
}

const csStr = csdata.join(':')
const csCalc = CRC.str(csStr)

if (csCalc !== checksum) {
console.error('CHECKSUM_FAILED')
}

NOTE: It is important that you recreate your book string in the same format that the checksum was created. An illustration:

If you had bids [{ price: 6000, amount: 1 }, { price: 5900, amount: 2 }] and asks: [{ price: 6100, amount: -3 }, { price: 6200, amount: -4 }], your checksum string would be 6000:1:6100:-3:5900:2:6200:-4.

By comparing your calculated checksum and the one provided by the API, you can verify that your local data is correct. If the values diverge, you’ll know that you need to get a new order book snapshot, re-subscribe to the channel, or inspect your implementation of the API.

The checksum allows you to rest easy by always knowing that your books are valid and up to date.

WebSocket w/ Checksums — An Example

Feel free to use the following example as a starting point in your own Bitfinex tool.

const WS = require('ws')
const CRC = require('crc-32')
const _ = require('lodash')

const BOOK = {}

// connect to websocket
const ws = new WS('wss://api.bitfinex.com/ws/2')

// handle connect
ws.on('open', function open () {
BOOK.bids = {}
BOOK.asks = {}
BOOK.psnap = {}
BOOK.mcnt = 0

// send websocket conf event with checksum flag
ws.send(JSON.stringify({ event: 'conf', flags: 131072 }))

// send subscribe to get desired book updates
ws.send(JSON.stringify({ event: 'subscribe', channel: 'book', pair: 'tBTCUSD', prec: 'P0' }))
})

// handle incoming messages
ws.on('message', function (msg) {
msg = JSON.parse(msg)
if (msg.event) return
if (msg[1] === 'hb') return

// if msg contains checksum, perform checksum
if (msg[1] === 'cs') {
const checksum = msg[2]
const csdata = []
const bidsKeys = BOOK.psnap['bids']
const asksKeys = BOOK.psnap['asks']

// collect all bids and asks into an array
for (let i = 0; i < 25; i++) {
if (bidsKeys[i]) {
const price = bidsKeys[i]
const pp = BOOK.bids[price]
csdata.push(pp.price, pp.amount)
}
if (asksKeys[i]) {
const price = asksKeys[i]
const pp = BOOK.asks[price]
csdata.push(pp.price, -pp.amount)
}
}

// create string of array to compare with checksum
const csStr = csdata.join(':')
const csCalc = CRC.str(csStr)
if (csCalc !== checksum) {
console.error('CHECKSUM FAILED')
process.exit(-1)
} else {
console.log('Checksum: ' + checksum + ' success!')
}
return
}

// handle book. create book or update/delete price points
if (BOOK.mcnt === 0) {
_.each(msg[1], function (pp) {
pp = { price: pp[0], cnt: pp[1], amount: pp[2] }
const side = pp.amount >= 0 ? 'bids' : 'asks'
pp.amount = Math.abs(pp.amount)
BOOK[side][pp.price] = pp
})
} else {
msg = msg[1]
const pp = { price: msg[0], cnt: msg[1], amount: msg[2] }

// if count is zero, then delete price point
if (!pp.cnt) {
let found = true

if (pp.amount > 0) {
if (BOOK['bids'][pp.price]) {
delete BOOK['bids'][pp.price]
} else {
found = false
}
} else if (pp.amount < 0) {
if (BOOK['asks'][pp.price]) {
delete BOOK['asks'][pp.price]
} else {
found = false
}
}

if (!found) {
console.error('Book delete failed. Price point not found')
}
} else {
// else update price point
const side = pp.amount >= 0 ? 'bids' : 'asks'
pp.amount = Math.abs(pp.amount)
BOOK[side][pp.price] = pp
}

// save price snapshots. Checksum relies on psnaps!
_.each(['bids', 'asks'], function (side) {
const sbook = BOOK[side]
const bprices = Object.keys(sbook)
const prices = bprices.sort(function (a, b) {
if (side === 'bids') {
return +a >= +b ? -1 : 1
} else {
return +a <= +b ? -1 : 1
}
})
BOOK.psnap[side] = prices
})
}
BOOK.mcnt++
})

Read more on the Bitfinex blog.

Stay up to date with Bitfinex on Twitter, Facebook & LinkedIn.

--

--

Bitfinex
Bitfinex

Bitfinex is the world’s leading cryptocurrency trading platform.