diff --git a/README.md b/README.md index 7328038e..d07b8680 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,8 @@ CAGR [%] 16.80 Sharpe Ratio 0.66 Sortino Ratio 1.30 Calmar Ratio 0.77 +Alpha [%] 450.62 +Beta 0.02 Max. Drawdown [%] -33.08 Avg. Drawdown [%] -5.58 Max. Drawdown Duration 688 days 00:00:00 diff --git a/backtesting/_stats.py b/backtesting/_stats.py index 359e57d4..045bd7ca 100644 --- a/backtesting/_stats.py +++ b/backtesting/_stats.py @@ -152,6 +152,13 @@ def _round_timedelta(value, _period=_data_period(index)): s.loc['Sortino Ratio'] = (annualized_return - risk_free_rate) / (np.sqrt(np.mean(day_returns.clip(-np.inf, 0)**2)) * np.sqrt(annual_trading_days)) # noqa: E501 max_dd = -np.nan_to_num(dd.max()) s.loc['Calmar Ratio'] = annualized_return / (-max_dd or np.nan) + equity_log_returns = np.log(equity[1:] / equity[:-1]) + market_log_returns = np.log(c[1:] / c[:-1]) + cov_matrix = np.cov(equity_log_returns, market_log_returns) + beta = cov_matrix[0, 1] / cov_matrix[1, 1] + # Jensen CAPM Alpha: can be strongly positive when beta is negative and B&H Return is large + s.loc['Alpha [%]'] = s.loc['Return [%]'] - risk_free_rate * 100 - beta * (s.loc['Buy & Hold Return [%]'] - risk_free_rate * 100) # noqa: E501 + s.loc['Beta'] = beta s.loc['Max. Drawdown [%]'] = max_dd * 100 s.loc['Avg. Drawdown [%]'] = -dd_peaks.mean() * 100 s.loc['Max. Drawdown Duration'] = _round_timedelta(dd_dur.max()) diff --git a/backtesting/backtesting.py b/backtesting/backtesting.py index 688aadf0..25d98f52 100644 --- a/backtesting/backtesting.py +++ b/backtesting/backtesting.py @@ -1262,6 +1262,8 @@ def run(self, **kwargs) -> pd.Series: Sharpe Ratio 0.58038 Sortino Ratio 1.08479 Calmar Ratio 0.44144 + Alpha [%] 394.37391 + Beta 0.03803 Max. Drawdown [%] -47.98013 Avg. Drawdown [%] -5.92585 Max. Drawdown Duration 584 days 00:00:00 diff --git a/backtesting/test/_test.py b/backtesting/test/_test.py index c57ca668..eeb46d40 100644 --- a/backtesting/test/_test.py +++ b/backtesting/test/_test.py @@ -317,6 +317,8 @@ def test_compute_stats(self): 'Start': pd.Timestamp('2004-08-19 00:00:00'), 'Win Rate [%]': 46.96969696969697, 'Worst Trade [%]': -18.39887353835481, + 'Alpha [%]': 394.37391142027462, + 'Beta': 0.03803390709192, }) def almost_equal(a, b):