16 Nov Bitfinex API: Order Books & Checksums
The Bitfinex API has been crafted to offer our users an easy, yet effective, way to build the digital asset trading applications and tools you need to optimise your trading. Today Bitfinex is unveiling a new feature that will help API users ensure their books are always in sync with the market. The following guide breaks down how to interact with both the REST and WebSocket interfaces.
Furthermore, this guide introduces a new WebSockets feature: checksums, which can be used to ensure that your order book remains timely and up to date.
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);
})
This 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_asks and/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);
})
This will return a response as such:
[
[
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 and you can process them 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:
[ CHAN_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 the checksum was created. For example:
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 up to date and valid.
WebSocket with 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++
})
Stay up to date with Bitfinex on Twitter, LinkedIn, Facebook & Youtube.
If you are interested in learning more about the Bitfinex development libraries, please visit our Github.
Join us on our mission to create the industry’s most innovative cryptocurrency exchange.