Maximizing Portfolio Performance: Leveraging the Sharpe Ratio for Optimal Balance

A comprehensive guide to using the Sharpe Ratio as a tool for optimizing and balancing your financial portfolio

As a pre-requisite, I recommend reading my previous blog How To Build A Financial Portfolio Using PythonThis would help with a basic understanding of the financial portfolio, calculation of returns, building a portfolio, and optimization but please feel free to continue reading.

In this blog, we will cover the below topics

  • What is a Sharpe Ratio?
  • What is an Efficient Frontier?
  • Data loading and exploratory data analysis — EDA
  • Calculation of Sharpe Ratio and its interpretation
  • Building a portfolio of banking stocks
  • Optimizing the portfolio for better returns at reasonable risk
  • Comparing different portfolios

What is a Sharpe Ratio?

The Sharpe ratio (also known Sharpe index) is a ratio to measure the performance of an investment such as a portfolio. It was proposed by William Sharpe in 1966 as a ratio that is a difference in the returns on the portfolio and risk-free rate to the volatility of the portfolio. The risk-free return is the theoretical return that has zero risk or it can be an industry benchmark eg: government bonds etc where risk is minimal or close to zero. It helps us determine how much the excess return is for a unit of volatility. In short, the higher Sharpe ratio indicates that for a lower risk, the portfolio had higher returns. In this blog, we assume the risk-free rate to be zero.

Sharpe Ratio = (Rₚ — Rₑ) / SDₚ

Rₚ = return of the portolio
Rₑ = risk-free returns
SDₚ = Standard deviation of the portfolio

What is an Efficient Frontier?

Later in this blog, we will build a few thousand portfolios to arrive at the most optimal weights for the stocks in the portfolio. Remember, we have assigned equal weightage earlier. Once we build this portfolio, there will be a set of portfolios that would give higher returns either for a defined level of risk or minimum risk. This leads us to a theory called Efficient Frontier introduced by Harry Markowitz in 1952. The theory graphically represents the distribution of portfolios that maximize the returns for defined risk.

Source: financetrain.com

The curve in the Efficient Frontier graph shows the benefits of diversification and the portfolio’s risk-to-reward trade-off. The optimal portfolio is the one with the highest return for reasonable (acceptable) risk. An investor with a high-risk appetite would incline towards the right side of the curve with high risk and conservative ones would prefer to be on the left side of the frontier with lesser risk. All the portfolios that fall on the frontier line are efficient and any portfolio that is either below or above is not optimal because they offer either a lesser return for the same risk or a higher risk for the same return. Our objective will be to create an Efficient Frontier chart and find a combination of weights for each stock to build an optimal portfolio.

Exploratory data analysis (EDA)

We will be downloading and working on a year of data from Feb 2022 to Feb 2023.

Load the libraries

We will use nsepy to load the data for all the stocks. The rest of the libraries has standard usage for data loading, data transformation, and visualization.

import numpy as np
import pandas as pd
import warnings
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from statistics import NormalDist
import statistics
import seaborn as sns
from datetime import date
from nsepy import get_history as gh
from mplcursors import cursor

Load the data and set the initial allocation

We have an investment amount of 1,00,000 and plan to invest in five different banking stocks i.e Axis Bank, HBFC Bank, ICICI Bank, Kotak Bank, and SBI. As we do not know how much to allocate in each of the stocks right now, let’s start with an equal proportion i.e, 20% into each of the stocks. Later, we will calculate the right allocation (%) for each of the stocks to maximize the returns at reasonable risk. Ideally, a portfolio should have a mix of stocks from various sectors but for this blog let us have stocks from the banking sector.

initial_investment: int = 100000
start_date = date(2022,2,2)
end_date = date(2023,2,2)
stocksymbols = ['AXISBANK', 'HDFCBANK', 'ICICIBANK', 'KOTAKBANK', 'SBIN']
weights = np.array([0.2, 0.2, 0.2, 0.2, 0.2])

Calculate the daily returns

def load_stock_data(start_date, end_date, investment: int, ticker: str):
df = pd.DataFrame()
for i in range(len(ticker)):
data = gh(symbol=ticker[i],start= start_date, end=end_date)
[['Symbol','Close']]
data.rename(columns={'Close':data['Symbol'][0]},inplace=True)
data.drop(['Symbol'], axis=1,inplace=True)
if i == 0:
df = data
if i != 0:
df = df.join(data)

return df

df_stockPrice = load_stock_data(start_date, end_date,
initial_investment, stocksymbols)

df_returns = df_stockPrice.pct_change().dropna()
Stock price Output:

AXISBANK HDFCBANK ICICIBANK KOTAKBANK SBIN
Date
2022-02-02 804.10 1531.20 813.75 1942.60 539.8
2022-02-03 799.55 1515.35 808.95 1909.05 540.1
2022-02-04 798.55 1524.00 805.05 1884.20 530.3
.....

Returns Output:

AXISBANK HDFCBANK ICICIBANK KOTAKBANK SBIN
Date
2022-02-03 -0.005659 -0.010351 -0.005899 -0.017271 0.000556
2022-02-04 -0.001251 0.005708 -0.004821 -0.013017 -0.018145
2022-02-07 -0.011208 -0.036647 -0.023166 -0.029588 0.005563
.....

Let’s visualize the price and the cumulative trends of all the stock

Source: Author | Price Trends of all the stocks
Source: Author | Cumulative Trends of all the stocks
Source: Author | Histogram of returns of stocks

Calculation of Sharpe Ratio

We will calculate the weighted returns and take a summation along the columns

df_returns['Portfolio'] = (weights * df_returns.values).sum(axis=1)

OUTPUT:
AXISBANK HDFCBANK ICICIBANK KOTAKBANK SBIN Portfolio
Date
2022-02-03 -0.005659 -0.010351 -0.005899 -0.017271 0.000556 -0.008825
2022-02-04 -0.001251 0.005708 -0.004821 -0.013017 -0.018145 -0.006149
2022-02-07 -0.011208 -0.036647 -0.023166 -0.029588 0.005563 -0.021400

Calculate the mean and standard deviation

# Average daily return
df_returns['Portfolio'].mean()

OUTPUT:
0.0001426

# Standard deviation of the portfolio
df_returns['Portfolio'].std()

OUTPUT:
0.0127614

calculate the Sharpe ratio

# Sharpe ratio
sharpe_ratio = df_returns['Portfolio'].mean() /
df_returns['Portfolio'].std()

OUTPUT:
0.0111790

Finding the right balance for the portfolio

We had initially assigned equal weights to the stocks. Now that we understand the shape ratio, let’s build a few thousand portfolios with varying weightage for each of the stocks but at the same time ensure that the sum of all the weights adds up to 1. We will calculate the portfolio return, risk, weights assigned, and shape ratio in each iteration.


def sim_portfolio(weights):
# port_stdev = np.sqrt(weights.T.dot(cov_matrix).dot(weights))
# return port_stdev
port_mean = (weights * df_returns.values).sum(axis=1)
# Annualized mean
port_mean = port_mean * 252
port_sd = np.sqrt(weights.T.dot(df_returns.cov() * 252 ).dot(weights))

port_var = np.percentile(port_mean, 5, interpolation = 'lower')
port_var_95.append(port_var)

port_volatility.append(port_sd)
mc_sim_sr = (port_mean.mean() / port_sd)

return mc_sim_sr

Please refer to the complete code on Github

At the end of the 25k iteration, we have a summary of the simulation in a tabular form where each row represents an iteration with parameters associated with it.

  Returns   Risk     Sharpe     Ratio Weights
0 0.006322 0.203214 0.078392 [0.05091, 0.26731, 0.13599, 0.35021, 0.19558]
1 0.020290 0.209528 0.244030 [0.17965, 0.30068, 0.10195, 0.08729, 0.33043]
2 0.017599 0.212385 0.208811 [0.31511, 0.16065, 0.04037, 0.17625, 0.30762]
3 0.022703 0.220802 0.259110 [0.35485, 0.02419, 0.21121, 0.10095, 0.3088]
....
....
24998 0.020777 0.210912 0.248245 [0.29097, 0.03917, 0.34305, 0.19782, 0.12899]
24999 0.014266 0.202581 0.177462 [0.16349, 0.21943, 0.19902, 0.26292, 0.15515]

Our interest is to find the portfolio that has the maximum value for the Sharpe ratio and this happens to be portfolio number 8618 with a Sharpe ratio of 0.4364 and weights as [0.51593, 0.39851, 0.07618, 0.00189, 0.0075].

It is also to be noted that the initial Sharpe ratio was 0.011179 where we had not factored in any weights and the revised Sharpe ratio is 0.4364

sr_max = df_risk[df_risk['Sharpe Ratio'] == df_risk['Sharpe Ratio'].max()]

OUTPUT:

Returns Risk Sharpe Ratio Weights
8618 0.038822 0.224159 0.436432 [0.51593, 0.39851, 0.07618, 0.00189, 0.0075]

So there we have our revised weights for each of the stocks in the portfolio.

AxisBank = 0.5159
HDFCBank = 0.39851
ICICIBANK = 0.07618
KOTAK BANK = 0.00189
SBI = 0.0075

Let us visualize the relation between volatility and returns along with the Sharpe ratio to get a better picture. Also, highlight the data point that represents the highest Sharpe ratio.

plt.figure(figsize=(12,8))
plt.scatter(port_volatility,
port_returns,
c=sharpe_ratio,
cmap='plasma')
plt.plot(sr_max['Risk'], sr_max['Returns'], marker = 'p',
ms = 10, color = 'r')
plt.text(sr_max['Risk']+0.001, sr_max['Returns'], 'Max Sharpe Ratio')
plt.colorbar(label='Sharpe Ratio')
plt.xlabel('Volatility')
plt.ylabel('Return')
Source: Author | Return Vs Volatility Vs Sharpe Ratio

The optimized portfolio has a return of 3.8% with a Sharpe ratio of 0.44 or a value at risk at a 95% probability is 2.2% meaning the portfolio loss will not be more than 2.2%.

Comparing different Portfolios

In our previous blog, How To Build A Financial Portfolio Using Python, we built a different portfolio and optimized it with a simulation to minimize the risk of the portfolio but we didn’t use the Sharpe ratio instead we used value at risk (VaR). Let us apply the Sharpe ratio approach to that portfolio and check if the weights of each of the stock change significantly or remain the same.

initial_investment = 100000
startdate = date(2022,2,2)
end_date = date(2023,2,2)
stocksymbols = ['TATAMOTORS','DABUR', 'ICICIBANK','WIPRO','INFY']
     Returns Risk     Sharpe Ratio Weights
2889 0.01705 0.189345 0.226913 [0.03087, 0.3918, 0.52679, 0.00689, 0.04365]

Weights
Previous Current
Stocks
TATAMOTORS 0.0529 0.0308
DBAUR 0.3400 0.3918
ICICIBank 0.3098 0.5267
WIPRO 0.1863 0.0068
INFY 0.1108 0.0436

With the VaR approach, we got a return of 16.9 % with a risk of 18.2% at a 95% confidence interval. With the Sharpe ratio, we got a return of 17.05% with a risk of 18.9%. Well, we do see noticeable changes in the weights, especially for WIPRO and INFY but the return and risk trade-off is almost similar. There is a 0.15 % increase in returns with the Sharpe ratio approach which is marginal but with a large fund and long investment period, this might just be the preferred one.

Closing thoughts

There are various methods for portfolio optimization and in this blog, we learned the application of the Sharpe ratio and the Efficient Frontier to build an optimal portfolio. The Sharpe ratio is widely used in comparing portfolios that are similar and as the ratio is very intuitive, it is easy to interpret as well. Also, in the last section of the blog, we compared different portfolios to verify performance.

I hope you liked the article and found it helpful.

You can connect with me — on Linkedin and Github

Disclaimer

The blog is only for educational purposes and should not be used as professional advice for making any real-world financial decisions.

References

Investopedia

Leave a Reply

Your email address will not be published. Required fields are marked *