Python For Finance Portfolio Optimization

Image for post
Image for post

Portfolio optimization is the process of selecting the best portfolio (asset distribution),out of the set of all portfolios being considered, according to some objective. The objective typically maximizes factors such as expected return, and minimizes costs like financial risk.
-Wikipedia

In this article I will show you how to create a program to optimize a stock portfolio using the efficient frontier & Python ! In modern portfolio theory, the efficient frontier is an investment portfolio which occupies the ‘efficient’ parts of the risk-return spectrum. Formally, it is the set of portfolios which satisfy the condition that no other portfolio exists with a higher expected return but with the same standard deviation of return.

The Sharpe Ratio goes further: it actually helps you find the best possible proportion of these stocks to use, in a portfolio.
- Moneychimp

The Sharpe ratio was developed by William F. Sharpe in 1966. The ratio describes how much excess return you receive for the extra volatility you endure for holding a riskier asset. It measures the performance of an investment compared to a risk-free asset (bonds, treasury bills, etc.), after adjusting for its risk. It is defined as the difference between the returns of the investment and the risk-free return, divided by the standard deviation of the investment.
-Investopedia

Usually, any Sharpe ratio greater than 1.0 is considered acceptable to good by investors. A ratio higher than 2.0 is rated as very good. A ratio of 3.0 or higher is considered excellent. A ratio under 1.0 is considered sub-optimal.

If you prefer not to read this article and would like a video representation of it, you can check out the YouTube Video . It goes through everything in this article with a little more detail, and will help make it easy for you to start programming even if you don’t have the programming language Python installed on your computer. Or you can use both as supplementary materials for learning !

Programming:

The first thing that I like to do before writing a single line of code is to put in a description in comments of what the code does. This way I can look back on my code and know exactly what it does.

# Description: This program attempts to optimize a users portfolio using the Efficient Frontier & Python.

Import the libraries.

# Import the python libraries
from pandas_datareader import data as web
import pandas as pd
import numpy as np
from datetime import datetime
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')

Get the stock symbols / tickers for the fictional portfolio. I am going to use the five most popular and best performing American technology companies known as FAANG, which is an acronym for Facebook, Amazon , Apple, Netflix , & Alphabet (formerly known as Google).

assets =  ["FB", "AMZN", "AAPL", "NFLX", "GOOG"]

Next I will assign equivalent weights to each stock within the portfolio, meaning 20% of this portfolio will have shares in Facebook (FB), 20% in Amazon (AMZN), 20% in Apple (AAPL) , 20% in Netflix (NFLX), and 20% in Google (GOOG).

This means if I had a total of $100 USD in the portfolio, then I would have $20 USD in each stock.

# Assign weights to the stocks. Weights must = 1 so 0.2 for each
weights = np.array([0.2, 0.2, 0.2, 0.2, 0.2])
weights
Image for post
Image for post

Now I will get the stocks starting date which will be January 1st 2013, and the ending date which will be the current date (today).

#Get the stock starting date
stockStartDate = '2013-01-01'
# Get the stocks ending date aka todays date and format it in the form YYYY-MM-DD
today = datetime.today().strftime('%Y-%m-%d')

Time to create the data frame that will hold the stocks Adjusted Close price.

#Create a dataframe to store the adjusted close price of the stocks
df = pd.DataFrame()
#Store the adjusted close price of stock into the data frame
for stock in assets:
df[stock] = web.DataReader(stock,data_source='yahoo',start=stockStartDate , end=today)['Adj Close']

Show the data frame and the adjusted close price of each stock.

df
Image for post
Image for post

Visually show the stock prices.

# Create the title 'Portfolio Adj Close Price History
title = 'Portfolio Adj. Close Price History '
#Get the stocks
my_stocks = df
#Create and plot the graph
plt.figure(figsize=(12.2,4.5)) #width = 12.2in, height = 4.5
# Loop through each stock and plot the Adj Close for each day
for c in my_stocks.columns.values:
plt.plot( my_stocks[c], label=c)#plt.plot( X-Axis , Y-Axis, line_width, alpha_for_blending, label)
plt.title(title)
plt.xlabel('Date',fontsize=18)
plt.ylabel('Adj. Price USD ($)',fontsize=18)
plt.legend(my_stocks.columns.values, loc='upper left')
plt.show()
Image for post
Image for post

I’m done creating the fictional portfolio. Now I want to show the daily simple returns which is a calculation of the (new_price + -old_price)/ old_price or (new_price / old_price)-1.

#Show the daily simple returns, NOTE: Formula = new_price/old_price - 1
returns = df.pct_change()
returns
Image for post
Image for post

Create and show the annualized co-variance matrix. The co-variance matrix is a mathematical concept which is commonly used in statistics when comparing data samples from different populations and is used to determine how much two random variables vary or move together (so it’s the directional relationship between two asset prices ).

The diagonal of the matrix are the variances and the other entries are the co-variances. Variance is a measure of how much a set of observations differ from each other. If you take the square root of variance you get the volatility also known as the standard deviation.

To show the annualized co-variance matrix we must multiply the co-variance matrix by the number of trading days for the current year. In this case the number of trading days will be 252 for this year.

cov_matrix_annual = returns.cov() * 252
cov_matrix_annual
Image for post
Image for post

Now calculate and show the portfolio variance using the formula :
Expected portfolio variance= WT * (Covariance Matrix) * W

port_variance = np.dot(weights.T, np.dot(cov_matrix_annual, weights))
port_variance
Image for post
Image for post

Now calculate and show the portfolio volatility using the formula :
Expected portfolio volatility= SQRT (WT * (Covariance Matrix) * W)

Image for post
Image for post

Don’t forget the volatility (standard deviation) is just the square root of the variance.

port_volatility = np.sqrt(port_variance)
port_volatility
Image for post
Image for post

Last but least not I’m going to show and calculate the portfolio annual simple return.

portfolioSimpleAnnualReturn = np.sum(returns.mean()*weights) * 252
portfolioSimpleAnnualReturn
Image for post
Image for post

Show the expected annual return, volatility or risk, and variance.

percent_var = str(round(port_variance, 2) * 100) + '%'
percent_vols = str(round(port_volatility, 2) * 100) + '%'
percent_ret = str(round(portfolioSimpleAnnualReturn, 2)*100)+'%'
print("Expected annual return : "+ percent_ret)
print('Annual volatility/standard deviation/risk : '+percent_vols)
print('Annual variance : '+percent_var)
Image for post
Image for post

So, now I can see the expected annual return on the investments which is 32% and the amount of risk for this portfolio which is 23%, but can I do better ? I think I can.

It’s now time to optimize this portfolio, meaning I want to optimize for the maximum return with the least amount of risk . Luckily their is a very nice package that can help with this created by Robert Ansrew Martin.

I will install the package that he created called pyportfolioopt.

pip install PyPortfolioOpt

Next, I will import the necessary libraries.

from pypfopt.efficient_frontier import EfficientFrontier
from pypfopt import risk_models
from pypfopt import expected_returns

Calculate the expected returns and the annualised sample covariance matrix of daily asset returns.

mu = expected_returns.mean_historical_return(df)#returns.mean() * 252
S = risk_models.sample_cov(df) #Get the sample covariance matrix

Optimize for maximal Sharpe ration .

ef = EfficientFrontier(mu, S)weights = ef.max_sharpe() #Maximize the Sharpe ratio, and get the raw weightscleaned_weights = ef.clean_weights() print(cleaned_weights) #Note the weights may have some rounding error, meaning they may not add up exactly to 1 but should be closeef.portfolio_performance(verbose=True)
Image for post
Image for post

Now we see that we can optimize this portfolio by having about 15.791% of the portfolio in Facebook, 23.296% in Amazon , 25.573% in Apple, 35.341% in Netflix and 0% in Google.

Also I can see that the expected annual return has increased to 37.6% with this optimization and the annual volatility / risk is 26.3%. This optimized portfolio has a Sharpe ratio of 1.35 which is good. The numbers in the parenthesis at the bottom are the same three numbers I just mentioned in decimal form.

I want to get the discrete allocation of each share of the stock, meaning I want to know exactly how many of each stock I should buy given some amount that I am willing to put into this portfolio.

So, for example I am willing to put in $15,000 USD into this portfolio, and need to know how much of each stock I can purchase in the portfolio to give me the optimal results.

First I will install pulp.

pip install pulp

Now it’s time to get the discrete allocation of each stock.

from pypfopt.discrete_allocation import DiscreteAllocation, get_latest_priceslatest_prices = get_latest_prices(df)
weights = cleaned_weights
da = DiscreteAllocation(weights, latest_prices, total_portfolio_value=15000)
allocation, leftover = da.lp_portfolio()
print("Discrete allocation:", allocation)
print("Funds remaining: ${:.2f}".format(leftover))
Image for post
Image for post

Alright ! Looks like I can buy 14 shares of Facebook, 2 shares of Amazon, 13 shares of Apple, and 16 shares of NetFlix for this optimized portfolio and still have about $51.67 USD leftover from my initial investment of $15,000 USD.

That’s it, we are done creating this program ! If you want to start an investment portfolio of your own, then sign up with WeBull using this link, deposit $100 and get 2 FREE stocks worth up to $1600 USD! It’s free stocks that you can either sell, or play with.

If you are also interested in reading more on Python one of the fastest growing programming languages that many companies and computer science departments use then I recommend you check out the book Learning Python written by Mark Lutz’s.

Image for post
Image for post
Learning Python

Thanks for reading this article I hope it’s helpful to you all! If you enjoyed this article and found it helpful please leave some claps to show your appreciation. Keep up the learning, and if you like Python, machine learning, mathematics, computer science, programming or algorithm analysis, please visit and subscribe to my YouTube channels (randerson112358 & compsci112358 ).

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store