{binance} Tracking Total Account Balance

I started dabbling in Crypto trading on Binance at the beginning of September 2021. I am really impressed with the interface, which is smooth and full featured (if perhaps a little complicated and confusing!). One of the things that has frustrated me though is not being able to get an idea of whether I’m making progress. There’s no view which shows me the overall status of my account and how this has evolved over time.

However, there is a very comprehensive API, so I can build my own view.

There’s already a Python wrapper around the Binance API. However, I’d prefer to do this project in R, so I’ve started building a {binance} package for R.

Update: Gergely Daróczi has been developing the {binancer} package which already covers a lot of the Binance API.

Install & Load

First we’ll install the nascent package from its GitHub repository.

remotes::install_github("datawookie/binance")

Next, load the library and we’re ready to play.

library(binance)

Authenticate

There is certainly functionality in the Binance API which is accessible without authentication. But, to tap into the interesting stuff you’ll want to create an API key and secret, then use these to authenticate requests.

authenticate(
  key = Sys.getenv("BINANCE_API_KEY"),
  secret = Sys.getenv("BINANCE_API_SECRET")
)

I’m storing these credentials in environment variables. There are better and worse ways to handle these secrets, but this seems like a reasonable compromise.

Daily Snapshot

I’ll start out by retrieving a daily snapshot of my account for months.

snapshot <- rbind(
  wallet_daily_snapshot(start_time = "2021-09-02", limit = 30),
  wallet_daily_snapshot(start_time = "2021-10-02", limit = 30),
  wallet_daily_snapshot(start_time = "2021-11-01", limit = 30)
)
# A tibble: 60 × 4
   type  time                total_asset_of_btc balances        
   <chr> <dttm>              <chr>              <list>          
 1 spot  2021-09-02 23:59:59 0.00000022         <tibble [1 × 4]>
 2 spot  2021-09-03 23:59:59 0.00000044         <tibble [1 × 4]>
 3 spot  2021-09-04 23:59:59 0.00000066         <tibble [1 × 4]>
 4 spot  2021-09-05 23:59:59 0.00407599         <tibble [5 × 4]>
 5 spot  2021-09-06 23:59:59 0.003973           <tibble [5 × 4]>
 6 spot  2021-09-07 23:59:59 0.00095605         <tibble [6 × 4]>
 7 spot  2021-09-08 23:59:59 0.00344274         <tibble [7 × 4]>
 8 spot  2021-09-09 23:59:59 0.00068881         <tibble [7 × 4]>
 9 spot  2021-09-10 23:59:59 0.00066829         <tibble [7 × 4]>
10 spot  2021-09-11 23:59:59 0.00003602         <tibble [7 × 4]>
# … with 50 more rows

The details are wrapped up in the balances list column. However, already the total_asset_of_btc provides a summary of the account with all balances converted to BTC.

If we unnest the balances column then we can see the balance of each coin per day.

snapshot <- snapshot %>% unnest(balances)
# A tibble: 468 × 7
   type  time                total_asset_of_btc asset   free locked   total
   <chr> <dttm>              <chr>              <chr>  <dbl>  <dbl>   <dbl>
 1 spot  2021-09-02 23:59:59 0.00000022         BUSD  0.0110  0      0.0110
 2 spot  2021-09-03 23:59:59 0.00000044         BUSD  0.0219  0      0.0219
 3 spot  2021-09-04 23:59:59 0.00000066         BUSD  0.0329  0      0.0329
 4 spot  2021-09-05 23:59:59 0.00407599         ADA   0.08   19.9   20.0   
 5 spot  2021-09-05 23:59:59 0.00407599         BNB   0       0      0     
 6 spot  2021-09-05 23:59:59 0.00407599         BUSD  0.0438  0      0.0438
 7 spot  2021-09-05 23:59:59 0.00407599         ETH   0.0531  0.032  0.0851
 8 spot  2021-09-05 23:59:59 0.00407599         XRP   0       0      0     
 9 spot  2021-09-06 23:59:59 0.003973           ADA   0.08   19.9   20.0   
10 spot  2021-09-06 23:59:59 0.003973           BNB   0       0      0     
# … with 458 more rows

List of Assets

I’d like to have a list of the various coins I’ve dabbled in.

assets <- snapshot %>% select(asset) %>% arrange(asset) %>% unique()
# A tibble: 10 × 1
   asset
   <chr>
 1 ADA  
 2 BNB  
 3 BTC  
 4 BUSD 
 5 DAI  
 6 ENJ  
 7 ETH  
 8 USDT 
 9 XLM  
10 XRP  

Crypto Pairs

My superficial understanding of Crypto trading (and FOREX trading for that matter) is that we are essentially swapping out one currency for another, attempting to ride the bulls and avoid the bears.

Here are some of my executed trades of ENJ (Enjin Coin) against ETH (Ethereum).

enjeth_trades <- spot_trades_list("ENJ/ETH", start_time = "2021-09-02")
enjeth_trades %>% select(symbol, time, id, order_id, side, price, qty, commission)
# A tibble: 13 × 8
   symbol time                     id  order_id side     price   qty commission
   <chr>  <dttm>                <int>     <int> <chr>    <dbl> <dbl>      <dbl>
 1 ENJETH 2021-09-07 07:15:46 6669282 256940624 BUY   0.00054   75    0.075    
 2 ENJETH 2021-09-08 08:07:16 6675428 257208044 BUY   0.000485  36    0.036    
 3 ENJETH 2021-09-08 08:07:32 6675429 257208044 BUY   0.000485  14    0.014    
 4 ENJETH 2021-09-08 08:15:18 6675451 257199673 BUY   0.00048   17.7  0.0177   
 5 ENJETH 2021-09-08 08:15:33 6675453 257199673 BUY   0.00048   32.3  0.0323   
 6 ENJETH 2021-09-16 14:18:20 6690561 257199768 BUY   0.00047   50    0.000149 
 7 ENJETH 2021-09-20 02:26:18 6695349 258830230 BUY   0.000468  42.7  0.000122 
 8 ENJETH 2021-09-20 07:11:40 6695790 257199809 BUY   0.00046   50    0.000141 
 9 ENJETH 2021-09-20 07:11:40 6695791 257200114 BUY   0.00046   50    0.000141 
10 ENJETH 2021-09-20 12:34:23 6696750 258853892 BUY   0.00045  100    0.000278 
11 ENJETH 2021-09-21 05:08:05 6698656 258853965 BUY   0.00044  100    0.000273 
12 ENJETH 2021-09-26 05:19:29 6709426 258830471 SELL  0.000485  42.7  0.000129 
13 ENJETH 2021-10-27 12:37:32 6781180 257208303 SELL  0.00055   25    0.0000138

Over the last month I’ve bought quite a lot of Enjin Coin. Below are the open orders I have on the same pair, hoping to sell for a small profit.

enjeth_orders <- spot_open_orders("ENJ/ETH")
enjeth_orders %>% select(symbol, time, order_id, side, price, orig_qty)
# A tibble: 7 × 6
  symbol time                 order_id side    price orig_qty
  <chr>  <dttm>                  <int> <chr>   <dbl>    <dbl>
1 ENJETH 2021-09-07 08:16:56 256969607 SELL  0.0007      74.9
2 ENJETH 2021-09-08 08:17:12 257209816 SELL  0.0006      37.5
3 ENJETH 2021-09-08 08:17:19 257209828 SELL  0.00065     37.4
4 ENJETH 2021-09-19 04:04:35 258709098 SELL  0.0007      50  
5 ENJETH 2021-09-20 08:13:36 258891740 SELL  0.0007     100  
6 ENJETH 2021-09-20 17:28:19 258968838 SELL  0.0007     100  
7 ENJETH 2021-09-21 11:22:39 259087648 SELL  0.0007     100 

General Market Information

It’d be useful to get some general information about the pairs that are available for trading on Binance. We can retrieve that with market_exchange_info().

info <- market_exchange_info()

The result is a list, from which we’ll pull out the symbols element.

symbols <- info$symbols %>% select(symbol, status, base, quote)
# A tibble: 1,801 × 4
   symbol  status  base  quote
   <chr>   <chr>   <chr> <chr>
 1 ETHBTC  TRADING ETH   BTC  
 2 LTCBTC  TRADING LTC   BTC  
 3 BNBBTC  TRADING BNB   BTC  
 4 NEOBTC  TRADING NEO   BTC  
 5 QTUMETH TRADING QTUM  ETH  
 6 EOSETH  TRADING EOS   ETH  
 7 SNTETH  TRADING SNT   ETH  
 8 BNTETH  TRADING BNT   ETH  
 9 BCCBTC  BREAK   BCC   BTC  
10 GASBTC  TRADING GAS   BTC  
# … with 1,791 more rows

There are 1818 pairs listed. However, not all of them are always available for trading. Those with a status of BREAK or HALT are experiencing downtime and are not currently enabled for trading.

symbols %>% count(status)
# A tibble: 2 × 2
  status      n
  <chr>   <int>
1 BREAK     447
2 TRADING  1354

Converting Assets to Stable Coin

In order to track the daily balance of my account I’m going to find the value for each coin translated to USDT. First we’ll extract only those pairs which involve USDT and join them with my list of assets.

# Symbols to transfer to USDT.
#
symbols <- rbind(
  symbols %>%
    inner_join(assets, by = c("base" = "asset")) %>%
    filter(quote == "USDT"),
  symbols %>%
    inner_join(assets, by = c("quote" = "asset")) %>%
    filter(base == "USDT")
) %>%
  filter(status == "TRADING") %>% 
  select(symbol, base, quote)
# A tibble: 9 × 3
  symbol   base  quote
  <chr>    <chr> <chr>
1 BTCUSDT  BTC   USDT 
2 ETHUSDT  ETH   USDT 
3 BNBUSDT  BNB   USDT 
4 ADAUSDT  ADA   USDT 
5 XRPUSDT  XRP   USDT 
6 XLMUSDT  XLM   USDT 
7 ENJUSDT  ENJ   USDT 
8 BUSDUSDT BUSD  USDT 
9 USDTDAI  USDT  DAI 

Okay, so now we just need to get some historical data for each of those pairs.

klines <- map_dfr(symbols$symbol, function(symbol) {
  market_klines(symbol, interval = "1d", start_time = "2021-09-01", limit = 61)
})

Let’s use {tidyquant} to take a quick look at the recent history of the ENJ/USDT price.

Price history of ENJ/USDT.

Tracking Account Balance

We now have all of the required ingredients to see the daily account balance translated into a single currency. This is going to make it a lot easier to understand whether my overall crypto strategy is working.

Binance account balance history.

The vertical dashed lines indicate the times of deposits, retrieved using wallet_deposit_history().

Way Forward

My initial work on {binance} has been focused on implementing functions to wrap the endpoints required to do the above analysis. Over the next few weeks I plan on extending the coverage to the complete API. If you’d like to contribute to {binance}, please fork a copy of the code and get hacking!