# Investing: Minimum Variance Portfolio and the Efficient Frontiers

How should you invest your wealth? This simple question does not have a simple answer. In 1952, Harry Markowitz introduced the Modern Portfolio Theory (MPT) as the answer to this question.

The MPT is a mathematical framework for estimating the maximum expected return of a portfolio of assets for a given level of risk. If we have two assets - A and B - the return of the portfolio is mathematically represented as:

While the risk - or variance - of the portfolio is given by:

The weights are the percentage of wealth that one would invest in both asset classes. And their sum is:

The weight of an asset can be a negative number. For example, when an investor shorts a stock. However, we are only interested in the long position for this article.

The mathematical equations get more cumbersome when there are more than two stocks in the portfolio. This article will describe how to create a minimum variance portfolio when there are two stocks in the portfolio and when there are more than two stocks in the portfolio.

Two Assets Portfolio

import yfinance as yf
import pandas as pd

# Investigating Two Assets: TSLA and Bitcoin

# Cryptocurrencies are traded 24/7 and more than 360 days in a year
# On the other hand, stock are traded about 250 days in a year
# We therefore need to adjust the length of the dataframes

dfBTC = BTC[BTC.index.isin(TSLA.index)]
dfTSLA = TSLA[TSLA.index.isin(dfBTC.index)]

# Concatenate both dataframes, take the Adjusted Close price and
# rename the columns

df.columns = ['TSLA', 'BTC']

In the above block of code, we have imported Tesla and Bitcoin, and have selected their Adjusted closing prices. We will calculate return using the formula:

# Calculate the return of the stock and drop NA cells
df = df.pct_change().dropna()

# Get the Expected return (mean) and variance from both stocks
summary = df.describe()

ETSLA = summary.loc['mean', 'TSLA']
EBTC = summary.loc['mean', 'BTC']

stdTSLA = summary.loc['std', 'TSLA']
varTSLA = stdTSLA**2
stdBTC = summary.loc['std', 'BTC']
varBTC = stdBTC**2

# Get the covariance
cov = df.cov().loc['TSLA', 'BTC']

We have all the parameters that we need aside from the weights. We will define the portfolio variance and portfolio return function according to the MPT and apply several combinations of weights in the following block of code.

# Define the portfolio variance and portfolio return functions

def portfolio_variance(w1):
return w1**2 * varTSLA + (1-w1)**2 * varBTC + 2*w1*(1-w1)*cov

def portfolio_return(w1):
return w1 * ETSLA + (1 - w1) * EBTC

# Generate weights for w1 which is the TSLA stock

import numpy as np
wTSLA = np.arange(0,1.1,0.1)

# Put weights in dataframe
dff = pd.DataFrame(wTSLA, columns=['w1'])

# Calculate the portfolio returns and variance

dff['PortfolioReturn'] = dff['w1'].apply(portfolio_return)
dff['PortfolioVar'] = dff['w1'].apply(portfolio_variance)

# Plot Efficient Frontiers

import plotly.graph_objects as go

fig = go.Figure(data=go.Scatter(x=dff['PortfolioVar'], y=dff['PortfolioReturn']))
fig.update_layout(title='Efficient Frontiers for a Portfolio with Two Assets: BTC and TSLA',
xaxis_title='Portfolio Variance',
yaxis_title='Portfolio Expected Return')
fig.show()

Quite straightforward. We have been able to make an efficient frontier plot using two asset classes. But why is the above graph important? We can invest our wealth in either asset class in many different combinations. However, we want the combination that gives us the maximum return given our risk appetite. With the help of the efficient frontier plot, given the risk we are willing to take, we get the portfolio expected return and the weight combinations associated with the return - we interpolate if necessary.

Four Assets Portfolio

The idea behind estimating the Efficient Frontiers is the same but the mathematics becomes more involved when the number of assets are more than two. Usually, there are more than two assets in a portfolio. In this section, we will extend the MPT to estimate the risks and returns for a four assets portfolio.

# Investigating the following assets: TSLA, AAPL, BITCOIN, ETHEREUM

# Adjust length of the dataframes

dfAAPL = AAPL[AAPL.index.isin(dfBTC.index)]
dfETH = ETH[ETH.index.isin(dfBTC.index)]
dfBTC = BTC[BTC.index.isin(TSLA.index)]
dfTSLA = TSLA[TSLA.index.isin(dfBTC.index)]

# Concatenate the dataframes, take the Adj Close Prices

df.columns = ['TSLA', 'AAPL', 'BTC', 'ETH']

# Calculate the return
df = df.pct_change().dropna()

# Get descriptive statistics
summary = df.describe()

# Get the Expected return for each asset

ETSLA = summary.loc['mean', 'TSLA']
EAAPL = summary.loc['mean', 'AAPL']
EBTC = summary.loc['mean', 'BTC']
EETH = summary.loc['mean', 'ETH']

# Get the assets variance and covariance
dfCov = df.cov()
varTSLA = dfCov.loc['TSLA', 'TSLA']
varAAPL = dfCov.loc['AAPL', 'AAPL']
varBTC = dfCov.loc['BTC', 'BTC']
varETH = dfCov.loc['ETH', 'ETH']

covTSLA_AAPL = dfCov.loc['TSLA', 'AAPL']
covTSLA_BTC = dfCov.loc['TSLA', 'BTC']
covTSLA_ETH = dfCov.loc['TSLA', 'ETH']

covAAPL_BTC = dfCov.loc['AAPL', 'BTC']
covAAPL_ETH = dfCov.loc['AAPL', 'ETH']

covBTC_ETH = dfCov.loc['BTC','ETH']

We have gotten most of the parameters to calculate the portfolio’s estimated return and variance. We will now extend the portfolio expected return and portfolio variance functions that we used previously. In addition, we have to also generate weights for the four asset classes that sums to one. The itertools library will help us generate the weights.

# Extended portfolio variance and portfolio return functions

portfolio_return = lambda w: w[0] * ETSLA + w[1] * EAAPL + w[2] * EBTC + w[3] * EETH

portfolio_variance = lambda w: w[0]**2 * varTSLA + w[1]**2 * varAAPL + w[2]**2 * varBTC + w[3]**2 * varETH + \
2*w[0]*w[1]*covTSLA_AAPL + 2*w[0]*w[2]*covTSLA_BTC + 2*w[0]*w[3]*covTSLA_ETH + \
2*w[1]*w[2]*covAAPL_BTC + 2*w[1]*w[3]*covAAPL_ETH + 2*w[2]*w[3]*covBTC_ETH

# Generate weights w1, w2, w3, w4
from itertools import product

iterable = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]

my_iter = product(iterable, iterable, iterable, iterable)

weights = [(a, b, c, d) for a, b, c, d in my_iter if sum([a, b, c, d]) == 1]

# Put weights in a dataframe
dff = pd.DataFrame(weights, columns=['w1', 'w2', 'w3', 'w4'])

# Calculate the Portfolio expected return and the variance

returns = pd.Series(dff[['w1', 'w2', 'w3', 'w4']].values.tolist()).apply(portfolio_return)

variances = pd.Series(dff[['w1', 'w2', 'w3', 'w4']].values.tolist()).apply(portfolio_variance)

dff['PortfolioReturn'] = returns
dff['PortfolioVar'] = variances

# Plot the Efficient Frontier

import plotly.express as px
fig = px.scatter(x=dff['PortfolioVar'], y=dff['PortfolioReturn'])
fig.update_layout(title='Efficient Frontiers for a Portfolio with Four Assets: TSLA, AAPL, BTC, ETH',
xaxis_title='Portfolio Variance',
yaxis_title='Portfolio Expected Return')
fig.show()

Similarly, we can estimate the percentage of our assets that should be invested in each asset class given our risk appetite. Points below the efficient frontiers are suboptimal investments and positions above the efficient frontiers are impossible positions.

Conclusion

In this article, we looked at how we can invest our wealth to get the maximum possible returns. We discussed the Modern Portfolio Theory and showed how it helps us answer the question, “How should we invest our wealth?”, by estimating the maximum return for each risk profile. We showed how the MPT is applied to two asset classes and extended to a portfolio with more than two assets. And we concluded that investments below the efficient frontiers give suboptimal returns. One area we did not discuss is shorting of a stock. This analysis can be extended to investigate the efficient frontiers for stocks with one or more negative weight - shorting.

8 Likes