Sharpe Ratio, Sortino Ratio and Calmar Ratio
By: Shuo Wang
Tags: Portfolio Management, Quantitative Analysis, Python, Data Science, Data Visualization

Sharpe Ratio Revisited

Sharpe ratio is the ratio of average return divided by the standard deviation of returns annualized. We had an introduction to it in a previous story.

Let’s take a look at it again with a test price time series.

import pandas as pd

import numpy as np



from pandas.tseries.offsets import BDay





def daily_returns(prices):



res = (prices/prices.shift(1) - 1.0)[1:]

res.columns = ['return']



return res





def sharpe(returns, risk_free=0):

adj_returns = returns - risk_free

return (np.nanmean(adj_returns) * np.sqrt(252)) \

/ np.nanstd(adj_returns, ddof=1)





def test_price1():



start_date = pd.Timestamp(2020, 1, 1) + BDay()



len = 100



bdates = [start_date + BDay(i) for i in range(len)]

price = [10.0 + i/10.0 for i in range(len)]



return pd.DataFrame(data={'date': bdates,

'price1': price}).set_index('date')





def test_price2():



start_date = pd.Timestamp(2020, 1, 1) + BDay()



len = 100



bdates = [start_date + BDay(i) for i in range(len)]

price = [10.0 + i/10.0 for i in range(len)]



price[40:60] = [price[40] for i in range(20)]



return pd.DataFrame(data={'date': bdates,

'price2': price}).set_index('date')





def test_price3():



start_date = pd.Timestamp(2020, 1, 1) + BDay()



len = 100



bdates = [start_date + BDay(i) for i in range(len)]

price = [10.0 + i/10.0 for i in range(len)]



price[40:60] = [price[40] - i/10.0 for i in range(20)]



return pd.DataFrame(data={'date': bdates,

'price3': price}).set_index('date')





def test_price4():



start_date = pd.Timestamp(2020, 1, 1) + BDay()



len = 100



bdates = [start_date + BDay(i) for i in range(len)]

price = [10.0 + i/10.0 for i in range(len)]



price[40:60] = [price[40] - i/8.0 for i in range(20)]



return pd.DataFrame(data={'date': bdates,

'price4': price}).set_index('date')





price1 = test_price1()

return1 = daily_returns(price1)



price2 = test_price2()

return2 = daily_returns(price2)



price3 = test_price3()

return3 = daily_returns(price3)



price4 = test_price4()

return4 = daily_returns(price4)



print('price1')

print(f'sharpe: {sharpe(return1)}')



print('price2')

print(f'sharpe: {sharpe(return2)}')



print('price3')

print(f'sharpe: {sharpe(return3)}')



print('price4')

print(f'sharpe: {sharpe(return4)}')

As you can see in this example, I have 4 test price time series. First one, price1, is simply a straight line (never mind the wiggles, it’s due to weekends that were left out). The second price time series, price2, has a flat region. The third price time series, price3, has a downward sloping region. The last time series, price4, has a slighter larger downward slope.

The sharpe ratio for each price is calculated below:

'''

price1

sharpe: 78.59900981328562

price2

sharpe: 7.9354707022912825

price3

sharpe: 3.61693599695678

price4

sharpe: 3.151996500460301 '''

As you can see, price1 has a very high sharpe ratio, due to almost non-existent volatility. Price2 has a sharpe ratio of almost 10 times less, due to the presence of a flat region. Price3 and Price4 are about two times lower than price2 due to the downward slope, and Price4 sharpe ratio is slightly lower than price3’s sharpe ratio.

Looking at these numbers, one thing that might seem a little strange is how much the sharpe ratio decreased simply due to the presence of a flat region. After all, the total return didn’t change, and there is no drawdown at all. For an investor, price1 and price2 are not all that different.

On the other hand, price3 and price4 have sizable drawdowns, yet the sharpe ratio decreased by about half, compared to a 10 times drop between price1 and price2. This seems to be a deficiency in the ability of sharpe ratio to tell us how desirable the price time series is.