quantstats icon indicating copy to clipboard operation
quantstats copied to clipboard

CAGR and Sharpe ratio should have the same sign

Open kasparthommen opened this issue 3 years ago • 2 comments

Hi,

Please consider the following snippet:

import datetime as dt
import pandas as pd
import quantstats as qs

returns = [0.5, -0.4, 0.5, -0.4]  # i.e., +50%, -40%, +50%, -40%
years = [dt.date(2000 + i, 1, 1) for i in range(len(returns))]

returns = pd.Series(data=returns, index=years)
cagr = qs.stats.cagr(returns)
sharpe = qs.stats.sharpe(returns, periods=1)
print(f"CAGR = {cagr:.2f}, Sharpe = {sharpe:.2f}")

This will print:

CAGR = -0.07, Sharpe = 0.10

Now, an investment that has a negative cumulative return cannot possibly have a positive Sharpe ratio, and vice-versa. I have written about this problem in my blog, and I also propose a solution there:

https://kasparthommen.github.io/posts/sharpe-ratio.html

TL;DR: Use log returns for Sharpe/Sortino/CALMAR/... ratio computations, not simple returns.

kasparthommen avatar Nov 30 '22 15:11 kasparthommen

I think in this case, Sharpe is computed as per : ( returns_mean - rfr) / stdDev(returns)

np.mean([0.50, -0.4, +0.5, -0.4]) 
0.04999999999999999

std = np.std([0.50, -0.4, 0.5, -0.4])
0.45

Assuming rfr=0.00 , we should get a Sharpe = 0.11 , or 0.096 if we use ddof=1 for stdDev

CAGR might be negative because it's the compounded average growth rate, which is not indeed the mean of individual returns. In fact +50% and -50% have a mean of 0% , but CAGR is 86% , and 75% cumulative return.

I agree with you this can be counterintuitive (and potentially misleading) , and I also think your suggestion to use LogReturns instead, might be solving this issue, on the other hand, Log returns are an approximation of returns, and because of the nature of Log function, the approximation is only acceptable for small values, in your case, with fairly big values (~ grater than 10%) , this is the comparison:

np.array([1.50, 1-0.4, 1+0.5, 1-0.4]).prod() -1 
= -0.19

ret = np.array([0.50, -0.4, 0.5, -0.4])
np.log1p(ret).sum()
= -21%

Log Returns are excellent for intraday prices, daily returns or up to weekly maybe, I would not personally use that for yearly returns, as the results can be quite off.

In any case, a very nice improvement to QuantStats would be to offer the option to use LogReturns or Returns for Sharpe. 100% agree with you for that.

Looking forward to hearing from you. Thanks

lbsm2017 avatar Dec 03 '22 20:12 lbsm2017

Hi @lbsm2017,

Thanks for the detailed answer. You are right that log returns "compress" returns that are large in absolute value. However, when using log returns in the Sharpe ratio computation, the fact that it's a ratio cancels out that effect to some extent:

  • In the numerator, the mean of the log returns is a bit smaller than if computed on simple returns
  • In the denominator, the standard deviation computed on log returns is also a bit smaller than if computed on simple returns

Try it on real-world data - you'll see that the difference between a Sharpe ratio computed using log returns vs. simple returns is quite small, but the former comes with the additional benefit of always having the same sign as the CAGR.

kasparthommen avatar Dec 06 '22 16:12 kasparthommen