Aggregating Options Data
Coin Metrics Options Data Aggregation
This notebook demonstrates basic functionality offered by the Coin Metrics Python API Client and Market Data Feed.
Coin Metrics offers a vast assortment of data for hundreds of cryptoassets. The Python API Client allows for easy access to this data using Python without needing to create your own wrappers using requests and other such libraries.
Resources
To understand the data that Coin Metrics offers, feel free to peruse the resources below.
The Coin Metrics API v4 website contains the full set of endpoints and data offered by Coin Metrics.
The Coin Metrics Product Documentation gives detailed, conceptual explanations of the data that Coin Metrics offers.
The API Spec contains a full list of functions.
File Download
Download the entire notebook as either a jupyter notebook to run yourself or as a pdf from the two links below
Notebook Setup
import os
from os import environ
import sys
import pandas as pd
import numpy as np
import seaborn as sns
import logging
from datetime import date, datetime, timedelta
from coinmetrics.api_client import CoinMetricsClient
import json
import logging
from datetime import timezone as timezone_info
import matplotlib.ticker as mticker
from matplotlib.ticker import ScalarFormatter
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
%matplotlib inline
from plotly import graph_objects as gosns.set_theme()
sns.set(rc={'figure.figsize':(14,8)})
sns.set_style("whitegrid",{'axes.grid' : True,'grid.linestyle': '--', 'grid.color': 'gray','axes.edgecolor': 'white'})logging.basicConfig(
    format='%(asctime)s %(levelname)-8s %(message)s',
    level=logging.INFO,
    datefmt='%Y-%m-%d %H:%M:%S'
)# We recommend privately storing your API key in your local environment.
try:
    api_key = environ["CM_API_KEY"]
    logging.info("Using API key found in environment")
except KeyError:
    api_key = ""
    logging.info("API key not found. Using community client")
client = CoinMetricsClient(api_key)2024-09-12 08:53:59 INFO     Using API key found in environmentSet start and end time
end = datetime.utcnow()
start = '2024-09-01'
end = '2024-09-08'
asset = 'eth'OPTIONS CATALOG
To simplify some of the complexity around options contract size, we will investigate only options markets on Deribit, the largest options exchange by volume.
asset_options = client.reference_data_markets(
    type='option',
    exchange='deribit',
    base='eth',
    quote='usd',
    page_size=10000
).to_list()print('Total number of supported options markets: ' + str(len(asset_options)))Total number of supported options markets: 74827Filter out expired/inactive contracts
df_asset_options = pd.DataFrame(asset_options)df_asset_options.head()0
deribit-ETH-10APR20-100-P-option
deribit
option
eth
usd
eth-usd
ETH-10APR20-100-P
eth
100
put
...
2020-03-26T08:00:02.000000000Z
2020-04-10T08:00:00.000000000Z
158.1
offline
1
0.0005
0.0004
0.0004
NaN
NaN
1
deribit-ETH-10APR20-110-P-option
deribit
option
eth
usd
eth-usd
ETH-10APR20-110-P
eth
110
put
...
2020-03-26T08:00:02.000000000Z
2020-04-10T08:00:00.000000000Z
158.1
offline
1
0.0005
0.0004
0.0004
NaN
NaN
2
deribit-ETH-10APR20-120-C-option
deribit
option
eth
usd
eth-usd
ETH-10APR20-120-C
eth
120
call
...
2020-03-26T08:00:02.000000000Z
2020-04-10T08:00:00.000000000Z
158.1
offline
1
0.0005
0.0004
0.0004
NaN
NaN
3
deribit-ETH-10APR20-120-P-option
deribit
option
eth
usd
eth-usd
ETH-10APR20-120-P
eth
120
put
...
2020-03-26T08:00:02.000000000Z
2020-04-10T08:00:00.000000000Z
158.1
offline
1
0.0005
0.0004
0.0004
NaN
NaN
4
deribit-ETH-10APR20-130-C-option
deribit
option
eth
usd
eth-usd
ETH-10APR20-130-C
eth
130
call
...
2020-03-26T08:00:02.000000000Z
2020-04-10T08:00:00.000000000Z
158.1
offline
1
0.0005
0.0004
0.0004
NaN
NaN
5 rows × 22 columns
df_asset_options.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 74827 entries, 0 to 74826
Data columns (total 22 columns):
 #   Column                 Non-Null Count  Dtype 
---  ------                 --------------  ----- 
 0   market                 74827 non-null  object
 1   exchange               74827 non-null  object
 2   type                   74827 non-null  object
 3   base                   74827 non-null  object
 4   quote                  74827 non-null  object
 5   pair                   74827 non-null  object
 6   symbol                 74827 non-null  object
 7   size_asset             74827 non-null  object
 8   strike                 74827 non-null  object
 9   option_contract_type   74827 non-null  object
 10  is_european            74827 non-null  bool  
 11  contract_size          74827 non-null  object
 12  listing                74827 non-null  object
 13  expiration             74827 non-null  object
 14  settlement_price       74117 non-null  object
 15  status                 74827 non-null  object
 16  order_amount_min       74827 non-null  object
 17  order_price_increment  74827 non-null  object
 18  order_taker_fee        74827 non-null  object
 19  order_maker_fee        74827 non-null  object
 20  base_native            3558 non-null   object
 21  quote_native           3558 non-null   object
dtypes: bool(1), object(21)
memory usage: 12.1+ MBdf_asset_options['listing'] = pd.to_datetime(df_asset_options['listing'])
df_asset_options['expiration'] = pd.to_datetime(df_asset_options['expiration'])
df_asset_options = df_asset_options.loc[((df_asset_options.listing > start) & (df_asset_options.listing < end))]
df_asset_options2384
deribit-ETH-10SEP24-1950-C-option
deribit
option
eth
usd
eth-usd
ETH-10SEP24-1950-C
eth
1950
call
...
2024-09-07 08:00:09+00:00
2024-09-10 08:00:00+00:00
2360.48
offline
1
0.0001
0.0003
0.0003
ETH
USD
2385
deribit-ETH-10SEP24-1950-P-option
deribit
option
eth
usd
eth-usd
ETH-10SEP24-1950-P
eth
1950
put
...
2024-09-07 08:00:09+00:00
2024-09-10 08:00:00+00:00
2360.48
offline
1
0.0001
0.0003
0.0003
ETH
USD
2386
deribit-ETH-10SEP24-2000-C-option
deribit
option
eth
usd
eth-usd
ETH-10SEP24-2000-C
eth
2000
call
...
2024-09-07 08:00:09+00:00
2024-09-10 08:00:00+00:00
2360.48
offline
1
0.0001
0.0003
0.0003
ETH
USD
2387
deribit-ETH-10SEP24-2000-P-option
deribit
option
eth
usd
eth-usd
ETH-10SEP24-2000-P
eth
2000
put
...
2024-09-07 08:00:09+00:00
2024-09-10 08:00:00+00:00
2360.48
offline
1
0.0001
0.0003
0.0003
ETH
USD
2388
deribit-ETH-10SEP24-2050-C-option
deribit
option
eth
usd
eth-usd
ETH-10SEP24-2050-C
eth
2050
call
...
2024-09-07 08:00:09+00:00
2024-09-10 08:00:00+00:00
2360.48
offline
1
0.0001
0.0003
0.0003
ETH
USD
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
74822
deribit-ETH-9SEP24-2800-P-option
deribit
option
eth
usd
eth-usd
ETH-9SEP24-2800-P
eth
2800
put
...
2024-09-06 08:00:10+00:00
2024-09-09 08:00:00+00:00
2311.75
offline
1
0.0001
0.0003
0.0003
ETH
USD
74823
deribit-ETH-9SEP24-2850-C-option
deribit
option
eth
usd
eth-usd
ETH-9SEP24-2850-C
eth
2850
call
...
2024-09-06 08:00:10+00:00
2024-09-09 08:00:00+00:00
2311.75
offline
1
0.0001
0.0003
0.0003
ETH
USD
74824
deribit-ETH-9SEP24-2850-P-option
deribit
option
eth
usd
eth-usd
ETH-9SEP24-2850-P
eth
2850
put
...
2024-09-06 08:00:10+00:00
2024-09-09 08:00:00+00:00
2311.75
offline
1
0.0001
0.0003
0.0003
ETH
USD
74825
deribit-ETH-9SEP24-2900-C-option
deribit
option
eth
usd
eth-usd
ETH-9SEP24-2900-C
eth
2900
call
...
2024-09-06 10:37:00+00:00
2024-09-09 08:00:00+00:00
2311.75
offline
1
0.0001
0.0003
0.0003
ETH
USD
74826
deribit-ETH-9SEP24-2900-P-option
deribit
option
eth
usd
eth-usd
ETH-9SEP24-2900-P
eth
2900
put
...
2024-09-06 10:37:00+00:00
2024-09-09 08:00:00+00:00
2311.75
offline
1
0.0001
0.0003
0.0003
ETH
USD
376 rows × 22 columns
TRADE VOLUME
On Deribit, each option contract provides notional exposure to 1 unit of the underlying asset (i.e. 1 BTC or 1 ETH). To calculate options volume, we must multiply the 'amount' field (representing the number of contracts traded) by the USD price of the underlying asset.
list_options_markets = list(df_asset_options.market.unique())full_vol = pd.DataFrame()
batch_size = 420
for i in range(0, len(list_options_markets),batch_size):
    mkt_batch = list_options_markets[i:i+batch_size]
    print(str('Retrieving batch of trades for markets ' + str(i) + ' - ' + str(i+batch_size) + '...'))
    vol_batch = client.get_market_trades(
        markets=mkt_batch,
        start_time = start,
        end_time= end,
        page_size=10000
    ).to_dataframe()
    print('Retrieved batch of ' + str(len(vol_batch)) + ' trades')
    
    full_vol = pd.concat((full_vol, vol_batch), axis = 0, ignore_index=False)
    print('Total of ' + str(len(full_vol)) + ' trades\n')
full_vol = full_vol.reset_index(drop=True)Retrieving batch of trades for markets 0 - 420...
Retrieved batch of 12012 trades
Total of 12012 tradesfull_vol = full_vol[['market','time','amount']]
full_vol = full_vol[~(full_vol['time'] > end)]
full_vol.sort_values('time')1071
deribit-ETH-3SEP24-2375-P-option
2024-09-01 06:55:13.947000+00:00
1
1072
deribit-ETH-3SEP24-2375-P-option
2024-09-01 08:04:42.145000+00:00
1
1073
deribit-ETH-3SEP24-2375-P-option
2024-09-01 08:07:41.254000+00:00
3
2429
deribit-ETH-4SEP24-2550-C-option
2024-09-01 08:33:21.911000+00:00
1
2805
deribit-ETH-4SEP24-2700-C-option
2024-09-01 08:47:26.803000+00:00
14
...
...
...
...
11208
deribit-ETH-9SEP24-2300-C-option
2024-09-07 23:54:16.170000+00:00
9
9225
deribit-ETH-8SEP24-2325-C-option
2024-09-07 23:54:52.991000+00:00
2
8166
deribit-ETH-8SEP24-2050-P-option
2024-09-07 23:57:33.509000+00:00
2
9650
deribit-ETH-8SEP24-2400-C-option
2024-09-07 23:58:04.697000+00:00
1
9651
deribit-ETH-8SEP24-2400-C-option
2024-09-07 23:58:53.038000+00:00
1
10824 rows × 3 columns
Retrieve 1-minute Reference Rate
df_refrate = client.get_asset_metrics(
    assets=asset,
    frequency='1m',
    metrics='ReferenceRateUSD',
    start_time=start,
    end_time=end
).to_dataframe()df_refrate = df_refrate.set_index('time').sort_index().drop(columns=['asset'])
df_refratetime
2024-09-01 00:00:00+00:00
2512.79
2024-09-01 00:01:00+00:00
2512.41
2024-09-01 00:02:00+00:00
2510.65
2024-09-01 00:03:00+00:00
2513.37
2024-09-01 00:04:00+00:00
2511.32
...
...
2024-09-08 23:55:00+00:00
2298.15
2024-09-08 23:56:00+00:00
2297.49
2024-09-08 23:57:00+00:00
2294.79
2024-09-08 23:58:00+00:00
2296.77
2024-09-08 23:59:00+00:00
2298.61
11520 rows × 1 columns
full_vol['time'] = full_vol['time'].round('T')
# Merge trades data with 1s reference rate
full_vol = full_vol.merge(df_refrate.reset_index(),how='left', on='time')Calculate USD value of trades
full_vol['USD Volume'] = full_vol['amount'] * full_vol['ReferenceRateUSD']full_vol0
deribit-ETH-10SEP24-1950-P-option
2024-09-07 09:06:00+00:00
5
2293.8
11469.0
1
deribit-ETH-10SEP24-1950-P-option
2024-09-07 09:43:00+00:00
10
2279.45
22794.5
2
deribit-ETH-10SEP24-1950-P-option
2024-09-07 09:43:00+00:00
9
2279.45
20515.05
3
deribit-ETH-10SEP24-1950-P-option
2024-09-07 09:43:00+00:00
1
2279.45
2279.45
4
deribit-ETH-10SEP24-1950-P-option
2024-09-07 11:42:00+00:00
5
2285.0
11425.0
...
...
...
...
...
...
10819
deribit-ETH-9SEP24-2750-C-option
2024-09-06 17:38:00+00:00
1
2235.41
2235.41
10820
deribit-ETH-9SEP24-2750-C-option
2024-09-07 17:02:00+00:00
5
2288.46
11442.3
10821
deribit-ETH-9SEP24-2750-C-option
2024-09-07 18:21:00+00:00
45
2278.46
102530.7
10822
deribit-ETH-9SEP24-2750-C-option
2024-09-07 18:21:00+00:00
23
2278.46
52404.58
10823
deribit-ETH-9SEP24-2800-P-option
2024-09-06 17:37:00+00:00
1
2233.79
2233.79
10824 rows × 5 columns
vol_sum = full_vol.groupby(pd.Grouper(key='time', axis=0, freq='1D', sort=True)).sum()vol_sum.to_csv('./deribit_' + str(asset).upper() + '_options_vol_' + str(start) + '_to_' + str(end) + '.csv')vol_sum[['USD Volume']]time
2024-09-01 00:00:00+00:00
5424658.14
2024-09-02 00:00:00+00:00
19562749.43
2024-09-03 00:00:00+00:00
40646881.35
2024-09-04 00:00:00+00:00
63609486.54
2024-09-05 00:00:00+00:00
44657679.33
2024-09-06 00:00:00+00:00
158263998.37
2024-09-07 00:00:00+00:00
44401580.5
v = vol_sum.plot.line(y='USD Volume')
v.set_xlabel("Date", fontsize = 15)
plt.setp(v.get_yticklabels(), fontsize=13)
plt.setp(v.get_xticklabels(), fontsize=13)
v.set_xlabel("")
plt.xlim([vol_sum.index[0], vol_sum.index[-1]])
v.set_ylabel("Daily Volume (USD)\n", fontsize = 15)
v.set_title(('\nDeribit ' + str(asset).upper() + ' Options Volume\n'),fontsize=23)
v.get_legend().remove()
v.yaxis.set_major_formatter(mticker.FuncFormatter(lambda x, pos: '${:,.1f}'.format(x/1000000) + 'M'))
OPEN INTEREST
Open interest represents the number of contracts that are currently outstanding and not settled for a specific derivatives market.
# Drop markets without open interest data
oi_cat = df_asset_options.loc[df_asset_options.listing < end, 'market'].tolist()
len(oi_cat)376full_oi = pd.DataFrame()
batch_size = 420
for i in range(0, len(oi_cat),batch_size):
    mkt_batch = oi_cat[i:i+batch_size]
    print(str('\nRetrieving EOD open interest for markets ' + str(i) + ' through ' + str(i+batch_size) + '...'))
    
    start_date = pd.to_datetime(start)
    end_date = pd.to_datetime(end)
    delta = timedelta(days=1)
    print(str(start_date) + '\n...')
    
    while start_date <= end_date:
        
        oi_batch = client.get_market_open_interest(
            markets=mkt_batch,
            start_time = start_date.strftime("%Y-%m-%d"),
            limit_per_market = 1,
            paging_from='start',
            timezone='America/New_York'
        ).to_dataframe()
        #print(oi_batch)
        full_oi = pd.concat((full_oi, oi_batch), axis = 0, ignore_index=False)
        start_date += delta
        
    print(start_date)Retrieving EOD open interest for markets 0 through 420...
2024-09-01 00:00:00
...
2024-09-09 00:00:00full_oi.info()<class 'pandas.core.frame.DataFrame'>
Index: 2534 entries, 0 to 193
Data columns (total 6 columns):
 #   Column          Non-Null Count  Dtype              
---  ------          --------------  -----              
 0   market          2534 non-null   string             
 1   time            2534 non-null   datetime64[ns, UTC]
 2   contract_count  2534 non-null   Int64              
 3   value_usd       2534 non-null   Float64            
 4   database_time   2534 non-null   datetime64[ns, UTC]
 5   exchange_time   2534 non-null   datetime64[ns, UTC]
dtypes: Float64(1), Int64(1), datetime64[ns, UTC](3), string(1)
memory usage: 143.5 KBoi_sum = full_oi.groupby(pd.Grouper(key='time', axis=0, freq='1D', sort=True))['value_usd'].sum()oi_sum.to_csv('./deribit_' + str(asset).upper() + '_options_oi_' + str(start) + '_to_' + str(end) + '.csv')
oi_sumtime
2024-09-01 00:00:00+00:00             0.0
2024-09-02 00:00:00+00:00      6467612.33
2024-09-03 00:00:00+00:00     24927750.02
2024-09-04 00:00:00+00:00     55323320.59
2024-09-05 00:00:00+00:00     59305999.71
2024-09-06 00:00:00+00:00      51922555.8
2024-09-07 00:00:00+00:00    144108764.26
2024-09-08 00:00:00+00:00    122269418.49
Freq: D, Name: value_usd, dtype: Float64#p = sns.lineplot(data=vol_sum, x="time", y="USD Volume")
oi = oi_sum.plot.area(y='value_usd')
oi.set_xlabel("Date", fontsize = 15)
plt.setp(oi.get_yticklabels(), fontsize=13)
plt.setp(oi.get_xticklabels(), fontsize=13)
oi.set_xlabel("")
plt.xlim([oi_sum.index[0], oi_sum.index[-1]])
oi.set_ylabel("Daily Open Interest (USD)\n", fontsize = 15)
oi.set_title(('\nDeribit ' + str(asset).upper() + ' Options Open Interest\n'),fontsize=23)
# oi.get_legend().remove()
oi.yaxis.set_major_formatter(mticker.FuncFormatter(lambda x, pos: '${:,.2f}'.format(x/1_000_000) + 'M'))
Split into Calls/Puts
full_oi_split = full_oi.merge(df_asset_options[['market','option_contract_type']],how='left', on='market')full_oi_split0
deribit-ETH-10SEP24-1950-C-option
2024-09-07 08:00:00+00:00
0
0.0
2024-09-07 08:00:19.312717+00:00
2024-09-07 08:00:00+00:00
call
1
deribit-ETH-10SEP24-1950-P-option
2024-09-07 08:00:00+00:00
0
0.0
2024-09-07 08:00:18.188738+00:00
2024-09-07 08:00:00+00:00
put
2
deribit-ETH-10SEP24-2000-C-option
2024-09-07 08:00:00+00:00
0
0.0
2024-09-07 08:00:18.188738+00:00
2024-09-07 08:00:00+00:00
call
3
deribit-ETH-10SEP24-2000-P-option
2024-09-07 08:00:00+00:00
0
0.0
2024-09-07 08:00:18.708225+00:00
2024-09-07 08:00:00+00:00
put
4
deribit-ETH-10SEP24-2050-C-option
2024-09-07 08:00:00+00:00
0
0.0
2024-09-07 08:00:18.708225+00:00
2024-09-07 08:00:00+00:00
call
...
...
...
...
...
...
...
...
2529
deribit-ETH-9SEP24-2800-P-option
2024-09-08 04:00:00+00:00
1
2285.35
2024-09-08 04:00:06.461248+00:00
2024-09-08 04:00:00+00:00
put
2530
deribit-ETH-9SEP24-2850-C-option
2024-09-08 04:00:00+00:00
0
0.0
2024-09-08 04:00:01.855497+00:00
2024-09-08 04:00:00+00:00
call
2531
deribit-ETH-9SEP24-2850-P-option
2024-09-08 04:00:00+00:00
0
0.0
2024-09-08 04:00:03.353208+00:00
2024-09-08 04:00:00+00:00
put
2532
deribit-ETH-9SEP24-2900-C-option
2024-09-08 04:00:00+00:00
0
0.0
2024-09-08 04:00:04.393566+00:00
2024-09-08 04:00:00+00:00
call
2533
deribit-ETH-9SEP24-2900-P-option
2024-09-08 04:00:00+00:00
0
0.0
2024-09-08 04:00:02.869006+00:00
2024-09-08 04:00:00+00:00
put
2534 rows × 7 columns
calls = full_oi_split.loc[(full_oi_split.option_contract_type == 'call')]
call_sum_split = calls.groupby(pd.Grouper(key='time', axis=0, freq='1D', sort=True))[['value_usd']].sum()
puts = full_oi_split.loc[(full_oi_split.option_contract_type == 'put')]
put_sum_split = puts.groupby(pd.Grouper(key='time', axis=0, freq='1D', sort=True))[['value_usd']].sum()put_sum_split = put_sum_split.rename(columns={"value_usd": "Puts - USD Value", "contract_count": "Puts - Contract Count" })
call_sum_split = call_sum_split.rename(columns={"value_usd": "Calls - USD Value", "contract_count": "Calls - Contract Count" })full_oi_split = call_sum_split.merge(put_sum_split,how='left', on='time')
full_oi_splittime
2024-09-01 00:00:00+00:00
0.0
0.0
2024-09-02 00:00:00+00:00
4987937.59
1479674.74
2024-09-03 00:00:00+00:00
14177673.6
10750076.42
2024-09-04 00:00:00+00:00
30418324.31
24904996.28
2024-09-05 00:00:00+00:00
37814891.94
21491107.77
2024-09-06 00:00:00+00:00
24772293.7
27150262.1
2024-09-07 00:00:00+00:00
73624086.44
70484677.82
2024-09-08 00:00:00+00:00
61750871.09
60518547.4
full_oi_split['Put/Call Ratio'] = full_oi_split['Puts - USD Value'] / full_oi_split['Calls - USD Value']
full_oi_split[['Put/Call Ratio']]time
2024-09-01 00:00:00+00:00
NaN
2024-09-02 00:00:00+00:00
0.296651
2024-09-03 00:00:00+00:00
0.75824
2024-09-04 00:00:00+00:00
0.81875
2024-09-05 00:00:00+00:00
0.568324
2024-09-06 00:00:00+00:00
1.095993
2024-09-07 00:00:00+00:00
0.957359
2024-09-08 00:00:00+00:00
0.980044
full_oi_split.to_csv('./deribit_' + str(asset).upper() + '_options_oi_calls_puts_' + str(start) + '_to_' + str(end) + '.csv')pc = sns.lineplot(data=full_oi_split[['Put/Call Ratio']], x="time", y="Put/Call Ratio")
pc.set_xlabel("Date", fontsize = 15)
plt.setp(pc.get_yticklabels(), fontsize=13)
plt.setp(pc.get_xticklabels(), fontsize=13)
pc.set_xlabel("")
plt.xlim([full_oi_split.index[0], full_oi_split.index[-1]])
pc.set_ylabel("Put/Call Ratio\n", fontsize = 15)
pc.set_title(('\nDeribit ' + str(asset).upper() + ' Options\nPut/Call Ratio\n'),fontsize=23)
pc.yaxis.set_major_formatter(mticker.FuncFormatter(lambda x, pos: '{:,.2f}'.format(x)))
Last updated
Was this helpful?