Skip to content

Instantly share code, notes, and snippets.

@brentkirkland
Last active May 23, 2023 01:56
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save brentkirkland/b75f76c6bda90ce874e7f6eddeff5d26 to your computer and use it in GitHub Desktop.
Save brentkirkland/b75f76c6bda90ce874e7f6eddeff5d26 to your computer and use it in GitHub Desktop.
Bitfinex OrderBook API Guide

Bitfinex API: Order Book and Checksums

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.

REST

Using the REST book endpoint is great for those who need a single snapshot of the book.

There are two version available:

v1

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

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, 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.

WebSockets

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)
})

NEW: Websocket Checksums

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!

WebSocket Example with Checksums

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++
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment