Processing math: 100%

Order book from Binance in R

Beniamino Sartini (R package)

Last update: 2023-06-10

GET depth function

The binance_depth(api = “spot”, pair, quiet = TRUE) function takes as three arguments:

  • api character (mandatory, default: spot): specify the api module to use. It can be:

    • spot: for Spot market;
    • fapi: for Futures and Margin markets denominated in USD, denoted in the website by USD-M;
    • dapi: for Futures and Margin markets denominated in the underlying asset, denoted in the website by COIN-M;
    • eapi: for Options market.
  • pair character (optional, default: BTCUSDT): the a trading pair available on Binance.com, for example BTCUSDT, ETHUSDT, ETHBTC.

  • quiet logical (mandatory, default: FALSE): if TRUE the function will not display messages and warnings.

Example: Spot (api)

# import depth data from binance 
depth <- binance_depth(api = "spot", pair = "BTCUSDT", quiet = TRUE)

data <- dplyr::bind_rows(
  tail(dplyr::filter(depth, side == "ASK"), n = 3), # best 3 ask
  head(dplyr::filter(depth, side == "BID"), n = 3)  # best 3 bid
) 

format_kable(data, caption)
Last Update: 10 Jun 2023 at 03:23 CET
date market pair side price quantity
2023-06-10 03:23:21 spot BTCUSDT ASK 26459.09 0.02288
2023-06-10 03:23:21 spot BTCUSDT ASK 26459.08 0.02138
2023-06-10 03:23:21 spot BTCUSDT ASK 26459.07 10.05993
2023-06-10 03:23:21 spot BTCUSDT BID 26459.06 1.66527
2023-06-10 03:23:21 spot BTCUSDT BID 26459.05 0.00946
2023-06-10 03:23:21 spot BTCUSDT BID 26459.01 0.03392

Example: Futures USD-M (fapi)

# import depth data from binance 
depth_fapi <- binance_depth(api = "fapi", pair = "BTCUSDT", quiet = TRUE)

dplyr::bind_rows(
  tail(dplyr::filter(depth_fapi, side == "ASK"), n = 3), # best 3 ask
  head(dplyr::filter(depth_fapi, side == "BID"), n = 3)  # best 3 bid
) %>% 
  format_kable(caption)
Last Update: 10 Jun 2023 at 03:23 CET
date market pair side price quantity
2023-06-10 03:23:22 usd-m BTCUSDT ASK 26444.1 0.001
2023-06-10 03:23:22 usd-m BTCUSDT ASK 26444.0 0.016
2023-06-10 03:23:22 usd-m BTCUSDT ASK 26443.9 17.520
2023-06-10 03:23:22 usd-m BTCUSDT BID 26443.8 14.344
2023-06-10 03:23:22 usd-m BTCUSDT BID 26443.7 0.034
2023-06-10 03:23:22 usd-m BTCUSDT BID 26443.6 0.489

Example: Futures COIN-M (dapi)

# import depth data from binance 
depth_dapi <- binance_depth(api = "dapi", pair = "BTCUSD_PERP", quiet = TRUE)

dplyr::bind_rows(
  tail(dplyr::filter(depth_dapi, side == "ASK"), n = 3), # best 3 ask
  head(dplyr::filter(depth_dapi, side == "BID"), n = 3)  # best 3 bid
) %>% 
  format_kable(caption)
Last Update: 10 Jun 2023 at 03:23 CET
date market pair side price quantity
2023-06-10 03:23:24 coin-m BTCUSD_PERP ASK 26451.0 926
2023-06-10 03:23:24 coin-m BTCUSD_PERP ASK 26450.9 3
2023-06-10 03:23:24 coin-m BTCUSD_PERP ASK 26450.7 17402
2023-06-10 03:23:24 coin-m BTCUSD_PERP BID 26450.6 425
2023-06-10 03:23:24 coin-m BTCUSD_PERP BID 26450.5 538
2023-06-10 03:23:24 coin-m BTCUSD_PERP BID 26450.4 55

Example: Options (eapi)

df_options <- binance_exchange_info(api = "eapi", pair = "BTCUSDT")

symbol <- filter(df_options, strikePrice == "27000.00000000")[1,]$symbol
df_options <- binance_exchange_info(api = "eapi", pair = "BTCUSDT")
symbol <- filter(df_options, as.numeric(strikePrice) == trunc(data$price[1]/1000)*1000)[1,]$symbol

# import depth data from binance 
depth_eapi <- binance_depth(api = "eapi", pair = symbol, quiet = TRUE)

dplyr::bind_rows(
  tail(dplyr::filter(depth_eapi, side == "ASK"), n = 3), # best 3 ask
  head(dplyr::filter(depth_eapi, side == "BID"), n = 3)  # best 3 bid
) %>% 
  format_kable(caption)
Last Update: 10 Jun 2023 at 03:23 CET
date market pair side price quantity
2023-06-10 03:23:25 options BTC-230610-26000-C ASK 800 0.02
2023-06-10 03:23:25 options BTC-230610-26000-C ASK 660 0.05
2023-06-10 03:23:25 options BTC-230610-26000-C ASK 590 2.50
2023-06-10 03:23:25 options BTC-230610-26000-C BID 325 2.50

Example: Micro price and Imbalance

Denoting by:

  • PBIDt: best BID price.
  • PASKt: best ASK price.
  • QBIDt: quantity as limit buy order at BID price.
  • QASKt: quantity as limit sell order at ASK price.

The mid-price is computed as simple mean among the best BID-ASK price:

PMidt=PBIDt+PASKt2

The bid-ask spread is the mean distance among the best BID and ASK price:

St=PASKtPBIDt2

The microprice is defined as weighted sum considering also the quantities H at the best BID-ASK:

PMicrot=PBIDtQBIDt+PASKtQASKtQBIDt+QASKt The order imbalance is defined as the proportion of quantity on the best BID with respect to all the quantities:

It=QBIDtQBIDt+QASKt - Gatheral and Oomen (2009)

# best ask and bid 
bask <- tail(dplyr::filter(depth, side == "ASK"), n = 1)
bbid <- head(dplyr::filter(depth, side == "BID"), n = 1)

# compute mid-price 
mid_price  <- (bbid$price + bask$price)/2

# compute bid-ask spread 
bid_ask_spread  <- (bask$price - bbid$price)/2

# compute micro-price 
micro_price  <- (bbid$price*bbid$quantity + bask$price*bask$quantity)/(bbid$quantity + bask$quantity)

# compute order-imbalance 
bid_imbalance <- (bbid$quantity)/(bask$quantity + bbid$quantity)*100

df_mp_example <- dplyr::tibble(
        best_ask = bask$price,
        ask = bask$quantity, 
        mid_price = mid_price,
        micro_price = micro_price,
        bid_ask_spread = bid_ask_spread,
        imbalance = paste0(round(bid_imbalance,2), "%"),
        bid = bbid$quantity,
        best_bid = bbid$price) 

colnames(df_mp_example) <- c("$P^{\\text{ASK}}_t$", "$\\text{ASK}$", "$P^{\\text{Mid}}_t$", "$P^{\\text{Micro}}_t$",
                             "$S_t$", "$I_t$", "$\\text{BID}$", "$P^{\\text{BID}}_t$")
                        
df_mp_example %>%
  format_kable(caption)
Last Update: 10 Jun 2023 at 03:23 CET
PASKt ASK PMidt PMicrot St It BID PBIDt
26459.07 10.05993 26459.06 26459.07 0.005 14.2% 1.66527 26459.06

Build an order-book from Depth

An order book is a table with three columns: - price: the price level - bid: the quantity that is on the buy side - ask: the quantity that is on the sell side

We have also added three more columns:

  • buy: the quantity bought at that level
  • sell: the quantity sold at that level
  • net: the balance between buy and sell at that level, computed as net=buysell

OrderBook

The function OrderBook has the purpose of structuring a dataset in the same structure as this Table.

OrderBook(data = depth, from = micro_price*0.99, to = micro_price*1.01, levels = 10, as_datatable = TRUE)

Example: Liquidity curve

# order by price (descending)
plot_data <- dplyr::arrange(depth, price)
# compute cumulated quantity 
plot_data$cumQ <- cumsum(plot_data$quantity)

mp = c(x = micro_price, xend = micro_price, 
       y = 0, yend = plot_data$cumQ[which(plot_data$side == "ASK")[1]])

ggplot()+
  geom_line(data = plot_data, aes(price, cumQ, color = side))+
  geom_segment(aes(x = mp[1], xend = mp[2], y = mp[3], yend = mp[4]), linetype="dashed", color = "red")+
  geom_point(aes(x = mp[1], y = mp[4]), size=2, color = "black")+
  geom_label(aes(x = mp[1], y = mp[4] + mp[4]/10, label = round(micro_price, 2)), size=2, color = "black")+
  scale_color_manual(values = c(ASK = "red", BID = "green"))+
  labs(x = "Price", y = "Cumulated Quantity", color = "Side", caption = caption)+
  theme(legend.position = "top")+
  theme(axis.title  = element_text(face = "bold"), 
        plot.title  = element_text(face = "bold"),
        axis.line = element_line(),
        axis.text.x = element_text(angle = 0, face = "bold", size = 7), 
        axis.text.y = element_text(face = "bold"), 
        axis.title.x = element_text(face = "bold",size = 10),
        axis.title.y = element_text(face = "bold", size = 10),
        plot.subtitle = element_text(face = "italic"),
        plot.caption = element_text(face = "italic"),
        panel.grid.minor.x = element_blank(),
        panel.grid.minor.y = element_blank(),
        panel.grid.major.x = element_blank(),
        panel.grid.major.y = element_blank(),
        panel.border = element_blank(),
        strip.background = element_blank(),
        panel.background = element_blank(),
        strip.text = element_text(angle = 0, face = "bold", size = 7), 
        legend.title = element_text(face = "bold", size = 11),
        legend.text = element_text(face = "italic", size = 10),
        legend.box.background = element_rect(),
        legend.position = "top") 

Example: Depth curve

# ASK orders 
ask <- dplyr::filter(depth, side == "ASK") 
ask <- dplyr::arrange(ask, price) # arrange by price (descending) 
ask$cumQ <- cumsum(ask$quantity) # cumulated quantity 

# BID orders 
bid <- dplyr::filter(depth, side == "BID")
bid <- dplyr::arrange(bid, dplyr::desc(price)) # arrange by price (ascending) 
bid$cumQ <- cumsum(bid$quantity) # cumulated quantity 

plot_data <- dplyr::bind_rows(ask, bid)

mp = c(x = micro_price, xend = micro_price, 
       y = 0, yend = 0)

ggplot()+
  geom_line(data = plot_data, aes(price, cumQ, color = side))+
  geom_label(aes(x = mp[1], y = mp[4], label = round(micro_price, 2)), size=2, color = "black")+
  scale_color_manual(values = c(ASK = "red", BID = "green"))+
  labs(x = "Price", y = "Quantity", color = "Side", caption = caption)+
  theme(legend.position = "top")+
  theme(axis.title  = element_text(face = "bold"), 
        plot.title  = element_text(face = "bold"),
        axis.line = element_line(),
        axis.text.x = element_text(angle = 0, face = "bold", size = 7), 
        axis.text.y = element_text(face = "bold"), 
        axis.title.x = element_text(face = "bold",size = 10),
        axis.title.y = element_text(face = "bold", size = 10),
        plot.subtitle = element_text(face = "italic"),
        plot.caption = element_text(face = "italic"),
        panel.grid.minor.x = element_blank(),
        panel.grid.minor.y = element_blank(),
        panel.grid.major.x = element_blank(),
        panel.grid.major.y = element_blank(),
        panel.border = element_blank(),
        strip.background = element_blank(),
        panel.background = element_blank(),
        strip.text = element_text(angle = 0, face = "bold", size = 7), 
        legend.title = element_text(face = "bold", size = 11),
        legend.text = element_text(face = "italic", size = 10),
        legend.box.background = element_rect(),
        legend.position = "top") 

Example: Quantile detection of supports and resitances

ask <- filter(depth, side == "ASK")
ob <- OrderBook(ask) 
ob$ask <- ob$ask/sum(ob$ask)
ob$col <- ifelse(ob$ask > quantile(ob$ask, 0.8), "up", "dw")

ggplot(ob)+
  geom_bar(data = ob, stat = "identity", aes(price, ask, fill = col), color = "black") +
  geom_label(data = filter(ob, col == "up"), aes(price, ask, label = round(price)), color = "black") +
  coord_flip()+
  scale_x_continuous(breaks = round(ob$price))+
  scale_fill_manual(values = c(up = "red", dw = "gray"))+
  labs(x = "Price", y = "ASK Quantity", caption = caption)+
  theme(axis.title  = element_text(face = "bold"), 
        plot.title  = element_text(face = "bold"),
        axis.line = element_line(),
        axis.text.x = element_text(angle = 0, face = "bold", size = 7), 
        axis.text.y = element_text(face = "bold"), 
        axis.title.x = element_text(face = "bold",size = 10),
        axis.title.y = element_text(face = "bold", size = 10),
        plot.subtitle = element_text(face = "italic"),
        plot.caption = element_text(face = "italic"),
        panel.grid.minor.x = element_blank(),
        panel.grid.minor.y = element_blank(),
        panel.grid.major.x = element_blank(),
        panel.grid.major.y = element_blank(),
        panel.border = element_blank(),
        strip.background = element_blank(),
        panel.background = element_blank(),
        strip.text = element_text(angle = 0, face = "bold", size = 7), 
        legend.title = element_text(face = "bold", size = 11),
        legend.text = element_text(face = "italic", size = 10),
        legend.box.background = element_rect(),
        legend.position = "none") 

bid <- filter(depth, side == "BID")
ob <- OrderBook(bid) 
ob$bid <- ob$bid/sum(ob$bid)
ob$col <- ifelse(ob$bid > quantile(ob$bid, 0.8), "up", "dw")

ggplot(ob)+
  geom_bar(data = ob, stat = "identity", aes(price, bid, fill = col), color = "black") +
  geom_label(data = filter(ob, col == "up"), aes(price, bid, label = round(price)), color = "black") +
  labs(x = "Price", y = "BID Quantity", caption = caption)+
  coord_flip()+
  scale_x_continuous(breaks = round(ob$price))+
  scale_fill_manual(values = c(up = "green", dw = "gray"))+
  theme(axis.title  = element_text(face = "bold"), 
        plot.title  = element_text(face = "bold"),
        axis.line = element_line(),
        axis.text.x = element_text(angle = 0, face = "bold", size = 7), 
        axis.text.y = element_text(face = "bold"), 
        axis.title.x = element_text(face = "bold",size = 10),
        axis.title.y = element_text(face = "bold", size = 10),
        plot.subtitle = element_text(face = "italic"),
        plot.caption = element_text(face = "italic"),
        panel.grid.minor.x = element_blank(),
        panel.grid.minor.y = element_blank(),
        panel.grid.major.x = element_blank(),
        panel.grid.major.y = element_blank(),
        panel.border = element_blank(),
        strip.background = element_blank(),
        panel.background = element_blank(),
        strip.text = element_text(angle = 0, face = "bold", size = 7), 
        legend.title = element_text(face = "bold", size = 11),
        legend.text = element_text(face = "italic", size = 10),
        legend.box.background = element_rect(),
        legend.position = "none")