bitmexr: An R client for BitMEX cryptocurrency exchange.

Bitcoin
R

How bitmexr came to be.

Published

04/13/2020

While writing my previous post, I was surprised to find that there was no R related package for the cryptocurrency exchange BitMEX. While cryptocurrency itself is a somewhat niche area, BitMEX is one of the largest and most popular exchanges, so I had assumed someone would have already put together something in R for accessing data through BitMEX’s API. As I could find no such package, I drew inspiration from the few ‘crypto’ related packages that do exist, and set about creating a package that would provide a R users with a set of tools to obtain historic trade data from the exchange..

BitMEX has a very detailed API that allows users to perform essentially every action possible on the site through the API. This ranges from simple queries about historic price data through to executing trades on the platform. Initially, I just wanted the package to be able to easily access historic data for research purposes, however it would be relatively straightforward to implement additional features such as executing trades through the API.

And like that, bitmexr was born…

Currently you can install bitmexr from github, but hopefully the package will be on CRAN soon.

remotes::install_github("hfshr/bitmexr")

bitmexr

bitmexr is a relatively simple package that enables the user to obtain data about trades that have been executed on the exchange. The API supports trade data to be returned in two forms:

Individual trade data

  • Details about individual trades that have taken place on the exchange

Bucketed trade data

  • A summarised form of individual trade data where individual trades have been “bucketed” into one of the following time intervals; 1-minute, 5-minute, 1-hour or 1-day.

To access this data, bitmexr has two core functions:

  • trades() for individual trade data
library(bitmexr)
library(dplyr)
library(rmarkdown)
library(knitr)
library(tidyquant)
library(purrr)
library(gganimate)


trades(symbol = "XBTUSD", count = 5) %>% 
  select(-trdMatchID) %>% # unique trade identifier, not particularly interesting
  kable()
timestamp symbol side size price tickDirection grossValue homeNotional foreignNotional
2022-03-13 20:18:49 XBTUSD Sell 400 38710.0 ZeroMinusTick 1033324 0.0103332 400
2022-03-13 20:18:49 XBTUSD Sell 200 38710.0 ZeroMinusTick 516662 0.0051666 200
2022-03-13 20:18:49 XBTUSD Sell 100 38710.0 MinusTick 258331 0.0025833 100
2022-03-13 20:18:49 XBTUSD Sell 100 38713.5 MinusTick 258308 0.0025831 100
2022-03-13 20:18:49 XBTUSD Sell 100 38715.5 MinusTick 258294 0.0025829 100
  • bucket_trades() for bucketed trade data
bucket_trades(symbol = "XBTUSD", count = 5, binSize = "1d") %>% 
  kable()
timestamp symbol open high low close trades volume vwap lastSize turnover homeNotional foreignNotional
2022-03-13 XBTUSD 38714.5 39480.0 38645.5 38794.0 63987 377767900 39060.82 1400 9.671287e+11 9671.287 377767900
2022-03-12 XBTUSD 39418.0 40245.5 38239.0 38714.5 141479 979194400 39062.35 1700 2.506753e+12 25067.534 979194400
2022-03-11 XBTUSD 41954.5 42037.0 38420.0 39418.0 181394 1351599500 39651.86 6500 3.408674e+12 34086.744 1351599500
2022-03-10 XBTUSD 38723.5 42596.5 38641.5 41954.5 167006 1079137900 41338.54 200 2.610491e+12 26104.907 1079137900
2022-03-09 XBTUSD 37967.0 39381.0 37857.0 38723.5 156917 1087892900 38651.68 3700 2.814608e+12 28146.078 1087892900

These functions allow the user to quickly return historic trade/price data that have been executed on the exchange.

map_* variants

In addition to the core functions, the packages contains map_* variants of each function. These functions were implemented to address two restrictions within the API:

  1. The maximum number of rows per API call is limited to 1000
  2. The API is limited to 30 requests within a 60 second period

The map_* functions are useful for when the data you wanted to return is greater than the 1000 row limit, but you want to avoid running in to the request limit (too many request timeouts may lead to an IP for up to one week).

For example, say you want to get hourly bucketed trade data from the 2019-01-01 to 2020-01-01.

bucket_trades(startTime = "2019-01-01", 
              endTime = "2020-01-01", 
              binSize = "1h",
              symbol = "XBTUSD") %>% 
  filter(timestamp == max(timestamp)) %>% 
  select(timestamp) %>% 
  kable()
timestamp
2019-02-11 15:00:00

The first 1000 rows have only returned data up until 2019-02-11. To obtain the rest of the data, you would need to pass in this start date and run the function again, repeating this process until you had the desired time span of data.

This is where the map_* variants come in handy.

map_bucket_trades(start_date = "2019-01-01", 
                  end_date = "2020-01-01", 
                  binSize = "1h",
                  symbol = "XBTUSD",
                  verbose = FALSE) %>% 
  paged_table()