For a long time I have been engaged in the formation of portfolios for clients consisting of stocks and ETFs. I stayed away from the cryptocurrency market, as many warned about its unreliability. The same Warren Buffett. But times are changing and I decided to see what's going on on it. The first task I tried to look at was the prospect of portfolio investment in cryptocurrencies. For analysis, I took cryptocurrencies with maximum capitalization as of December 30, 2020 and for which there is data for at least three years:
- BTC-USD
- ETH-USD
- USDT-USD
- XRP-USD
- LTC-USD
- BCH-USD
- ADA-USD
- BNB-USD
- LINK-USD
At the beginning, I connect the necessary libraries and get data for analysis from yahoo finance.
import yfinance as yf
import pandas as pd
import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt
import seaborn as sns
cryptocurrency = ['BTC-USD', 'ETH-USD', 'USDT-USD', 'XRP-USD', 'LTC-USD', 'BCH-USD', 'ADA-USD', 'BNB-USD', 'LINK-USD']
data= yf.download(cryptocurrency, start="2018-01-01", end="2020-12-31")['Close']
data.head()
Next, I will plot the price change of the received cryptocurrencies from 2018 to 2020.
fig, axs = plt.subplots(5, 2, figsize=(15,15))
axs[0, 0].plot(data.index, data['BTC-USD'], 'tab:blue' )
axs[0, 0].set_title('BTC')
axs[0, 1].plot(data.index, data['ETH-USD'], 'tab:orange')
axs[0, 1].set_title('ETH')
axs[1, 0].plot(data.index, data['USDT-USD'], 'tab:green')
axs[1, 0].set_title('USDT')
axs[1, 1].plot(data.index, data['XRP-USD'], 'tab:red')
axs[1, 1].set_title('XRP')
axs[2, 0].plot(data.index, data['LTC-USD'], 'tab:grey')
axs[2, 0].set_title('LTC')
axs[2, 1].plot(data.index, data['BCH-USD'], 'tab:purple')
axs[2, 1].set_title('BCH')
axs[3, 0].plot(data.index, data['ADA-USD'], 'tab:purple')
axs[3, 0].set_title('ADA')
axs[3, 1].plot(data.index, data['BNB-USD'], 'tab:purple')
axs[3, 1].set_title('BNB')
axs[4, 0].plot(data.index, data['LINK-USD'], 'tab:purple')
axs[4, 0].set_title('LINK')
fig.delaxes(axs[4,1])
for ax in axs.flat:
ax.set(xlabel='Data', ylabel='Price')
for ax in axs.flat:
ax.label_outer()
Next, you need to get the percentage change in the value of the asset for the trading day. It is calculated using the pct_change() function from the Pandas package. The profitability indicator allows you to better understand and investigate changes over time. It also allows you to compare different assets with each other.
return_data = data.pct_change()
return_data.head()
In order to analyze the dependence, we construct a covariance matrix. It will be needed in the future for the study of portfolios.
var_matrix = return_data.cov()
var_matrix
After that, you can analyze by generating random weights of each cryptocurrency in the portfolio and calculating profitability and volatility for them. The execution may take a long time.
port_return = []
# Initialize an empty list for storing the portfolio volatility
port_volatility = []
# Initialize an empty list for storing the Sharpo Ratio
# port_sharpo = []
# Initialize an empty list for storing the portfolio weights
port_weights = []
num_assets = len(data.columns)
num_portfolio = 1000000
individual_rets = data.resample('Y').last().pct_change().mean()
for port in range(num_portfolio):
# Randomly generate weigh combination
weights = np.random.random(num_assets)
# Normalize weight so that they sum to 1
weights = weights/np.sum(weights)
port_weights.append(weights)
# Return are the dot product of individual expected returns of asset and its weights
returns = np.dot(weights, individual_rets)
port_return.append(returns)
# Computing Portfolio Volatility
portfolio_volatility = np.sqrt(np.dot(weights.T,np.dot(var_matrix*252,weights)))
port_volatility.append(portfolio_volatility)
portfolio = {'Returns': port_return,'Volatility': port_volatility}
for counter, symbol in enumerate(data.columns.tolist()):
portfolio[symbol] = [w[counter] for w in port_weights]
portfolios_V1 = pd.DataFrame(portfolio)
portfolios_V1.head()
After that, you can choose a portfolio with minimal volatility, maximum Sharpe ratio and maximum profitability.
rf = 0.02
min_vol_port = portfolios_V1.iloc[portfolios_V1['Volatility'].idxmin()]
optimal_risky_port = portfolios_V1.iloc[((portfolios_V1['Returns']-rf)/portfolios_V1['Volatility']).idxmax()]
max_ret_port = portfolios_V1.iloc[portfolios_V1['Returns'].idxmax()]
weights_min_vol = np.array(min_vol_port[2:])
#weights in a portfolio with max Sharpe Ratio
weights_opt_sr = np.array(optimal_risky_port[2:])
#weights in a portfolio with max Returns
weights_max_ret = np.array(max_ret_port[2:])
We keep for these portfolios the weights that will be required in the analysis in 2021.
df_weights = pd.DataFrame(columns=portfolios_V1.columns)
df_weights = df_weights.append(min_vol_port.rename("Minimum Volatility").to_frame().T)
df_weights = df_weights.append(max_ret_port.rename("Maximum Returns").to_frame().T)
df_weights = df_weights.append(optimal_risky_port.rename("Maximum Sharpe Ratio").to_frame().T)
We also calculate weights for a portfolio with maximum diversification and store weights for it in a separate variable.
array_returns = np.asarray(return_data.dropna())
array_cov = np.asarray(var_matrix)
mean_returns = np.mean(array_returns, axis = 0)
portfolios_V1_div = portfolios_V1.copy()
div_ratio = []
for i in range(portfolios_V1.shape[0]):
weight_vector = list(portfolios_V1.iloc[i])[2:]
portfolio_risk = np.sqrt(np.matmul((np.matmul(weight_vector,array_cov)), np.transpose(weight_vector)))
ann_portfolio_risk = portfolio_risk*np.sqrt(252)*100
portfolio_return = np.matmul(weight_vector, np.transpose(mean_returns))
ann_portfolio_return = 252*portfolio_return * 100
portfolio_asset_sdv = np.sqrt(np.diagonal(array_cov))
portfolio_div_ratio = np.sum(np.multiply(portfolio_asset_sdv, weight_vector)) \
/ portfolio_risk
div_ratio.append(portfolio_div_ratio)
portfolios_V1_div['Diversification Ratio'] = div_ratio
div_port = portfolios_V1_div.iloc[portfolios_V1_div['Diversification Ratio'].idxmax()]
df_weights = df_weights.append(div_port[:-1].rename("Maximum Diversification").to_frame().T)
div_port[-1]
weights_div = np.array(portfolios_V1_div.iloc[portfolios_V1_div['Diversification Ratio'].idxmax()][2:-1])
All weights are added to the shared DataFrame.
df_weights
For a visual representation, we will display pie charts with asset weights in portfolios.
df_weights_circle = df_weights.drop(['Returns', 'Volatility'], axis = 1)
df_weights_circle
color_pie = ['Grey', 'Purple', 'Blue', 'Green', 'Orange', 'Red', 'Yellow', 'magenta', 'cyan']
color_pie_dict = dict(zip(df_weights_circle.columns, color_pie))
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
for i, (idx, row) in enumerate(df_weights_circle.iterrows()):
ax = axes[i//2, i%2]
row = row[row.gt(row.sum() * .01)]
ax.pie(row, labels=row.index, colors=[color_pie_dict.get(i) for i in row.index], startangle=30)
ax.set_title(idx)
fig.subplots_adjust(wspace=.2)
Now comes the most interesting moment - to see how the portfolios behaved in 2021. Let's carry out the same steps - download the data, calculate their daily profitability.
data_2021= yf.download(cryptocurrency, start="2020-12-31", end="2021-12-31")['Adj Close']
data_2021.head()
return_data_2021 = data_2021.pct_change()
cum_daily_return = (1 + return_data_2021).cumprod()
var_matrix_2021 = return_data_2021.cov()
var_matrix_2021.head()
Then, using the previously obtained weights, we will calculate the profitability of portfolios at the end of the year and their volatility.
individual_rets_2021 = data_2021.resample('Y').last().pct_change().mean()
result_2021 = pd.DataFrame()
for i in range(df_weights_circle.shape[0]):
weight = np.array(df_weights_circle.iloc[i])
ret = (np.dot(weight, individual_rets_2021))
vol = np.sqrt(np.dot(weight.T,np.dot(var_matrix*252,weight)))
result_2021 = result_2021.append(pd.Series([ret, vol]).rename(df_weights_circle.iloc[[i]].index[0]).to_frame().T)
result_2021.columns = ['Returns', 'Volatility']
result_2021
We will also plot the price change of portfolios during the year.
fig, axs = plt.subplots(figsize=(15,10))
(cum_daily_return.multiply(weights_min_vol, axis=1).sum(axis=1) - 1).plot(label = 'Minimum volatility')
(cum_daily_return.multiply(weights_opt_sr, axis=1).sum(axis=1) - 1).plot(label = 'Maximum Sharp')
(cum_daily_return.multiply(weights_div, axis=1).sum(axis=1) - 1).plot(label = 'Maximum Diversification')
(cum_daily_return.multiply(weights_max_ret, axis=1).sum(axis=1) - 1).plot(label = 'Maximum Returns')
plt.title('2021 year')
plt.ylim(0)
plt.legend()
plt.show()
As can be seen from the results obtained, the portfolio showed the greatest profitability, which over the past years showed minimal volatility. In second place was a portfolio with maximum diversification. At the same time, all portfolios experienced maximum drawdown from May to June. I would like to warn you, this study is not an investment idea. Moreover, additional analyses using machine learning and time series analysis are still required.