diff --git a/README.md b/README.md index 5363eab7..e46f8c09 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ Avg. Trade Duration 32 days 00:00:00 Profit Factor 2.13 Expectancy [%] 6.91 SQN 1.78 +Kelly Criterion 0.6134 _strategy SmaCross(n1=10, n2=20) _equity_curve Equ... _trades Size EntryB... diff --git a/backtesting/_stats.py b/backtesting/_stats.py index 5cd64cd6..df72e1f1 100644 --- a/backtesting/_stats.py +++ b/backtesting/_stats.py @@ -117,11 +117,11 @@ def _round_timedelta(value, _period=_data_period(index)): # Our Sharpe mismatches `empyrical.sharpe_ratio()` because they use arithmetic mean return # and simple standard deviation - s.loc['Sharpe Ratio'] = np.clip((s.loc['Return (Ann.) [%]'] - risk_free_rate) / (s.loc['Volatility (Ann.) [%]'] or np.nan), 0, np.inf) # noqa: E501 + s.loc['Sharpe Ratio'] = (s.loc['Return (Ann.) [%]'] - risk_free_rate) / (s.loc['Volatility (Ann.) [%]'] or np.nan) # noqa: E501 # Our Sortino mismatches `empyrical.sortino_ratio()` because they use arithmetic mean return - s.loc['Sortino Ratio'] = np.clip((annualized_return - risk_free_rate) / (np.sqrt(np.mean(day_returns.clip(-np.inf, 0)**2)) * np.sqrt(annual_trading_days)), 0, np.inf) # noqa: E501 + 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'] = np.clip(annualized_return / (-max_dd or np.nan), 0, np.inf) + s.loc['Calmar Ratio'] = annualized_return / (-max_dd or np.nan) 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()) @@ -137,6 +137,8 @@ def _round_timedelta(value, _period=_data_period(index)): s.loc['Profit Factor'] = returns[returns > 0].sum() / (abs(returns[returns < 0].sum()) or np.nan) # noqa: E501 s.loc['Expectancy [%]'] = returns.mean() * 100 s.loc['SQN'] = np.sqrt(n_trades) * pl.mean() / (pl.std() or np.nan) + win_prob = (pl > 0).sum() / n_trades + s.loc['Kelly Criterion'] = win_prob - (1 - win_prob) / (pl[pl > 0].mean() / pl[pl < 0].mean()) # noqa: E501 s.loc['_strategy'] = strategy_instance s.loc['_equity_curve'] = equity_df diff --git a/backtesting/backtesting.py b/backtesting/backtesting.py index 504b634f..296dfa4b 100644 --- a/backtesting/backtesting.py +++ b/backtesting/backtesting.py @@ -1134,6 +1134,7 @@ def run(self, **kwargs) -> pd.Series: Profit Factor 2.08802 Expectancy [%] 8.79171 SQN 0.916893 + Kelly Criterion 0.6134 _strategy SmaCross _equity_curve Eq... _trades Size EntryB... diff --git a/backtesting/test/_test.py b/backtesting/test/_test.py index b82dfd65..27fd989c 100644 --- a/backtesting/test/_test.py +++ b/backtesting/test/_test.py @@ -276,6 +276,7 @@ def test_compute_stats(self): 'Return [%]': 414.2298999999996, 'Volatility (Ann.) [%]': 36.49390889140787, 'SQN': 1.0766187356697705, + 'Kelly Criterion': 0.7875234266909678, 'Sharpe Ratio': 0.5803778344714113, 'Sortino Ratio': 1.0847880675854096, 'Start': pd.Timestamp('2004-08-19 00:00:00'),