Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion for Improving the CAGR Calculation in Your Package #359

Open
wangyxDQPI opened this issue Jul 11, 2024 · 3 comments
Open

Suggestion for Improving the CAGR Calculation in Your Package #359

wangyxDQPI opened this issue Jul 11, 2024 · 3 comments

Comments

@wangyxDQPI
Copy link

Dear

I hope this email finds you well. I have been using your [package name] and find it incredibly useful for my work. However, I encountered an issue with the cagr function that I believe could be improved.

Currently, the calculation of years in the cagr function is performed as follows:
years = (returns.index[-1] - returns.index[0]).days / periods
This method assumes that the periods parameter represents the number of days in a year (e.g., 252 for trading days), but it doesn't account for cases where the number of returns may not span the entire period or if there are missing days in the time series. This can lead to inaccurate results, especially in time series with irregular data points.

I suggest modifying the calculation of years to:
years = len(returns) / periods
This approach uses the length of the returns series to calculate the total number of periods, which provides a more accurate estimation of the years, especially when dealing with incomplete data.

Here's the revised version of the cagr function:
def cagr(returns, rf=0.0, compounded=True, periods=252):
"""
Calculates the communicative annualized growth return (CAGR%) of access returns

If rf is non-zero, you must specify periods.
In this case, rf is assumed to be expressed in yearly (annualized) terms
"""
total = _utils._prepare_returns(returns, rf)
if compounded:
    total = comp(total)
else:
    total = _np.sum(total)

years = len(returns) / periods

res = abs(total + 1.0) ** (1.0 / years) - 1

if isinstance(returns, _pd.DataFrame):
    res = _pd.Series(res)
    res.index = returns.columns

return res

I believe this adjustment will enhance the accuracy and robustness of the CAGR calculation in various scenarios.

Thank you for your time and for maintaining such a valuable package. I look forward to your thoughts on this suggestion.

Best regards,

yuxue wang
wangyx0629@nepu.edu.cn

@itsXactlY
Copy link

Awesome! It took me days to weeks to debug my shit here inside my framework im building, and then out of the sudden;

Full traceback: Traceback (most recent call last): File "/home/alca/projects/backtrader-quant-main/backtest_cpp_mssql.py", line 84, in backtest strategy = cerebro.run()[0] ^^^^^^^^^^^^^ File "/home/alca/projects/.bt/lib/python3.12/site-packages/backtrader/cerebro.py", line 1131, in run runstrat = self.runstrategies(iterstrat) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/alca/projects/.bt/lib/python3.12/site-packages/backtrader/cerebro.py", line 1297, in runstrategies self._runonce(runstrats) File "/home/alca/projects/.bt/lib/python3.12/site-packages/backtrader/cerebro.py", line 1656, in _runonce strat._once() File "/home/alca/projects/.bt/lib/python3.12/site-packages/backtrader/lineiterator.py", line 297, in _once indicator._once() File "/home/alca/projects/.bt/lib/python3.12/site-packages/backtrader/lineiterator.py", line 297, in _once indicator._once() File "/home/alca/projects/.bt/lib/python3.12/site-packages/backtrader/linebuffer.py", line 631, in _once self.once(self._minperiod, self.buflen()) File "/home/alca/projects/.bt/lib/python3.12/site-packages/backtrader/linebuffer.py", line 755, in once self._once_op(start, end) File "/home/alca/projects/.bt/lib/python3.12/site-packages/backtrader/linebuffer.py", line 772, in _once_op dst[i] = op(srca[i], srcb[i]) ^^^^^^^^^^^^^^^^^^^^ ZeroDivisionError: float division by zero

Popped up over and over - i thrown everything apart, besides turn of Quantstats.

Then, out of sudden this happend;

Traceback (most recent call last):
  File "/home/alca/projects/backtrader-quant-main/backtest_cpp_mssql.py", line 103, in backtest
    quantstats.reports.html(returns, output=html_filepath, title=f'{current_date}_{startdate}_to_{enddate}_1m')
  File "/home/alca/projects/.bt/lib/python3.12/site-packages/quantstats/reports.py", line 124, in html
    mtrx = metrics(
           ^^^^^^^^
  File "/home/alca/projects/.bt/lib/python3.12/site-packages/quantstats/reports.py", line 839, in metrics
    metrics["CAGR﹪%"] = _stats.cagr(df, rf, compounded) * pct
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/alca/projects/.bt/lib/python3.12/site-packages/quantstats/stats.py", line 531, in cagr
    res = abs(total + 1.0) ** (1.0 / years) - 1
                               ~~~~^~~~~~~
ZeroDivisionError: float division by zero

Your fix is just my lifesaver right now, over spend another couple hours figure this out. Many thanks! <3

@sammybadd
Copy link

Has this issue been fixed? I am still seeing incorrect CAGRs due to my data being weekly data and quantstats recognizing only daily data.

@grzesir
Copy link

grzesir commented Jul 30, 2024

Check out https://github.com/Lumiwealth/quantstats_lumi, which is being updated regularly. We are a fork of this library that is being maintained by Lumiwealth

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants