Portfolios of Two Risky Assets with Python

Cristiane Silva
Analytics Vidhya
Published in
7 min readSep 5, 2020

--

How to build a portfolio of two risky assets using Python and the Pandas, NumPy, Matplotlib, Seaborn, and Scipy libraries.

An image with many doors representing a choice

Through asset diversification, we can build a portfolio to provide the lowest possible risk for any level of expected return.

Harry Markowitz observed a common portfolio diversification practice and showed exactly how an investor can reduce the volatility of the portfolio’s returns by choosing stocks that do not move exactly together.

The portfolios of two risky assets are simple to analyze and show the principles that apply to portfolios of many assets.

The assets

The two companies chosen to demonstrate the use of the Python language to calculate the efficient frontier were Cisco and Microsoft. The diversification of companies in relation to their areas of activity was not considered (both companies are in the technology area). There are other factors to be considered to reduce portfolio risk, one of which is diversification (but that is a topic for another post).

Daily prices chosen are from the period from 09/03/2013 to 08/31/2018 and were obtained from the Yahoo Finance website. Follow some information about the companies:

Cisco Systems (CSCO)— The Company groups its products and technologies in several categories, such as Switching; New Generation Network Routing (NGN); Collaboration; Datacenter; Wireless; Service provider video; Security, and other products

Microsoft Corporation(MSFT) — Microsoft is a technology company that develops, licenses, and supports a variety of software products, services, and devices

Analyzing the prices

First of all, we must import all the libraries need to start work.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
price= pd.read_csv('price.csv', index_col= 0, sep=';', parse_dates=True, dayfirst=True,)
price.head()

The next polt shows the behaviour of the price stocks over the five years.

# CSCO plot
plt.subplot(2, 1, 1)
plt.plot(price.CSCO, color='b')
plt.ylabel('Price')
plt.title('CSCO Daily Returns')
# MSFT plot
plt.subplot(2, 1, 2)
plt.plot(price.MSFT, color='g')
plt.ylabel('Price')
plt.title('MSFT Daily Returns')
# Use plt.tight_layout() to improve the spacing between subplots
plt.tight_layout()
plt.show()

Returns

We can calculate the daily return of two assets by using the function pct_change(). This function computes the percentage change from the immediately previous row by default. Because of this, we use the function dropna() to remove the NaN first row created by the inexistent day before the first day.

returns = price.pct_change().dropna()
print(returns.head())
returns.info()
returns.describe()
fig = plt.figure(figsize=(10,10))# CSCO plot
plt.subplot(2, 1, 1)
sns.distplot(returns.CSCO, color='b');
plt.xlabel('Return')
plt.ylabel('Probability')
plt.title('CSCO Returns')
# MSFT plot
plt.subplot(2, 1, 2)
sns.distplot(returns.MSFT, color='g')
plt.xlabel('Return')
plt.ylabel('Probability')
plt.title('MSFT Returns')
plt.show()

CSCO and MSFT investments are an expected daily return of 0.07% and 0.11% respectively according to the histogram and table describe. Also, both have a similar spread of possible returns and therefore similar risk. The spread can be measured by the standard deviation. CSCO investment has a standard deviation of 1.3% whereas MSFT has one of 1.38%.

Maximum Drawdown

Drawdown is defined as the drop in the asset price from its running maximum. A maximum drawdown measures the worst loss of such an investor. The following example shows that MSFT has a maximum drawdown of 23% on 10th February 2016.

wealth_index_MSFT = (1+returns['MSFT']).cumprod()
previous_peaks_MSFT = wealth_index.cummax()
drawdowns_MSFT = (wealth_index - previous_peaks_MSFT)/previous_peaks_MSFT
drawdowns_MSFT.plot.line()
plt.ylabel('Drawdown')
plt.title('MSFT Maximum Drawdown')
drawdowns_MSFT.min(), drawdowns_MSFT.idxmin()

Correlation

A statistic in which the covariance is scaled to a value between −1 (perfect negative correlation) and +1 (perfect positive correlation).

corr = returns[['CSCO', 'MSFT']].corr()
print(corr)
sns.scatterplot(x="CSCO", y="MSFT", data=returns)

The correlation coefficient of the two assets is 0.505.

Annualized Return

To annualize a daily return you compound that return 252 times. The formula to annualize a daily return Rd is:

This formula was applied to the code below in the following way:

n_days = returns.shape[0]compounded_growth = (1+returns).prod()n_periods = returns.shape[0]
ann_returns = compounded_growth**(252/n_days)-1
ann_returns

Annualized Volatility

We annualize volatility by multiplying it by the square root of the number of periods per observation, in this case, 252 per year.

ann_vol = returns.std()*(252**0.5)
ann_vol

Covariance

It is a measure of the degree to which returns on two risky assets move in tandem.

cov = returns.cov()
cov

The positive covariance means that asset returns move together.

Portfolio Return

Suppose that you are wondering whether to invest in the shares of Cisco or Microsoft. According to the results shown above , Cisco offers an expected return of 19.0% and Microsoft offers an expected return of 31.8%. Also, you see that the volatility of returns is 20.6% for Cisco Soup and 22.0% for Microsoft. Microsoft offers the higher expected return, but it is more risky.

Instead of holding only one stock, we can create a portfolio whose expected return is simply a weighted average of the expected returns on the two holdings.

A proportion denoted by wC is invested in the CSCO stock, and the remainder, 1 − wC, denoted wM, is invested in the MSFT stock . The expected return on the portfolio is a weighted average of the expected returns on component shares with the portfolio’s proportions as weights:

We can write this formula in python as a matrix transposed weights times return.

Portfolio Volatility

As can be seen in the formula below, unlike the expected return, the portfolio variance is not a weighted average of the variances of individual assets.

# Calculate the portfolio return
def portfolio_return(w, r):

return w.T @ r
# Calculate the portfolio volatility return
def portfolio_vol(w, covmat):

return (w.T @ covmat @ w)**0.5

Efficient Frontier

The efficient frontier (curved line) illustrates how expected
return and standard deviation change as you hold different combinations of two stocks.

Each point of the curve ( n_points=15) contains 15 combinations of two-assets weights (weights = [np.array([w, 1-w]) for w in np.linspace(0, 1, n_points)])

For example:

  • Point 0 of curve
weights[0]

That means there all the investment is allocated on one asset : CSCO asset

  • Point 2 of curve
weights[2]

That means CSCO asset has the weight=85.7%, whereas MSFT asset has the weight=14.3%. And so on until the 15th point on the curve, where all the MSFT asset has the total of investment.

#Plot the efficient frontier of two assetsn_points = 15
weights = [np.array([w, 1-w]) for w in np.linspace(0, 1, n_points)]
def plot_ef2(n_points, returns, cov):

weights = [np.array([w, 1-w]) for w in np.linspace(0, 1, n_points)]
rets = [portfolio_return(w, ann_returns) for w in weights]
vols = [portfolio_vol(w, cov) for w in weights]
ef = pd.DataFrame({
"Returns": rets,
"Volatility": vols
})
return ef.plot.line(x="Volatility", y="Returns", style="o-")
ax = plot_ef2(n_points, ann_returns, cov)
ax.set_ylabel('Expected Return')
ax.set_ylim(top=0.35)
ax.set_xlim(left = 0.0115, right=0.014)

The blue curved line shows the expected return and the risk that you could obtain through different combinations of the two stocks.
The objective here was to show the portfolio creation using python but the ideal would be not to be limited to investing in just two shares. For example, you may decide to create a portfolio of 10 stocks and, after analyzing each company’s prospects, come up with predictions of their returns.

--

--

Cristiane Silva
Analytics Vidhya

Engineer, MBA in Finance & Investment, and Data Scientist who contributes code to the community. linkedin.com/in/ssilvacris/