The order book is where users can find data price points of bids and asks. Each price point includes price, count, and amount. This data is useful to traders as it gives them an current market overview.
To get the order book via the API, you may either request it via REST or WebSockets. This guide highlights how to interact with both interfaces. Not only, this guide dicusses a new WebSockets feature: checksums! Now your book will never fall behind.
Using the REST book endpoint is great for those who need a single snapshot of the book.
There are two version available:
To receive the book, send a GET request to:
https://api.bitfinex.com/v1/book/SYMBOL
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 a response as such:
{
"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 50 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
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, while 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.
Finally, if you feel that you need a constant stream of updates, WebSockets is the tool for the job.
To use WebSockets, first connect 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 the as such:
ws.on('message', function (msg) {
console.log('New message: ', msg)
})
Bitfinex now offers checksums for v2 WebSockets. The checksum is a CRC32 checksum that is sent after every book iteration and covers the first 25 bids and 25 asks. Here we will go over 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 snippit of such 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
.
That's it! Now your books are never behind!
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++
})