Creating Coin Market Cap Based Portfolios
In this example I will show the code at once instead of breaking down to parts. This is partly due to its incompleteness.
The program creates 4 equal weighted (assumes theoretically constant rebalancing) portfolios. 4 portfolios are large, mid, small and micro cap coins. Marketcaps are based on the most recent day's value (yes, it is data snooping).
Data sources are CoinMarketCap for marketcap data and Binance for historical prices.
Here's the code:
import requests
import json
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats
binance_data = json.loads(requests.get('https://api.binance.com/api/v3/ticker/24hr').text)
binance_symbols = [_['symbol'] for _ in binance_data]
session = requests.Session()
session.headers.update({'Accepts': 'application/json', 'X-CMC_PRO_API_KEY': 'd3ec12e9-0065-4099-8f66-36492e71ba60',})
data = json.loads(session.get('https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?limit=600&market_cap_min=50000000').text)
symbols = [_['symbol'] for _ in data['data']]
marketcaps = [data['data'][idx]['quote']['USD']['market_cap'] for idx in range(len(data['data']))]
df = pd.DataFrame([symbols, marketcaps]).T
df.columns = ['symbol','marketcap']
df['marketcap (M)'] = df['marketcap']/(10**6)
df = df.sort_values(by='marketcap (M)', ascending=False).drop('marketcap',axis=1)
df.symbol += 'USDT' # to mach binance symbols
df = df[[_ in binance_symbols for _ in df.symbol]].reset_index(drop=True) # selected common coins between two APIs
largecap_symbols = df[df['marketcap (M)'] > 10000]['symbol'].values
midcap_symbols = df[(df['marketcap (M)'] > 2000) & (df['marketcap (M)'] < 10000)]['symbol'].values
smallcap_symbols = df[(df['marketcap (M)'] > 300) & (df['marketcap (M)'] < 2000)]['symbol'].values
microcap_symbols = df[(df['marketcap (M)'] > 50) & (df['marketcap (M)'] < 300)]['symbol'].values
all_coins, interval, limit = [], '1d', 90
for symbol in df.symbol:
coin = json.loads(requests.get(f'https://api.binance.com/api/v3/klines?symbol={symbol}&interval={interval}&limit={limit}').text)
all_coins.append([float(_[4]) for _ in coin])
coins = pd.DataFrame(all_coins).T
coins.columns = df.symbol
#drop exceptions (coins.max().sort_values())
coins.drop('LUNAUSDT',axis=1, inplace=True)
microcap_symbols = np.delete(microcap_symbols, 4)
allmarket = coins.pct_change().mean(axis=1)
largecap = coins[largecap_symbols].pct_change().mean(axis=1)
midcap = coins[midcap_symbols].pct_change().mean(axis=1)
smallcap = coins[smallcap_symbols].pct_change().mean(axis=1)
microcap = coins[microcap_symbols].pct_change().mean(axis=1)
# METRICS
def calculate_metrics(df):
df.dropna(inplace=True)
df_ret = df.mean()*252
df_std = df.std()*np.sqrt(252)
df_neg_std = df[df < 0].std()*np.sqrt(252)
df_sharpe = df_ret/df_std
sf_sortino = df_ret/df_neg_std
df_cumret = (df+1).cumprod()
df_peak = df_cumret.expanding(min_periods=1).max()
df_maxdd = ((df_cumret/df_peak)-1).min()
return [df_ret, df_sharpe, sf_sortino, df_maxdd]
def calculate_beta(df, market):
corr = scipy.stats.pearsonr(market, df)
beta = (df.std()*np.sqrt(252)) / (market.std()*np.sqrt(252)) * corr[0]
return beta
portfolios = [allmarket, largecap,midcap,smallcap,microcap]
portfolios_df = pd.DataFrame([calculate_metrics(_) for _ in portfolios]).T
portfolios_df.columns = ['AllMarket','LargeCap', 'MidCap','SmallCap', 'MicroCap']
portfolios_df.index = ['Return','Sharpe','Sortino','Max DD']
betas = [calculate_beta(_, allmarket) for _ in portfolios]
portfolios_df.loc['Betas'] = betas
portfolios_df.plot(kind='bar', figsize=(10,6))
plt.title("Coin MarketCap Portfolios' Performance Metrics")
plt.show()
for i in range(len(portfolios)):
(portfolios[i]+1).cumprod().plot(label=portfolios_df.columns[i], figsize=(10,6))
plt.title('Coin MarketCap Portfolios'' Cumilative Returns')
plt.legend()
plt.show()