Max's StatPage

Stat Student, Data Analysis Nerd, Chinese Speaker

Risks and Returns Sharpe Ratio - Python Stock Data Analysis

Max Lang / 2021-03-27


The Sharpe Ratio - Stock Data Analysis

An investment may make sense if we expect it to return more money than it costs. But returns are only part of the story because they are risky - there may be a range of possible outcomes. How does one compare different investments that may deliver similar results on average, but exhibit different levels of risks? Enter William Sharpe. He introduced the reward-to-variability ratio in 1966 that soon came to be called the Sharpe Ratio. It compares the expected returns for two investment opportunities and calculates the additional return per unit of risk an investor could obtain by choosing one over the other. In particular, it looks at the difference in returns for two investments and compares the average difference to the standard deviation (as a measure of risk) of this difference. A higher Sharpe ratio means that the reward will be higher for a given amount of risk. It is common to compare a specific opportunity against a benchmark that represents an entire category of investments. The Sharpe ratio has been one of the most popular risk/return measures in finance, not least because it’s so simple to use. It also helped that Professor Sharpe won a Nobel Memorial Prize in Economics in 1990 for his work on the capital asset pricing model (CAPM). So let’s inspect the Sharpe ratio by calculating it for the stocks of the two tech giants Facebook and Amazon. As benchmark we’ll use the S&P 500 that measures the performance of the 500 largest stocks in the US.

# Importing required modules
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# Settings to produce nice plots in a Jupyter notebook
plt.style.use('fivethirtyeight')

# Reading in the data
stock_data = pd.read_csv('/Users/max/Documents/Data Science/Project_Notebooks/Risk and Returns- The Sharpe Ratio/datasets/stock_data.csv', parse_dates= ['Date'], index_col= ['Date']).dropna()
benchmark_data = pd.read_csv('/Users/max/Documents/Data Science/Project_Notebooks/Risk and Returns- The Sharpe Ratio/datasets/benchmark_data.csv', parse_dates= ['Date'], index_col= ['Date']).dropna() 

Let’s take a quick look the data to find out how many observations and variables it contains.

print('Stocks\n')
## Stocks
stock_data.info()
## <class 'pandas.core.frame.DataFrame'>
## DatetimeIndex: 252 entries, 2016-01-04 to 2016-12-30
## Data columns (total 2 columns):
##  #   Column    Non-Null Count  Dtype  
## ---  ------    --------------  -----  
##  0   Amazon    252 non-null    float64
##  1   Facebook  252 non-null    float64
## dtypes: float64(2)
## memory usage: 5.9 KB
print(stock_data.head())
##                 Amazon    Facebook
## Date                              
## 2016-01-04  636.989990  102.220001
## 2016-01-05  633.789978  102.730003
## 2016-01-06  632.650024  102.970001
## 2016-01-07  607.940002   97.919998
## 2016-01-08  607.049988   97.330002
print('\nBenchmarks\n')
## 
## Benchmarks
benchmark_data.info()
## <class 'pandas.core.frame.DataFrame'>
## DatetimeIndex: 252 entries, 2016-01-04 to 2016-12-30
## Data columns (total 1 columns):
##  #   Column   Non-Null Count  Dtype  
## ---  ------   --------------  -----  
##  0   S&P 500  252 non-null    float64
## dtypes: float64(1)
## memory usage: 3.9 KB
print(benchmark_data.head())
##             S&P 500
## Date               
## 2016-01-04  2012.66
## 2016-01-05  2016.71
## 2016-01-06  1990.26
## 2016-01-07  1943.09
## 2016-01-08  1922.03

Daily prices for Amazon and Facebook

Before we compare an investment in either Facebook or Amazon with the index of the 500 largest companies in the US, let’s visualize the data.

# visualize the stock_data
fig, ax= plt.subplots(2,1)
fig.tight_layout()
plt.subplots_adjust(wspace=1.0, hspace= 0.75)
plt.gcf().subplots_adjust(left=0.15, bottom= 0.15)
fig.suptitle("Stock price of Amazon and Facebook",y= 1.92, ha= "center", va= "top", size= 20)


sns.lineplot(ax= ax[0], x= stock_data.index, y= "Amazon", data= stock_data, linewidth= 1.0, color= "darkorange")
ax[0].set_ylabel("Stock Price \n in US $")
ax[0].set_title("Amazon Stock", fontsize= 15)

sns.lineplot(ax= ax[1], x= stock_data.index, y= "Facebook", data= stock_data, linewidth= 1.0, color= "steelblue")
ax[1].set_ylabel("Stock Price \n in US $")
ax[1].set_title("Facebook Stock", fontsize= 15)
plt.show()
# summarize the stock_data

stock_data.describe()
##            Amazon    Facebook
## count  252.000000  252.000000
## mean   699.523135  117.035873
## std     92.362312    8.899858
## min    482.070007   94.160004
## 25%    606.929993  112.202499
## 50%    727.875000  117.765000
## 75%    767.882492  123.902503
## max    844.359985  133.279999

S&P 500

Let’s also take a closer look at the value of the S&P 500, our benchmark.

# plot the benchmark_data
fig, ax= plt.subplots()
plt.gcf().subplots_adjust(left=0.13, bottom= 0.17)
sns.lineplot(x= benchmark_data.index, y= 'S&P 500', linewidth= 1.0, data= benchmark_data)
ax.set_title("S&P 500 Index")
ax.set_ylabel("Index Points")
plt.show()

# summarize the benchmark_data

benchmark_data.describe()
##            S&P 500
## count   252.000000
## mean   2094.651310
## std     101.427615
## min    1829.080000
## 25%    2047.060000
## 50%    2104.105000
## 75%    2169.075000
## max    2271.720000

Calculating the Sharpe Ratio: Daily Stock Returns

The Sharpe Ratio uses the difference in returns between the two investment opportunities under consideration. However, the data shows the historical value of each investment, not the return. To calculate the return, one needs to calculate the percentage change in value from one day to the next.

# calculate daily stock_data returns
stock_returns = stock_data.pct_change()

# plot the daily returns
fig, ax= plt.subplots(2, sharey= True)
fig.tight_layout()
plt.subplots_adjust(wspace=1.0, hspace= 0.5)
plt.gcf().subplots_adjust(left=0.13, bottom= 0.12)

sns.lineplot(ax= ax[0], x= stock_returns.index, y= "Amazon", linewidth= 1.0, color= "darkorange", data= stock_returns)
sns.lineplot(ax= ax[1], x= stock_returns.index, y= "Facebook", linewidth= 1.0, color= "steelblue", data= stock_returns)

ax[0].set_xlabel("Date", fontsize= 13)
ax[0].set_ylabel("Change in Percent", fontsize= 13)
ax[0].set_title("Amazon Stock", fontsize= 15)

ax[1].set_xlabel("Date", fontsize= 13)
ax[1].set_ylabel("Change in Percent", fontsize= 13)
ax[1].set_title("Facebook Stock", fontsize= 15)
plt.show()

# summarize the daily returns

stock_returns.describe()
##            Amazon    Facebook
## count  251.000000  251.000000
## mean     0.000818    0.000626
## std      0.018383    0.017840
## min     -0.076100   -0.058105
## 25%     -0.007211   -0.007220
## 50%      0.000857    0.000879
## 75%      0.009224    0.008108
## max      0.095664    0.155214

For the S&P 500, calculating daily returns works just the same way.

# calculate daily benchmark_data returns
sp_returns = benchmark_data.pct_change()

# plot the daily returns
fig, ax= plt.subplots()
plt.gcf().subplots_adjust(left=0.15, bottom= 0.15)
sns.lineplot(x= sp_returns.index, y= "S&P 500", linewidth= 1.0, data= sp_returns)
ax.set_title("S&P 500 Index")
ax.set_ylabel("Change in Percent")

plt.hlines(y= 0.0, xmin= sp_returns.index.min(), xmax= sp_returns.index.max(),linestyles= 'dashed', color= 'black', linewidth= 1.5)

plt.show()
# summarize the daily returns

sp_returns.describe()
##           S&P 500
## count  251.000000
## mean     0.000458
## std      0.008205
## min     -0.035920
## 25%     -0.002949
## 50%      0.000205
## 75%      0.004497
## max      0.024760

Calculating Excess Returns for Amazon and Facebook vs. S&P 500

Next, one needs to calculate the relative performance of stocks vs. the S&P 500 benchmark. This is calculated as the difference in returns between stock_returns and sp_returns for each day.

# calculate the difference in daily returns
excess_returns = stock_returns.sub(sp_returns["S&P 500"], axis= 0)

# plot the excess_returns
fig, ax= plt.subplots(2, sharey= True)
fig.tight_layout()
plt.subplots_adjust(wspace=1.5, hspace= 0.75)
plt.gcf().subplots_adjust(left=0.15, bottom= 0.15)


sns.lineplot(ax= ax[0], x= excess_returns.index, y= "Amazon", linewidth= 1.0, color= "darkorange", data= excess_returns)
sns.lineplot(ax= ax[1], x= excess_returns.index, y= "Facebook", linewidth= 1.0, color= "steelblue", data= excess_returns)

ax[0].set_ylabel("Difference \n in returns", fontsize= 13)
ax[0].set_title("Amazon Stock", fontsize= 15)

ax[1].set_ylabel("Difference \n in returns", fontsize= 13)
ax[1].set_title("Facebook Stock", fontsize= 15)

plt.show()
# summarize the excess_returns

excess_returns.describe()
##            Amazon    Facebook
## count  251.000000  251.000000
## mean     0.000360    0.000168
## std      0.016126    0.015439
## min     -0.100860   -0.051958
## 25%     -0.006229   -0.005663
## 50%      0.000698   -0.000454
## 75%      0.007351    0.005814
## max      0.100728    0.149686

The Sharpe Ratio

Step 1: The Average Difference in Daily Returns Stocks vs S&P 500

# calculate the mean of excess_returns 
avg_excess_return = excess_returns.mean()
print(avg_excess_return)
## Amazon      0.000360
## Facebook    0.000168
## dtype: float64

Step 2: Standard Deviation of the Return Difference

# calculate the standard deviations
sd_excess_return = excess_returns.std()
print(sd_excess_return)
## Amazon      0.016126
## Facebook    0.015439
## dtype: float64

Step 3: Compute the ratio

# calculate the daily sharpe ratio
daily_sharpe_ratio = avg_excess_return.div(sd_excess_return)
# annualize the sharpe ratio
annual_factor = np.sqrt(252)
annual_sharpe_ratio = daily_sharpe_ratio.mul(annual_factor)
df_annual_sharpe_ratio = pd.DataFrame(annual_sharpe_ratio, index= annual_sharpe_ratio.index, columns= ["Sharpe Ratio"])

print(df_annual_sharpe_ratio)

# plot the annualized sharpe ratio
##           Sharpe Ratio
## Amazon        0.354283
## Facebook      0.172329
fig, ax= plt.subplots()
sns.barplot(x= df_annual_sharpe_ratio.index, y= "Sharpe Ratio",palette= ("darkorange", "steelblue"), data= df_annual_sharpe_ratio)
ax.set_title("Annual Sharpe Ratio of Amazon and Facebook")
plt.show()

Conclusion

Given the two Sharpe ratios, which investment should you go for? In 2016, Amazon had a Sharpe ratio twice as high as Facebook. This means that an investment in Amazon returned twice as much compared to the S&P 500 for each unit of risk an investor would have assumed. In other words, in risk-adjusted terms, the investment in Amazon would have been more attractive.

This difference was mostly driven by differences in return rather than risk between Amazon and Facebook. The risk of choosing Amazon over FB (as measured by the standard deviation) was only slightly higher so that the higher Sharpe ratio for Amazon ends up higher mainly due to the higher average daily returns for Amazon.

When faced with investment alternatives that offer both different returns and risks, the Sharpe Ratio helps to make a decision by adjusting the returns by the differences in risk and allows an investor to compare investment opportunities on equal terms, that is, on an ‘apples-to-apples’ basis.