Python Tutorial: Explore Liquidity Measures with Kaiko’s API

Anastasia Melachrinos
Kaiko
Published in
11 min readMar 25, 2021

--

Market Depth, Slippage, and Spreads

In this tutorial, we will explore how to access and visualize Kaiko’s order book aggregations. Kaiko collects two order book snapshots per minute for thousands of currency pairs. Each order book snapshot contains all bids and asks placed within 10% of the midprice. Order book aggregations are derived from order book snapshots. Kaiko provides three derivations of raw snapshot data: market depth, slippage, and spreads. All three measures can be used to measure liquidity on crypto exchanges. This tutorial will demonstrate how to pull this data from Kaiko’s REST API, format it, and create visualizations.

For more information on the order book aggregations and liquidity measures characteristics available through Kaiko’s API, you can visit Kaiko’s website https://www.kaiko.com/collections/order-books#liquidity and the API documentation https://docs.kaiko.com/#order-book-aggregations-full for even more details on the order book aggregations related endpoints.

1. Dependencies & Kaiko’s API Parameters

We will be making the requests using Kaiko’s package made for this purpose. Afterwards, we will visualize the results. This tutorial is based on a python wrapper to use Kaiko’s data API. Please note that this repository is not officially maintained by Kaiko. Contributions are welcomed! https://github.com/sgheb/kaiko-api

# kaiko data package
import kaiko

# processing the results
import pandas as pd
import datetime as dt
from functools import reduce

# Use environment variables
from os import environ

# plotting packages
import plotly.graph_objects as go
import plotly.express as px
import matplotlib.pyplot as plt
import matplotlib.dates as dates
import seaborn as sns

# hide warnings
import warnings
warnings.filterwarnings('ignore')

Kaiko’s API Order Book Aggregations Endpoints

Kaiko offers an entire suite of order book API endpoints for traders and researchers, built using order book snapshots. Through these endpoints, Kaiko offers several derivations of order book data: market depth, slippage, spread, and averages of these measures over time intervals ranging from 1 minute to 1 day.

Kaiko’s module is composed of several functions allowing private key holders to access order book and trade data more easily: Order Book Snapshots, Order Book Aggregations, and Trades. The present tutorial focuses on order book aggregations endpoints and invites you to discover three main liquidity measures: market depth, slippage, and spreads. For more information on order book aggregations endpoints, you can read our blog post describing the endpoints here: https://docs.kaiko.com/#order-book-aggregations-full

Set your Private API Key

To access Kaiko’s Data through the present module, you need to possess a private API key with access to order book aggregations endpoints.

It’s preferable to have your API key stored in an environment variable named “KAIKO_API_KEY”. Otherwise, you can replace it in the dedicated space ‘your_API_key_HERE’.

If you haven’t already purchased access to Kaiko’s order book data through the API, contact hello@kaiko.com to get your API key.

env = environ.get('KAIKO_API_KEY')
api_key = env if env is not None else 'your_API_key_HERE'
kc = kaiko.KaikoClient(api_key=api_key)

All about order book aggregations endpoints parameters

To access order book aggregations, you will need to input the following parameters: the pair, exchange, start date, end date, and interval. Those parameters are listed and explained in Kaiko’s API Documentation: https://docs.kaiko.com/#order-book-aggregations-depth

Aggregated order book endpoints return the same data as our raw endpoints, except for the fact that the data is an ensemble average. Thus, by inputting a “start-time” and “end-time,” you will get an average of derived liquidity measures for all snapshots within the time period specified. You can also specify the interval — do you want daily aggregations? Hourly aggregations? 5-minute aggregations? Finally, you can input an amount for “slippage,” which will calculate the average price slippage of the order size. Slippage will be calculated based on the quote asset. For example, for btc-usd, a slippage value of ‘100000’ will calculate the amount of price slippage for a $100k buy and sell order.

# exchanges --------------------------------------
exchanges_list = ['krkn', 'stmp', 'bfnx']

# instrument --------------------------------------
pair = 'btc-usd'

# today --------------------------------------
today = dt.datetime.utcnow()
end = today
# from datetime to string
end = end.strftime('%Y-%m-%d %H')

# 30 days ago --------------------------------------
N = 30
d = dt.timedelta(days=N)
start = today - d
# from datetime to string
start = start.strftime('%Y-%m-%d %H')

# interval (1m, 10m, 1h, 1d etc.) -------------------
interv = '1h'

# slippage (The simulated order amount you want to test. Slippage will be calculated based on the quote asset. For example, for btc-usd, a slippage value of '100000' will calculate the amount of price slippage for a $100k buy and sell order)
slipp = '50000'

2. 2% Market Depth

The first liquidity measure we’ll go through is the market depth for a specified pair trading on an exchange.

A quick reminder on market depth

2% market depth is the sum of the quantity of bids and asks placed within 2% from the midprice on a trading pair’s order book. Market makers will place bids and asks at various price levels surrounding the midprice. Depending on the current midprice, we take the sum of all bids and asks within a 2% range, and average this sum over time. This data is derived from raw order book snapshots, which we take twice per minute for all trading pairs. Generally, the greater the market depth for a trading pair, the more liquid the market.

Import Market Depth Series

Once the parameters are defined (as explained above), you can create a datastore from the class OrderBookAggregates, from which you’ll after find the series that interest you. In this example, we focus on the bid and ask volumes (depth) at a 2% level, allowing us to compute average 2% market depth over time. We choose to focus on BTC-USD on three exchanges: Bitstamp, Kraken, and Bitfinex, over the course of 1 month, at hourly intervals. Our tutorial script takes the sum of bid depth and ask depth, but our API outputs these values separately.

Kaiko’s order book aggregations give access to one month of rolling historical 10% order book aggregated data.

dfs = []
# Get the depth data for each exchange, and then group the data on one unique dataframe
for exch in exchanges_list:
try:
# load the data according to above parameters
res = kaiko.OrderBookAggregations(exchange = exch,
instrument = pair,
start_time = start,
end_time = end,
interval = interv,
client=kc)
# From OrderBookAggregations, get the bids and the asks series
tmp_df = res.df[['bid_volume2', 'ask_volume2']]
# Compute Market Depth = bid volume + ask volume at a certain level
tmp_df['depth2'] = tmp_df['bid_volume2'] + tmp_df['ask_volume2']
# keep only depth column
tmp_df = tmp_df['depth2']
tmp_df = tmp_df.to_frame()
# rename each column according to the exchange
tmp_df = tmp_df.rename(columns={'depth2': 'depth2' + '_' + str(exch)})
dfs.append(tmp_df)
except:
# if the request does not match any instrument, continue anyway
print('no match, but continue')

# merge the dataframes of the dfs dictionary of dataframes
depth = reduce(lambda left,right: pd.merge(left,right,on='poll_timestamp'), dfs)
Kraken, Bitstamp and Bitfinex 2% Market Depth Series

Chart BTC/USD Market Depth across exchanges

For comparison in whole volume units, we can use the sum of both Bid and Ask Volume 2% across Bitstamp, Bitfinex, and Kraken from Order Book Aggregations: Depth. The unit in the graph below is in BTC. Generally, the greater the market depth for a trading pair, the more liquid the market, that is why it is relevant to check the market depth of several exchanges.

depth.plot(figsize=(10,5), title='BTC/USD Market Depth across exchanges')

What about bid and ask BTC volumes on each exchange?

Let’s now take a look at the Bid and Ask Volume 2% over the past month for a selected pair (we choose BTC/USD because of historically high order book liquidity). The above example took the sum of bid and ask depth, but this example will chart the separate values. We observe different and sometimes contrasting depth trends over the same period on some exchanges, which gives insights into underlying market dynamics. The market depth can be used as an indicator of a market’s liquidity, allowing investors to compare exchanges and pick which markets will give them the highest profitability.

dfs = []
for exch in exchanges_list:
try:
# load the data according to above parameters
res = kaiko.OrderBookAggregations(exchange = exch,
instrument = pair,
start_time = start,
end_time = end,
interval = interv,
client=kc)
# From OrderBookAggregations, get the bids and the asks series
tmp_df = res.df[['bid_volume2', 'ask_volume2']]
# rename each column according to the exchange
tmp_df = tmp_df.rename(columns={'bid_volume2': 'bidvolume2' + '_' + str(exch)})
tmp_df = tmp_df.rename(columns={'ask_volume2': 'askvolume2' + '_' + str(exch)})
dfs.append(tmp_df)
except:
# if the request does not match any instrument, continue anyway
print('no match, but continue')

# merge the dataframes of the dfs dictionary of dataframes
bidask_volumes = reduce(lambda left,right: pd.merge(left,right,on='poll_timestamp'), dfs)
Bids and Asks Volume at a 2% Level DataFrame
plt.figure(figsize=(10,10))

plt.subplot(3,1,1)
sns.lineplot(x=bidask_volumes.index,
y=bidask_volumes['bidvolume2_krkn'],
label = 'bidvolume2_krkn',
color = 'green')
sns.lineplot(x=bidask_volumes.index,
y=bidask_volumes['askvolume2_krkn'],
label = 'askvolume2_krkn',
color = 'red')
plt.xlabel("Date", size=11)
plt.ylabel("Volume in BTC", size=11)
plt.legend(loc="upper right", prop={'size': 8})
plt.title('Bid & Ask volume2, BTC/USD, Kraken')


plt.subplot(3,1,2)
sns.lineplot(x=bidask_volumes.index,
y=bidask_volumes['bidvolume2_stmp'],
label = 'bidvolume2_stmp',
color = 'green')
sns.lineplot(x=bidask_volumes.index,
y=bidask_volumes['askvolume2_stmp'],
label = 'askvolume2_stmp',
color = 'red')
plt.xlabel("Date", size=11)
plt.ylabel("Volume in BTC", size=11)
plt.legend(loc="upper right", prop={'size': 8})
plt.title('Bid & Ask volume2, BTC/USD, Bitstamp')


plt.subplot(3,1,3)
sns.lineplot(x=bidask_volumes.index,
y=bidask_volumes['bidvolume2_bfnx'],
label = 'bidvolume2_bfnx',
color = 'green')
sns.lineplot(x=bidask_volumes.index,
y=bidask_volumes['askvolume2_bfnx'],
label = 'askvolume2_bfnx',
color = 'red')
plt.xlabel("Date", size=11)
plt.ylabel("Volume in BTC", size=11)
plt.legend(loc="upper right", prop={'size': 8})
plt.title('Bid & Ask volume2, BTC/USD, Bitfinex')

plt.tight_layout()

3. Slippage

The second liquidity measure we’ll go through is the price slippage for simulated order sizes for specified currency pairs.

A quick reminder on slippage

Price slippage is the difference between the expected execution price of a buy or sell order and the price at which the trade is fully executed. Price slippage can result from insufficient order book depth or changes to the bid/ask spread in between the time an order is placed and an order is executed.

Import Bid and Ask Slippage Series

Don’t forget: To use the slippage endpoint, you must input as a parameter an order size (10k, 50k, 100k, etc) in the quote asset of the pair you are interested in. For example, a trader wants to calculate slippage for a 100k USD order for BTC/USD trading on Kraken.

Once the parameter is defined, you can create a datastore from the class OrderBookAggregates, from which you’ll find the series that interest you. In this example, we focus on the ask and bid slippage. Ask slippage simulates a market buy order and Bid Slippage simulates a market sell order (buys are matched with asks and sells are matched with bids, thus we run the market orders through raw order book snapshots to determine the slippage). The below request shows a 1 hour average of slippage at a 50k USD order size, for BTC/USD on Bitstamp, Kraken, and Bitfinex.

Kaiko’s order book aggregations give access to one month of rolling historical data.

dfs = []
# Get the depth data for each exchange, and then group the data on one unique dataframe
for exch in exchanges_list:
try:
# load the data according to above parameters
res = kaiko.OrderBookAggregations(exchange = exch,
instrument = pair,
start_time = start,
end_time = end,
interval = interv,
slippage=slipp,
client=kc)
# From OrderBookAggregations, get the bids and the asks slippage series
tmp_df = res.df[['ask_slippage', 'bid_slippage']]
# rename each column according to the exchange
tmp_df = tmp_df.rename(columns={'ask_slippage': 'ask_slippage' + '_' + str(exch)})
tmp_df = tmp_df.rename(columns={'bid_slippage': 'bid_slippage' + '_' + str(exch)})
dfs.append(tmp_df)
except:
# if the request does not match any instrument, continue anyway
print('no match, but continue')

# merge the dataframes of the dfs dictionary of dataframes
slippage = reduce(lambda left,right: pd.merge(left,right,on='poll_timestamp'), dfs)
Bid and Ask Slippage for BTC-USD on Kraken, Bitstamp and Bitfinex

Chart BTC/USD Slippage across exchanges

Now that we have hourly aggregated slippage data, it is easy to compare price slippage across various markets. For example, a trader is trying to decide which BTC/USD market to execute a 50k USD buy and sell order. Using hourly aggregated order book data, we compared the average Ask and Bid Slippage across 3 BTC/USD markets over time.

The chart shows that hourly average slippage for a 50k USD buy order is consistently lowest on Coinbase. Average slippage was highest (and also the most volatile) on itBit for the studied time period.

plt.figure(figsize=(10,7))
plt.subplot(2,1,1)
sns.lineplot(x=slippage.index,
y=slippage['ask_slippage_krkn'],
label = 'ask_slippage_krkn',
color = 'blue')
sns.lineplot(x=slippage.index,
y=slippage['ask_slippage_bfnx'],
label = 'ask_slippage_bfnx',
color = 'green')
sns.lineplot(x=slippage.index,
y=slippage['ask_slippage_stmp'],
label = 'ask_slippage_stmp',
color = 'orange')
plt.xlabel("Date", size=11)
plt.ylabel("Price Slippage $50k (%)", size=11)
plt.legend(loc="upper right", prop={'size': 8})
plt.title('BTC/USD Price Slippage for a $50k Buy Order')


plt.subplot(2,1,2)
sns.lineplot(x=slippage.index,
y=slippage['bid_slippage_krkn'],
label = 'bid_slippage_krkn',
color = 'blue')
sns.lineplot(x=slippage.index,
y=slippage['bid_slippage_bfnx'],
label = 'bid_slippage_bfnx',
color = 'green')
sns.lineplot(x=slippage.index,
y=slippage['bid_slippage_stmp'],
label = 'bid_slippage_stmp',
color = 'orange')
plt.xlabel("Date", size=11)
plt.ylabel("Price Slippage $50k (%)", size=11)
plt.legend(loc="upper right", prop={'size': 8})
plt.title('BTC/USD Price Slippage for a $50k Sell Order')

plt.tight_layout()

4. Bid-Ask Spread

The final liquidity measure we’ll go through is the bid-ask sread of currency pair.

A quick reminder on bid-ask spread

The bid/ask spread is the difference between the highest price a buyer is willing to pay for an asset and the lowest price a seller is willing to accept. Generally, the narrower the spread the more liquid the market. To learn more about spread, read our in-depth analysis here: https://blog.kaiko.com/bid-ask-spread-as-an-indicator-of-crypto-market-liquidity-b15bdc0a621c.

The spread is calculated by taking the difference between the best bid and the best ask on an asset’s order book at a moment in time.

Import Spread and Mid Price Series

Once the parameters are determined, you can create a datastore from the class OrderBookAggregates, from which you’ll find the series that interest you. In this example, we focus on the spread and midprice. We will normalize the spread by dividing it by the mid-price and then multiplying by 10,000 to get the measure in basis points. The below request shows a 1 hour average of spread for BTC/USD on Bitstamp, Kraken, and Bitfinex.

Kaiko’s order book aggregations give access to one month of rolling historical data.

dfs = []
# Get the depth data for each exchange, and then group the data on one unique dataframe
for exch in exchanges_list:
try:
# load the data according to above parameters
res = kaiko.OrderBookAggregations(exchange = exch,
instrument = pair,
start_time = start,
end_time = end,
interval = interv,
slippage=slipp,
client=kc)
# From OrderBookAggregations, get the bids and the asks series
tmp_df = res.df[['spread', 'mid_price']]
# Compute the "Real Spread" = Spread / Mid Price * 10000
tmp_df['real_spread'] = (tmp_df['spread']/tmp_df['mid_price'])*10000
# keep only the real_spread as a column
tmp_df = tmp_df['real_spread']
tmp_df = tmp_df.to_frame()
# rename each column according to the exchange
tmp_df = tmp_df.rename(columns={'real_spread': 'spread' + '_' + str(exch)})
dfs.append(tmp_df)
except:
# if the request does not match any instrument, continue anyway
print('no match, but continue')

# merge the dataframes of the dfs dictionary of dataframes
spread = reduce(lambda left,right: pd.merge(left,right,on='poll_timestamp'), dfs)
BTC-USD Bid-Ask Spread on Kraken, Bitstamp and Bitfinex

Chart BTC/USD Bid-Ask Spread across exchanges

Now that we have hourly aggregated order book data, it is easy to compare bid/ask spread across various markets. The below charts also show how small changes in spreads on different exchanges can tell a lot about the overall liquidity of a market. For example, Bitstamp has higher average spreads for BTC/USD compared with the two other exchanges. To give you some interpretation, The tighter the spread is, the more attractive a market is for traders.

spread.plot(figsize=(10,5), title='Bid-Ask Spread across exchanges (in Basis Points)')

Hope this tutorial helped you better understand how to use Kaiko’s API through Python. This tutorial has been written by Anastasia Melachrinos, with help from Sacha Ghebali and others from the Kaiko team.

For any feedback or suggestions, email me at anastasia@kaiko.com.

--

--