In this tutorial, we will show you how to build optimized portfolios in Python using the PyPortfolioOpt library. In another tutorial, we have shown you how to build diversified portfolios from scratch using Python. For this task, we will build the optimized portfolios based on the Efficient Frontier. We will build one portfolio that maximized the Sharpe Ratio and another that minimizes the volatility. The chart below shows the location of the portfolios that we mentioned above on the efficient frontier.
The flowchart below represents the different functionalities of the PyPorfolioOpt library.
Load the Required Libraries
For this tutorial, we will need to use some libraries. If you want to follow along with the reproducible examples, you should have installed these libraries. Let’s load them.
import pandas as pd import requests import time from pypfopt import risk_models from pypfopt.risk_models import CovarianceShrinkage from pypfopt import expected_returns from pypfopt.efficient_frontier import EfficientFrontier
Get the Data
We will consider the daily prices of the cryptocurrencies in USD ($) since “2020-01-01” which means 2 years and 4 months of data (today is: 2022-04-01). We will use the Kraken API and all the available cryptocurrencies. Note that we will remove any cryptocurrency that has missing data during this time period. We will keep the closing daily prices. Note that the date is in Unix timestamp.
# get the starting date in unix timestamp date_time = '2020-01-01 00:00:00' pattern = '%Y-%m-%d %H:%M:%S' epoch = int(time.mktime(time.strptime(date_time, pattern))) # get all the cryptocurrencies in USD resp = requests.get('https://api.kraken.com/0/public/AssetPairs') resp = resp.json() # Keep all the cryptos over USD dollar_pairs = [] for pair in resp['result']: if pair.endswith('USD'): dollar_pairs.append(pair) # get all the unixtimestamps tmp_url = f'https://api.kraken.com/0/public/OHLC?pair=XXBTZUSD&since={epoch}&interval=1440'.format(epoch) resp = requests.get(tmp_url) dummy_df = pd.DataFrame(resp.json()['result']['XXBTZUSD'])[[0]] # create a pandas data frame and add the closing prices of each asset df = pd.DataFrame(index=dummy_df[0]) for pair in dollar_pairs: tmp_url = f'https://api.kraken.com/0/public/OHLC?pair={pair}&since={epoch}&interval=1440'.format(pair,epoch) resp = requests.get(tmp_url) tmp_df = pd.DataFrame(resp.json()['result'][pair])[[0,4]] tmp_df.columns = ['unixtimestap', pair] tmp_df[pair] = tmp_df[pair].astype(float) df = pd.concat([df,tmp_df.set_index('unixtimestap')], axis=1) # remove the last row since it is not a full day df = df[:-1].copy() # drop the columns with a least one NA df.dropna(axis='columns', inplace=True) # remove the other fiat currencies liker EURO and the stable coins like Tether and DAI df.drop(columns=['USDCUSD', 'USDTZUSD', 'DAIUSD', 'ZGBPZUSD','ZEURZUSD', 'PAXGUSD'], axis=1, inplace=True)
We ended up with 29 cryptocurrencies of 720 daily closing prices.
Get the Efficient Frontier
For the efficient frontier, we will need to pass the expected returns mu
and the variance-covariance matrix Sigma
. There are many variations of the mu and Sigma respectively. For example, for mu we have the following options:
- mean_historical_return
- ema_historical_return
- returns_from_prices
and for Sigma we have the following options:
sample_cov
semicovariance
exp_cov
ledoit_wolf
ledoit_wolf_constant_variance
ledoit_wolf_single_factor
ledoit_wolf_constant_correlation
oracle_approximating
In this case, we will work with the exponential mean and variance in order to give more weight to the most recent observations. Feel free to play with the other options too.
# calculate the mean and variance mu = expected_returns.ema_historical_return(df, frequency=365) Sigma = risk_models.exp_cov(df, frequency=365)
From the EMA returns, we can see that the Wave, the DogeCoin the Cosmos and the ETC have performed extremely well compared to the rest cryptocurrencies.
# get the efficient frontier ef = EfficientFrontier(mu, Sigma)
Maximum Sharpe Ratio Portfolio
We can get the weights of the optimum portfolio in terms of Sharpe Ratio as follows. Note that we have set the risk-free interest rate to be 0. If you would like to set another value, you can pass it to the max_sharpe()
function.
sharpe_weights = ef.max_sharpe() (ef.clean_weights())
The optimum portfolio that maximizes the Sharpe Ratio is to invest in Wave (70%) and in DogeCoin (30%). We can also get the performance of the portfolio.
ef.portfolio_performance(verbose=True)
Minimum Variance Portfolio
We can also get the weights of the portfolio that minimizes the risk and it is on the Efficient Frontier.
ef = EfficientFrontier(mu, Sigma) min_vol_weights = ef.min_volatility() (ef.clean_weights())
According to our data, the weights of the lowest variance portfolio are 50% BTC, 35% TRX, 10% REP and 5% MLN.
The performance of this portfolio is:
ef.portfolio_performance(verbose=True)
The Takeaways
Our goal was to show you how to apply the Markowitz portfolio theory to cryptocurrencies using Python. Personally, I believe that we didn’t get reliable results because:
- Many good cryptocurrencies were not included because we did not have 2.5 years data. Some examples are the DOT, AAVE, LUNA, AVAX and so on.
- There are some alt-coins that can have extremely high returns and this affects our data
However, I believe that you can apply these techniques by taking into consideration a shorter time period and by focusing on specific cryptocurrencies.
Want some Free Cryptos?
You can get $25 in Bitcoin by investing $100 in Nexo