FMP

Enter

API Tools

Extras

Sep 11, 2023 5:40 PM - Rajnish Katharotiya

Image credit: Scott Graham

Portfolio optimization is the process to identify the best possible portfolio from a set of portfolios. But how do we define the best portfolio?

The answer depends on the investor profile. We may have investors pursuing different objectives when optimizing their portfolio. For example, young investors may prefer to find portfolios maximizing expected return. While older investors could aim to find portfolio minimizing the risk.

Photo by Pixabay on Pexels

One of the most relevant theories on portfolio optimization was developed by Harry Markowitz. His theory, known as modern portfolio theory, states that investors can build portfolios which maximize expected return given a predefine level of risk.

The goal according to this theory is to select a level of risk that an investor is comfortable with. Then find a portfolio that maximizes returns based on the selected risk level.

Another approach to find the best possible portfolio is to use the Sharpe Ratio. The Sharpe ratio of a portfolio helps investors to understand the return of a portfolio based on the level of risk taken.

Below is the Sharpe ratio formula where Rp is the return of the portfolio. Rf is the risk free rate and Op is the standard deviation (i.e. portfolio risk) of the portfolio.

*Sharpe Ratio = (Rp - Rf) / Op*

The last element in the Sharpe Ratio is the Risk free rate (Rf). A common proxy for the risk free rate is to use Treasury Bill yields.

The higher the Sharpe Ratio, the better a portfolio returns have been relative to the taken risk. If we could choose between multiple portfolio options with similar characteristics, we should select the portfolio with the highest Sharpe Ratio.

In the next section we are going to calculate the Sharpe Ratio for multiple random generated portfolios.

Now that we know a bit more about portfolio optimization lets find out how to optimize a portfolio using Python.

To keep things consistent, I will follow the same methodology that we applied in my previous post in order to calculate portfolio returns and portfolio risk. Therefore, I will not go into the details on how to do this part.

See below a summary of the Python portfolio optimization process that we will follow:

NVS, AAPL, MSFT and GOOG.

We will generate 2000 random portfolios. That is 2000 portfolios containing our 4 stocks with different weights. We will calculate portfolio returns, portfolio risk and the Sharpe Ratio for each of the random generated portfolios.

Then, we will visualize the portfolio returns, risks and Sharpe Ratio using Matplotlib.

Finally, we will find out what is the portfolio with the highest return, lowest risk and highest Sharpe Ratio.

So let's start coding.

import requests

import pandas as pd

import numpy as np

import matplotlib.pyplot as plt

stocks = ['NVS','AAPL','MSFT','GOOG']

empresas = {}

api_key = 'your api key'

#Get all prices into a dataframe

for stock in stocks:

prices = requests.get(f'https://financialmodelingprep.com/api/v3/historical-price-full/{stock}?serietype=line&apikey={api_key}').json()

prices = prices['historical'][-900:]

prices = pd.DataFrame(prices)

empresas[stock] = prices.set_index('date')

empresas[stock] = empresas[stock]['close']

portfolio = pd.concat(empresas, axis=1)

return_stocks = portfolio.pct_change()

return_stocks

Next, we are going to generate 2000 random portfolios (i.e. random weights) and calculate the returns, risk and Sharpe Ratio for each of them.

We start by defining empty lists where we will append the calculated portfolio returns, risk and Sharpe Ratio for each of the random portfolios. The calculation will happen in a for loop. Note that we use Numpy to generate random arrays containing each of the portfolio weights.

number_of_portfolios = 2000

RF = 0

portfolio_returns = []

portfolio_risk = []

sharpe_ratio_port = []

portfolio_weights = []

for portfolio in range (number_of_portfolios):

weights = np.random.random_sample((len(stocks)))

print(type(weights))

print(weights)

#below line ensures that the sum of our weights is 1

weights = weights / np.sum(weights)

#outcome

[0.78253875 0.43725429 0.11399585 0.44219616]

For simplicity reasons we have assumed a Risk free rate of 0. Having our portfolio weights, we can move on to calculate the annualised portfolio returns, risk and Sharpe Ratio. If you are unfamiliar with the calculation, feel free to have a look at my previous post where portfolio risk calculation is explained in details.

annualize_return = np.sum((return_stocks.mean() * weights) * 252)

portfolio_returns.append(annualize_return)

#variance

matrix_covariance_portfolio = (return_stocks.cov())*252

portfolio_variance = np.dot(weights.T,np.dot(matrix_covariance_portfolio, weights))

portfolio_standard_deviation= np.sqrt(portfolio_variance)

portfolio_risk.append(portfolio_standard_deviation)

#sharpe_ratio

sharpe_ratio = ((annualize_return- RF)/portfolio_standard_deviation)

sharpe_ratio_port.append(sharpe_ratio)

#keep weights as well to find out later the weights from the optimized portfolio

portfolio_weights.append(weights)

Finally, we convert our list into Numpy arrays:

#convert to arrays

portfolio_risk = np.array(portfolio_risk)

portfolio_returns = np.array(portfolio_returns)

sharpe_ratio_port = np.array(sharpe_ratio_port)

Now that we have created 2000 random portfolios, we can visualize them using a Scatter plot in Matplotlib:

plt.figure(figsize=(10, 5))

plt.scatter(portfolio_risk, portfolio_returns, c=portfolio_returns / portfolio_risk)

plt.xlabel('volatility')

plt.ylabel('returns')

plt.colorbar(label='Sharpe ratio')

In the output graph, each point represents a portfolio. We see that portfolios with the higher Sharpe Ratio are shown as yellow. Yellow coloured portfolios are preferable since they offer better risk adjusted returns.

But how can we identify which portfolio (i.e. portfolio weights) has the highest Sharpe Ratio? And what about the portfolio with the highest return? And lowest risk?

We can find the answer to that questions by transforming our data into a Pandas DataFrame and performing some basic queries.

porfolio_metrics = [portfolio_returns,portfolio_risk,sharpe_ratio_port, portfolio_weights]

#from Python list we create a Pandas DataFrame

portfolio_dfs = pd.DataFrame(porfolio_metrics)

portfolio_dfs = portfolio_dfs.T

#Rename the columns:

portfolio_dfs.columns = ['Port Returns','Port Risk','Sharpe Ratio','Portfolio Weights']

#convert from object to float the first three columns.

for col in ['Port Returns', 'Port Risk', 'Sharpe Ratio']:

portfolio_dfs[col] = portfolio_dfs[col].astype(float)

portfolio_dfs

By looking into the DataFrame, we see that each row represents a different portfolio. For example, row 1 contains a portfolio with 18% weight in NVS, 45% in AAPL, etc.

Now, we are ready to use Pandas methods such asidmax and idmin. They will allow us to find out which portfolio has the highest returns and Sharpe Ratio and minimum risk:

#portfolio with the highest Sharpe Ratio

Highest_sharpe_port = portfolio_dfs.iloc[portfolio_dfs['Sharpe Ratio'].idxmax()]

#portfolio with the minimum risk

min_risk = portfolio_dfs.iloc[portfolio_dfs['Port Risk'].idxmin()]

print(Highest_sharpe_port)

print(min_risk)

#Highest Sharpe Ratio

Port Returns 0.342024

Port Risk 0.26507

Sharpe Ratio 1.29032

Portfolio Weights [0.055480947132996186, 0.20213618958420174, 0....

#Minimum Risk

Port Returns 0.114196

Port Risk 0.195615

Sharpe Ratio 0.58378

Portfolio Weights [0.7672050820919301, 0.00958106941668406, 0.02...

Within seconds, our Python code returns the portfolio with the highest Sharpe Ratio as well as the portfolio with the minimum risk.

By altering the variables a bit, you should be able to reuse the code to find the best portfolio using your favourite stocks.

May 14, 2024 11:41 AM - Sanzhi Kobzhan

A stock's target price, also known as its fair value, is an indication of what a share can cost based on the company’s forecasted financial statements. It is important to know a stock's fair value to find undervalued stocks with great growth potential. Let's consider how investment analysts calculat...

May 16, 2024 8:06 PM - Gordon Thompson

On Thursday, May 16, 2024, Deutsche Bank upgraded its rating on NASDAQ:ASTS to Buy, maintaining a hold position. At the time of the announcement, ASTS was trading at $4.03. This decision was accompanied by an increase in the price target for AST SpaceMobile, raised to $22 from $19, as detailed in a ...

May 24, 2024 9:30 AM - Rajnish Katharotiya

Earnings call transcripts are invaluable resources for investors, analysts, and financial enthusiasts. They provide insights into a company's performance, strategy, and future outlook, making them essential for making informed investment decisions. With Financial Modeling Prep, Earnings Call Transcr...