Optimal Cryptocurrencies Portfolio Allocation with Modern Portfolio Theory, in Python

Let’s understand how we can use Python to optimize our crypto portfolio!

Massimo Zambelli
Geek Culture

--

Photo by Executium on Unsplash

It is very well known that investing in cryptocurrencies can be very lucrative, but it is also very well known that the volatility of the market may expose to great risk, with price oscillations that can quickly reach even half of the value as recently happened.

Then, how can one try to get the most out of their investment without being exposed to a too-high risk? Well, the answer is optimal portfolio allocation: there is always a trade-off between risk and reward, so given a fixed amount of money and a set of assets we want to find the best proportion of each to buy according to our desired objective. While different tools exist, in this Python tutorial we will focus on the well-known Modern Portfolio Theory (MPT), a statistical method that allows us to quickly gain insight about where to put our money.

In particular, we will understand how MPT works and implement a program to automatically get historical data and analyze arbitrary portfolios. If you are interested, keep reading! :) The complete code for this tutorial can be found on my GitHub:

Disclaimer: This article is only meant for educational purposes and does not constitute financial advice. I am not a qualified advisor, and I take no responsibility for the incorrect use of the code provided in this manuscript.

(a small side note: we will apply MPT to cryptocurrencies, but it will work with any set of assets provided that we can get the prices over time.)

A primer on MPT

Modern Portfolio Theory (MPT) was proposed by the economist Harry Markowitz [1] back in the 50s as a way of objectively find the best portfolio allocation. As such, it can be used to build portfolios that minimize the risk for a given expected return or maximize the return for a given level of risk.

In few words, the theory is based on the following concepts:

  • Two key quantities: the expected return and the variance (a proxy for the volatility and, thus, of the risk) of the portfolio over the considered period;
  • The efficient frontier: a set of optimal portfolios in the sense of risk-return tradeoff, where risk is minimized for a given return;
  • Diversification: while single assets can be particularly volatile, combining efficiently a number of them can lead to a portfolio with greatly reduced overall volatility due to the effects of correlation;

The expected return of a portfolio is defined as a random variable R, obtained as the weighted sum of the expected returns of each asset (if we have N) over a considered period (we will pick one year in this tutorial):

The expected return of asset i is again a random variable, and w_i is the fraction of portfolio allocated to asset i, such that

Spoiler alert: these weights are what we want to optimize! ;). To get the expected return, by linearity of the expectation operator we simply get

which can be vectorized as

if we consider w as the weights vector and R as the (respective) assets return vector. The very concept of “return” here is somehow fuzzy, and there exist different valid formulations to define it formally. We will use the logarithm of the ratio between the end and start prices over the considered period, which means

At this point, we need to approximate the values E[R_i] for the assets we are considering: MTP is based on the hypothesis that returns are independent and identically distributed, so we can compute the expected value of daily returns and then just multiply it by the number of trading days in the period. Since the crypto market is always up, this is just 365.

As per the variance of our portfolio, first of all, we need to recall that for two random variables X and Y the covariance is defined as

which can be computed from the gathered data. In fact, given that X and Y in this case are prices of pairs of assets, the second term can be easily obtained from the consideration above, while the first is derived by multiplying the average of the product of all the daily returns by the trading days minus one, squared. The total portfolio variance, then, follows as:

(for the details on the computation see [2], from where I took some inspiration). The expression, again, can be vectorized introducing the covariance matrix Sigma as

Having defined the expected (log) return and variance, we can now define the trade-off between them as a cost function to be minimized. If we are interested in setting the risk, we can get the optimal weights as

where r is the risk tolerance (the higher, the higher the variance and the return). If, instead, we want to fix an expected return, we can resort to

where k is the (log) return we want to expect. Note that in both cases we need to solve constrained minimizations (due to the constraints on the weights and, in the second case, on the expected return), which is very easy in scipy.

Before proceeding with the Python implementation, we can define an additional quantity that is often considered in the context of MPT: the Sharpe ratio. It is defined as the performance of a portfolio with respect to a “risk-free” investment (for instance, US treasury bills), normalized by the variance (risk) of the portfolio. It can be easily obtained as a function of the weights w, and thus it can be optimized as well. In fact,

Python Implementation

In the first part of the program, we are going to code all that is needed to actually implement the computations introduced above: we define the two main classes for the single assets and the portfolios. The Asset class allows getting the required values from a prices time series passed as a pandas DataFrame. Notice that the computation of the covariance matrices is cached since it is quite expensive and we will need to call it many times.

Once defined all the basic building blocks, we need to instantiate the Asset objects, based on real data gathered from the web. To do so, we can use the RESTful API exposed by https://coincap.io/, which is freely available for anyone to use. Coding some little helper functions for the sake of simplicity and reusability, we can easily achieve the task in few lines.

We are now ready to wrap everything up, exploiting the power of MPT to gain insight into our portfolios. For the sake of the tutorial, we are going to consider a small set of popular currencies (Ethereum, Cardano, Polygon, Tether, Litecoin, Bitcoin, and Monero). We first plot several random (non-optimal) portfolios, obtained using randomly initialized weights. Then, we perform some optimizations for selected risk tolerances, expected returns, and the Sharpe ratio. Feel free to play with the parameters and the currencies!

Here are the results, which you should obtain exactly equal due to the fixed random seed :)

(picture by the author)

We can see that higher risks correspond to higher returns and vice versa, while it is by no means possible to do better than the efficient frontier. In particular, the selected optimal portfolios are simply points on the efficient frontier determined by the chosen trade-off.

(picture by the author)

Here we can see how lower-risk portfolios privilege USDT (a stable coin), while if we want to obtain higher returns we need to invest more in altcoins that, of course, are much more volatile but lucrative.

That’s all for this article, let me know if you’d like to see more on the subject with Python! :)

--

--

Massimo Zambelli
Geek Culture

Ph.D. in Computer Engineering || Machine Learning Enthusiast || Independent Developer. I love to keep learning interesting stuf and writing about what I love :)