Comparing Stablecoin Prices Using Different Pricing Methods

Since Tether's launch in 2014, stablecoins have grown to become one of the most dominant sectors in the world of digital assets. Many considered them to be crypto's "killer app," providing global access to stable savings and payment rails beyond the rigid confines of the traditional banking system. In this walkthrough, we use Coin Metrics Market Data Feed and CM Prices to explore the various venues where these assets are traded, and examine various pricing methodologies that allow us to better understand how they perform in comparison to the underlying fiat currencies.
Resources
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.
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
from os import environ
import sys
import pandas as pd
import seaborn as sns
import logging
from datetime import date, datetime, timedelta
from coinmetrics.api_client import CoinMetricsClient
import json
import logging
from pytz import timezone as timezone_conv
from datetime import timezone as timezone_info
import matplotlib
import matplotlib.dates as mdates
from matplotlib.dates import MonthLocator, DateFormatter, YearLocator, AutoDateLocator
from matplotlib.ticker import NullFormatter
import matplotlib.pyplot as plt
import plotly.express as px
import numpy as np
%matplotlib inline
sns.set_theme()
plt.rcParams.update({'font.size': 16, 'font.family': 'arial'})
sns.set(rc={'figure.figsize':(14,8)})
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-10-03 11:41:34 INFO Using API key found in environment
Get Stablecoin Markets
The catalog/markets endpoint returns a list of available markets along with time ranges of available data. Users can pass in a list of markets, exchanges, or market types (spot, futures, options). We can retrieve our stablecoin markets by fetching a list of all 'spot' markets, then filtering for the markets where the 'base' or 'quote' parameter is equivalent to our stablecoin of interest.
ticker = 'usdt'
stablecoin_markets = client.reference_data_markets(
type='spot',
asset=ticker,
page_size=10000
).to_dataframe()[['market','exchange','base','quote','symbol']]
client.reference_data_markets(
type='spot',
asset=ticker,
page_size=10000
).to_dataframe().info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 19949 entries, 0 to 19948
Data columns (total 37 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 market 19949 non-null string
1 exchange 19949 non-null string
2 base 19949 non-null string
3 quote 19949 non-null string
4 pair 19949 non-null string
5 symbol 19808 non-null string
6 type 19949 non-null string
7 size_asset 0 non-null Int64
8 margin_asset 0 non-null Int64
9 strike 0 non-null Int64
10 option_contract_type 0 non-null Int64
11 is_european 0 non-null Int64
12 contract_size 0 non-null Int64
13 tick_size 0 non-null Int64
14 multiplier_size 0 non-null Int64
15 listing 0 non-null Int64
16 expiration 0 non-null Int64
17 settlement_price 0 non-null Int64
18 pool_config_id 0 non-null Int64
19 contract_address 0 non-null Int64
20 fee 0 non-null Int64
21 price_includes_fee 0 non-null Int64
22 variable_fee 0 non-null Int64
23 base_address 0 non-null Int64
24 quote_address 0 non-null Int64
25 status 11787 non-null string
26 order_amount_increment 11436 non-null Float64
27 order_amount_min 6300 non-null Float64
28 order_amount_max 2567 non-null Float64
29 order_price_increment 12022 non-null Float64
30 order_price_min 2012 non-null Float64
31 order_price_max 759 non-null Float64
32 order_size_min 5835 non-null Float64
33 order_taker_fee 8332 non-null Float64
34 order_maker_fee 8332 non-null Float64
35 margin_trading_enabled 6926 non-null boolean
36 experimental 0 non-null Int64
dtypes: Float64(9), Int64(19), boolean(1), string(8)
memory usage: 6.1 MB
0
bibox-1inch-usdt-spot
bibox
1inch
usdt
1INCH_USDT
1
bibox-aaa-usdt-spot
bibox
aaa
usdt
AAA_USDT
2
bibox-aave-usdt-spot
bibox
aave
usdt
AAVE_USDT
3
bibox-ac-usdt-spot
bibox
ac
usdt
AC_USDT
4
bibox-acmd-usdt-spot
bibox
acmd
usdt
ACMD_USDT
...
...
...
...
...
...
19944
zb.com-yfii-usdt-spot
zb.com
yfii
usdt
yfii_usdt
19945
zb.com-ygg-usdt-spot
zb.com
ygg
usdt
ygg_usdt
19946
zb.com-zb-usdt-spot
zb.com
zb
usdt
zb_usdt
19947
zb.com-zkn-usdt-spot
zb.com
zkn
usdt
zkn_usdt
19948
zb.com-zrx-usdt-spot
zb.com
zrx
usdt
zrx_usdt
19949 rows × 5 columns
markets_by_exchange = pd.DataFrame(stablecoin_markets['exchange'].value_counts()).reset_index()
markets_by_exchange['count'] = markets_by_exchange['count'].astype(int)
markets_by_exchange.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 30 entries, 0 to 29
Data columns (total 2 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 exchange 30 non-null string
1 count 30 non-null int64
dtypes: int64(1), string(1)
memory usage: 608.0 bytes
fig, ax1 = plt.subplots()
ax1.bar(x=markets_by_exchange['exchange'], height=markets_by_exchange['count'], width=0.8)
plt.setp(ax1.get_xticklabels(), rotation=45);
ax1.set_facecolor("white")
plt.grid(color = 'black', linestyle = '--', linewidth = 0.2)
plt.title('\nCount of '+ str(ticker).upper() + ' Markets \nby Exchange\n',fontdict={'fontsize':20,'font':'arial'});

Get stablecoin prices
Single market trades
Trades are one of the foundational data types we collect from exchanges. From raw trades data, we can construct additional aggregated metrics.
market = 'coinbase-usdt-usd-spot'
coinbase_trades = client.get_market_trades(
markets = market,
limit_per_market = 100,
paging_from = 'end'
).to_dataframe()
coinbase_trades
0
coinbase-usdt-usd-spot
2024-10-03 16:37:50.356121+00:00
109485916
3000.0
0.99985
2024-10-03 16:37:51.099860+00:00
buy
1
coinbase-usdt-usd-spot
2024-10-03 16:37:52.857847+00:00
109485917
3000.0
0.99985
2024-10-03 16:37:53.278230+00:00
buy
2
coinbase-usdt-usd-spot
2024-10-03 16:37:54.748770+00:00
109485918
1555.08
0.99985
2024-10-03 16:37:55.566500+00:00
buy
3
coinbase-usdt-usd-spot
2024-10-03 16:37:55.358549+00:00
109485919
3000.0
0.99985
2024-10-03 16:37:55.652974+00:00
buy
4
coinbase-usdt-usd-spot
2024-10-03 16:37:59.787414+00:00
109485920
2069.41
0.99985
2024-10-03 16:38:00.856793+00:00
buy
...
...
...
...
...
...
...
...
95
coinbase-usdt-usd-spot
2024-10-03 16:41:24.050200+00:00
109486011
19.56
0.99985
2024-10-03 16:41:24.700790+00:00
buy
96
coinbase-usdt-usd-spot
2024-10-03 16:41:24.746299+00:00
109486012
1170.67
0.99985
2024-10-03 16:41:25.211754+00:00
buy
97
coinbase-usdt-usd-spot
2024-10-03 16:41:26.109516+00:00
109486013
7.18
0.99985
2024-10-03 16:41:26.660344+00:00
buy
98
coinbase-usdt-usd-spot
2024-10-03 16:41:26.855646+00:00
109486014
3000.0
0.99984
2024-10-03 16:41:27.752326+00:00
sell
99
coinbase-usdt-usd-spot
2024-10-03 16:41:34.937992+00:00
109486015
23.49
0.99984
2024-10-03 16:41:35.141694+00:00
sell
100 rows × 7 columns
Single market candles
From raw trades data, we construct OHLC candles for each market. Candles include the following data types:
price_open: The opening price of the candle.
price_high: The high price of the candle.
price_low: The low price of the candle.
price_close: The close price of the candle.
vwap: The volume-weighted average price of the candle.
volume: The volume of the candle in units of the base asset.
candle_usd_volume: The volume of the candle in units of U.S. dollars.
candle_trades_count: The number of trades in the candle interval.
market = 'coinbase-usdt-usd-spot'
coinbase_candles = client.get_market_candles(
markets = market,
frequency = '1d'
).to_dataframe()
coinbase_candles
0
coinbase-usdt-usd-spot
2021-05-04 00:00:00+00:00
1.002
1.0006
1.003
0.999
1.000696
24564061.73
24581147.409593
30527
1
coinbase-usdt-usd-spot
2021-05-05 00:00:00+00:00
1.0006
1.0013
1.002
0.9997
1.000816
40170830.16
40203590.541009
43688
2
coinbase-usdt-usd-spot
2021-05-06 00:00:00+00:00
1.0013
1.0009
1.002
1.0004
1.001023
51129166.79
51181449.603235
51177
3
coinbase-usdt-usd-spot
2021-05-07 00:00:00+00:00
1.0008
1.0011
1.0018
1.0
1.001045
44247619.59
44293836.840656
48729
4
coinbase-usdt-usd-spot
2021-05-08 00:00:00+00:00
1.001
1.0016
1.0022
1.0009
1.00147
26972741.16
27012382.918589
50140
...
...
...
...
...
...
...
...
...
...
...
1243
coinbase-usdt-usd-spot
2024-09-28 00:00:00+00:00
1.00012
1.00011
1.00021
1.0
1.000127
69876132.62
69884972.64875
33216
1244
coinbase-usdt-usd-spot
2024-09-29 00:00:00+00:00
1.00011
1.00013
1.00021
1.00001
1.000107
85000340.62
85009396.570012
34237
1245
coinbase-usdt-usd-spot
2024-09-30 00:00:00+00:00
1.00014
0.99971
1.00014
0.99958
0.999863
250585092.98
250550678.3691
81461
1246
coinbase-usdt-usd-spot
2024-10-01 00:00:00+00:00
0.9997
0.99975
0.99991
0.99923
0.999677
460273457.69
460124819.483157
169777
1247
coinbase-usdt-usd-spot
2024-10-02 00:00:00+00:00
0.99975
0.99977
1.0
0.99958
0.999796
345349657.46
345279261.97222
144072
1248 rows × 10 columns
ax1 = plt.subplot()
coinbase_price = sns.lineplot(data=coinbase_candles,y=coinbase_candles.vwap,x=coinbase_candles.time)
plt.setp(ax1.get_xticklabels(), rotation=45);
ax1.set_facecolor("white")
plt.grid(color = 'black', linestyle = '--', linewidth = 0.2)
coinbase_price.set_xlabel("", fontsize = 15)
coinbase_price.set_ylabel("Price", fontsize = 15)
coinbase_price.set_title('\nCoinbase USDT-USD\n', fontsize = 18, font = 'arial');

Reference Rate Candles
We offer reference rates quoted in USD, Euro, Bitcoin, and Ethereum. We now support these quote currencies for our entire reference rates coverage universe of over 500 assets and for all of our frequencies, including 1s, 1m, 1h, 1d-ny-close and 1d.
Current composition of markets for USDT-USD Reference Rate pair (as of May 17, 2023):
"coinbase-usdt-usd-spot",
"coinbase-eth-usdt-spot",
"coinbase-btc-usdt-spot",
"kraken-usdt-usd-spot",
"binance-btc-usdt-spot",
"binance-eth-usdt-spot",
"crypto.com-usdt-usd-spot"
pairs = client.catalog_asset_pair_candles().to_dataframe()
2024-10-03 11:41:41 WARNING /catalog/ endpoints will be deprecated in the future. Consider using /catalog-v2/ and /reference-data/ endpoints instead.
pairs.loc[pairs['pair']=='usdt-usd']
8640
usdt-usd
1m
2013-12-28 00:00:00+00:00
2024-10-03 16:39:00+00:00
8641
usdt-usd
5m
2013-12-28 00:00:00+00:00
2024-10-03 16:35:00+00:00
8642
usdt-usd
10m
2013-12-28 00:00:00+00:00
2024-10-03 16:30:00+00:00
8643
usdt-usd
15m
2013-12-28 00:00:00+00:00
2024-10-03 16:15:00+00:00
8644
usdt-usd
30m
2013-12-28 00:00:00+00:00
2024-10-03 16:00:00+00:00
8645
usdt-usd
1h
2013-12-28 00:00:00+00:00
2024-10-03 15:00:00+00:00
8646
usdt-usd
4h
2013-12-28 00:00:00+00:00
2024-10-03 12:00:00+00:00
8647
usdt-usd
1d
2013-12-28 00:00:00+00:00
2024-10-02 00:00:00+00:00
pair_candles = client.get_pair_candles(
pairs='usdt-usd',
start_time = datetime.now() - timedelta(weeks=4),
frequency='1d'
).to_dataframe()
pair_candles.tail()
22
usdt-usd
2024-09-28 00:00:00+00:00
1.00016
1.000153
1.00053
0.999861
23
usdt-usd
2024-09-29 00:00:00+00:00
1.000116
1.00013
1.000492
0.999772
24
usdt-usd
2024-09-30 00:00:00+00:00
1.00013
0.999649
1.00053
0.99937
25
usdt-usd
2024-10-01 00:00:00+00:00
0.999639
0.9998
1.000897
0.998931
26
usdt-usd
2024-10-02 00:00:00+00:00
0.9998
0.99982
1.000601
0.999219
prices = pair_candles[['price_open','price_close','price_high','price_low','time']].set_index('time')
fig, ax = plt.subplots()
ax.ticklabel_format(useOffset=False)
width = 0.8
width2 = .1
ax.set_facecolor("white")
plt.grid(color = 'black', linestyle = '--', linewidth = 0.2)
plt.title('\n'+ str(ticker).upper() + ' Reference Rate Candles (1D)',fontdict={'fontsize':20,'font':'arial'});
up = prices[prices.price_close>=prices.price_open]
down = prices[prices.price_close<prices.price_open]
col1 = 'green'
col2 = 'red'
#plot prices
ax.bar(up.index,up.price_close-up.price_open,width,bottom=up.price_open,color=col1)
ax.bar(up.index,up.price_high-up.price_close,width2,bottom=up.price_close,color=col1)
ax.bar(up.index,up.price_low-up.price_open,width2,bottom=up.price_open,color=col1)
ax.bar(down.index,down.price_close-down.price_open,width,bottom=down.price_open,color=col2)
ax.bar(down.index,down.price_high-down.price_open,width2,bottom=down.price_open,color=col2)
ax.bar(down.index,down.price_low-down.price_close,width2,bottom=down.price_close,color=col2)
#rotate x-axis tick labels
plt.xticks(rotation=45, ha='right')
ax.xaxis.set_minor_locator(MonthLocator(bymonthday=30))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %d'))
plt.show()

Reference Rate
rr_catalog = client.catalog_asset_metrics_v2(assets='usdt', metrics='ReferenceRateUSD').to_dataframe()
rr_catalog
0
usdt
[{'metric': 'ReferenceRateUSD', 'frequencies':...
asset_rr = client.get_asset_metrics(
assets = 'usdt',
metrics = 'ReferenceRateUSD',
frequency = '1m',
start_time = datetime.now() - timedelta(hours=24)
).to_dataframe()
asset_rr
0
usdt
2024-10-02 11:42:00+00:00
0.99984
1
usdt
2024-10-02 11:43:00+00:00
0.99979
2
usdt
2024-10-02 11:44:00+00:00
0.99983
3
usdt
2024-10-02 11:45:00+00:00
0.999845
4
usdt
2024-10-02 11:46:00+00:00
0.99982
...
...
...
...
1735
usdt
2024-10-03 16:37:00+00:00
0.999861
1736
usdt
2024-10-03 16:38:00+00:00
0.99985
1737
usdt
2024-10-03 16:39:00+00:00
0.99985
1738
usdt
2024-10-03 16:40:00+00:00
0.99984
1739
usdt
2024-10-03 16:41:00+00:00
0.999835
1740 rows × 3 columns
ax1 = plt.subplot()
ax1.ticklabel_format(useOffset=False)
asset_rr_chart = sns.lineplot(data=asset_rr,y=asset_rr.ReferenceRateUSD,x=asset_rr.time)
plt.setp(ax1.get_xticklabels());
ax1.set_facecolor("white")
plt.grid(color = 'black', linestyle = '--', linewidth = 0.2)
asset_rr_chart.set_xlabel("", fontsize = 15)
asset_rr_chart.set_ylabel("Price", fontsize = 15)
asset_rr_chart.set_title('\nUSDT Reference Rate (1 min)\n', fontsize = 18, font = 'arial');

Principal Market Price
The Principal Market Prices identify a principal market for each asset and utilize the most recent price from this market. Common use cases are for fair value measurement, preparing financial statements, and calculating closing prices for indexes or financial benchmarks.
asset_pmp = client.get_asset_metrics(
assets = 'usdt',
metrics = ['principal_market_price_usd','principal_market_usd'],
frequency = '1m',
start_time = datetime.now() - timedelta(hours=24)
).to_dataframe()
asset_pmp
0
usdt
2024-10-02 11:42:00+00:00
0.999835
binance-btc-usdt-spot
1
usdt
2024-10-02 11:43:00+00:00
0.999921
binance-btc-usdt-spot
2
usdt
2024-10-02 11:44:00+00:00
0.999892
binance-btc-usdt-spot
3
usdt
2024-10-02 11:45:00+00:00
0.999845
binance-btc-usdt-spot
4
usdt
2024-10-02 11:46:00+00:00
0.999763
binance-btc-usdt-spot
...
...
...
...
...
1735
usdt
2024-10-03 16:37:00+00:00
0.999861
crypto.com-btc-usdt-spot
1736
usdt
2024-10-03 16:38:00+00:00
0.999839
crypto.com-btc-usdt-spot
1737
usdt
2024-10-03 16:39:00+00:00
0.999777
crypto.com-btc-usdt-spot
1738
usdt
2024-10-03 16:40:00+00:00
0.999679
crypto.com-btc-usdt-spot
1739
usdt
2024-10-03 16:41:00+00:00
0.999798
crypto.com-btc-usdt-spot
1740 rows × 4 columns
market_list = list(set(asset_pmp['principal_market_usd'].to_list()))
market_list
['crypto.com-eth-usdt-spot',
'binance-eth-usdt-spot',
'binance-btc-usdt-spot',
'crypto.com-btc-usdt-spot']
unique_markets = asset_pmp['principal_market_usd'].unique()
colors = plt.cm.jet(np.linspace(0,1,len(unique_markets)))
color_map = dict(zip(unique_markets, colors))
asset_pmp = asset_pmp.sort_values('time')
fig, ax = plt.subplots()
ax.set_facecolor("white")
ax.grid(color='lightgray', linestyle='--')
for i in range(1, len(asset_pmp)):
ax.plot(asset_pmp['time'].iloc[i-1:i+1],
asset_pmp['principal_market_price_usd'].iloc[i-1:i+1],
color=color_map[asset_pmp['principal_market_usd'].iloc[i]],
label=asset_pmp['principal_market_usd'].iloc[i])
handles, labels = plt.gca().get_legend_handles_labels()
by_label = dict(zip(labels, handles))
import matplotlib.ticker as ticker
ax.legend(by_label.values(), by_label.keys(), loc='upper right', frameon=False, bbox_to_anchor=(1, 1.135))
ax.get_yaxis().set_major_formatter(ticker.FuncFormatter(lambda x, p: format(float(x), '.4f')))
plt.title('USDT Principal Market Price\n', fontsize=22)
plt.xlabel('')
plt.ylabel('Principal Market Price (USD)\n',fontsize=16)
plt.show()

Last updated
Was this helpful?