kernc / backtesting.py

:mag_right: :chart_with_upwards_trend: :snake: :moneybag: Backtest trading strategies in Python.
https://kernc.github.io/backtesting.py/
GNU Affero General Public License v3.0
5.39k stars 1.05k forks source link

maximize_func: Limit Maximum Drawdown and Maximize Sortino #978

Closed houseofai closed 1 year ago

houseofai commented 1 year ago

Following the explanation on ticket #234, I want to limit the maximum drawdown to 5% and maximize the Sortino ratio. But when I define the maximum_func, the best result always provides a maximum drawdown of over 5%.

maximize_func = lambda s: int(s['Max. Drawdown [%]'] > -5.0) * s['Sortino Ratio']
stats = bt.optimize(params, maximize=maximize_func)
print(stats._strategy)
print(stats)
Start                     2022-07-19 09:30...
End                       2023-05-03 15:55...
Duration                    288 days 06:25:00
Exposure Time [%]                    1.323567
Equity Final [$]                  9498.308576
Equity Peak [$]                  10085.801078
Return [%]                          -5.016914
Buy & Hold Return [%]               18.766863
Return (Ann.) [%]                   -6.279563
Volatility (Ann.) [%]                7.639916
Sharpe Ratio                              0.0
Sortino Ratio                             0.0
Calmar Ratio                              0.0
**Max. Drawdown [%]                    -6.93757**
Avg. Drawdown [%]                   -4.540014
Max. Drawdown Duration      272 days 06:15:00
Avg. Drawdown Duration      143 days 03:13:00
# Trades                                  104
Win Rate [%]                        41.346154
Best Trade [%]                       2.128565
Worst Trade [%]                     -1.053148
Avg. Trade [%]                      -0.050091
Max. Trade Duration           0 days 00:20:00
Avg. Trade Duration           0 days 00:05:00
Profit Factor                        0.849613
Expectancy [%]                      -0.047575
SQN                                 -0.713201

I can see that the maximize_func still has an impact because if I only optimize on the Sortino ratio, I get a drawdown of 11%.

Why is the Drawdown not limited to 5%?

none2003 commented 1 year ago

@kernc Is it a good option that also has a "minimize" as well as "maximize"?

kernc commented 1 year ago

@houseofai

With your:

maximize_func = lambda s: int(s['Max. Drawdown [%]'] > -5.0) * s['Sortino Ratio']

-6 > -5 => False times 0.0 (Sortino) is 0.

Since all of your results returned 0 for this maximize_func, this is your result. maximize_func doesn't do any constraints/filtering or results. It just projects to a raw ordinal metric.

kernc commented 1 year ago

Is it a good option that also has a "minimize" as well as "maximize"?

@none2003 For ordinal metrics, min(x) == max(-x). HTH