From c0d01dbc26a1552562ca339a92111f6193e7a02c Mon Sep 17 00:00:00 2001 From: sid Date: Wed, 6 Oct 2021 13:24:27 +0530 Subject: [PATCH 1/5] add max_drawdown loss --- .../optimize/hyperopt_loss_max_drawdown.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 freqtrade/optimize/hyperopt_loss_max_drawdown.py diff --git a/freqtrade/optimize/hyperopt_loss_max_drawdown.py b/freqtrade/optimize/hyperopt_loss_max_drawdown.py new file mode 100644 index 000000000..e6f73e04a --- /dev/null +++ b/freqtrade/optimize/hyperopt_loss_max_drawdown.py @@ -0,0 +1,42 @@ +""" +MaxDrawDownHyperOptLoss + +This module defines the alternative HyperOptLoss class which can be used for +Hyperoptimization. +""" +from datetime import datetime +from freqtrade.data.btanalysis import calculate_max_drawdown +from freqtrade.optimize.hyperopt import IHyperOptLoss + +from pandas import DataFrame + + +class MaxDrawDownHyperOptLoss(IHyperOptLoss): + + """ + Defines the loss function for hyperopt. + + This implementation optimizes for max draw down and profit + Less max drawdown more profit -> Lower return value + """ + + @staticmethod + def hyperopt_loss_function(results: DataFrame, trade_count: int, + min_date: datetime, max_date: datetime, + *args, **kwargs) -> float: + + """ + Objective function. + + Uses profit ratio weighted max_drawdown when drawdown is available. + Otherwise directly optimizes profit ratio. + """ + total_profit = results['profit_ratio'].sum() + try: + max_drawdown = calculate_max_drawdown(results) + except ValueError: + # No losing trade, therefore no drawdown. + return -total_profit + max_drawdown_rev = 1 / max_drawdown[0] + ret = max_drawdown_rev * total_profit + return -ret \ No newline at end of file From 6ba46b38bdd434ddd65b065c6393beb0b29aa492 Mon Sep 17 00:00:00 2001 From: sid Date: Wed, 6 Oct 2021 13:46:05 +0530 Subject: [PATCH 2/5] fix formatting --- freqtrade/optimize/hyperopt_loss_max_drawdown.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/hyperopt_loss_max_drawdown.py b/freqtrade/optimize/hyperopt_loss_max_drawdown.py index e6f73e04a..4fa32c00e 100644 --- a/freqtrade/optimize/hyperopt_loss_max_drawdown.py +++ b/freqtrade/optimize/hyperopt_loss_max_drawdown.py @@ -5,11 +5,12 @@ This module defines the alternative HyperOptLoss class which can be used for Hyperoptimization. """ from datetime import datetime -from freqtrade.data.btanalysis import calculate_max_drawdown -from freqtrade.optimize.hyperopt import IHyperOptLoss from pandas import DataFrame +from freqtrade.data.btanalysis import calculate_max_drawdown +from freqtrade.optimize.hyperopt import IHyperOptLoss + class MaxDrawDownHyperOptLoss(IHyperOptLoss): @@ -31,7 +32,7 @@ class MaxDrawDownHyperOptLoss(IHyperOptLoss): Uses profit ratio weighted max_drawdown when drawdown is available. Otherwise directly optimizes profit ratio. """ - total_profit = results['profit_ratio'].sum() + total_profit = results['profit_ratio'].sum() try: max_drawdown = calculate_max_drawdown(results) except ValueError: @@ -39,4 +40,4 @@ class MaxDrawDownHyperOptLoss(IHyperOptLoss): return -total_profit max_drawdown_rev = 1 / max_drawdown[0] ret = max_drawdown_rev * total_profit - return -ret \ No newline at end of file + return -ret From 46c320513aa0b825b370034feba9d6e9f29af312 Mon Sep 17 00:00:00 2001 From: sid Date: Thu, 7 Oct 2021 08:07:07 +0530 Subject: [PATCH 3/5] use profit_abs --- freqtrade/optimize/hyperopt_loss_max_drawdown.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/optimize/hyperopt_loss_max_drawdown.py b/freqtrade/optimize/hyperopt_loss_max_drawdown.py index 4fa32c00e..6777fb2e8 100644 --- a/freqtrade/optimize/hyperopt_loss_max_drawdown.py +++ b/freqtrade/optimize/hyperopt_loss_max_drawdown.py @@ -32,9 +32,9 @@ class MaxDrawDownHyperOptLoss(IHyperOptLoss): Uses profit ratio weighted max_drawdown when drawdown is available. Otherwise directly optimizes profit ratio. """ - total_profit = results['profit_ratio'].sum() + total_profit = results['profit_abs'].sum() try: - max_drawdown = calculate_max_drawdown(results) + max_drawdown = calculate_max_drawdown(results, value_col='profit_abs') except ValueError: # No losing trade, therefore no drawdown. return -total_profit From 45b7a0c8377a5493496abafa70e48b0872d0b4b5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 7 Oct 2021 07:12:45 +0200 Subject: [PATCH 4/5] Add Test and docs for MaxDrawDownHyperOptLoss --- docs/hyperopt.md | 18 ++++++++++-------- freqtrade/constants.py | 3 ++- tests/optimize/test_hyperoptloss.py | 5 +++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 09d43939a..45e0d444d 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -60,7 +60,7 @@ optional arguments: Specify what timerange of data to use. --data-format-ohlcv {json,jsongz,hdf5} Storage format for downloaded candle (OHLCV) data. - (default: `None`). + (default: `json`). --max-open-trades INT Override the value of the `max_open_trades` configuration setting. @@ -114,7 +114,8 @@ optional arguments: Hyperopt-loss-functions are: ShortTradeDurHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss, SharpeHyperOptLossDaily, - SortinoHyperOptLoss, SortinoHyperOptLossDaily + SortinoHyperOptLoss, SortinoHyperOptLossDaily, + MaxDrawDownHyperOptLoss --disable-param-export Disable automatic hyperopt parameter export. @@ -512,12 +513,13 @@ This class should be in its own file within the `user_data/hyperopts/` directory Currently, the following loss functions are builtin: -* `ShortTradeDurHyperOptLoss` (default legacy Freqtrade hyperoptimization loss function) - Mostly for short trade duration and avoiding losses. -* `OnlyProfitHyperOptLoss` (which takes only amount of profit into consideration) -* `SharpeHyperOptLoss` (optimizes Sharpe Ratio calculated on trade returns relative to standard deviation) -* `SharpeHyperOptLossDaily` (optimizes Sharpe Ratio calculated on **daily** trade returns relative to standard deviation) -* `SortinoHyperOptLoss` (optimizes Sortino Ratio calculated on trade returns relative to **downside** standard deviation) -* `SortinoHyperOptLossDaily` (optimizes Sortino Ratio calculated on **daily** trade returns relative to **downside** standard deviation) +* `ShortTradeDurHyperOptLoss` - (default legacy Freqtrade hyperoptimization loss function) - Mostly for short trade duration and avoiding losses. +* `OnlyProfitHyperOptLoss` - takes only amount of profit into consideration. +* `SharpeHyperOptLoss` - optimizes Sharpe Ratio calculated on trade returns relative to standard deviation. +* `SharpeHyperOptLossDaily` - optimizes Sharpe Ratio calculated on **daily** trade returns relative to standard deviation. +* `SortinoHyperOptLoss` - optimizes Sortino Ratio calculated on trade returns relative to **downside** standard deviation. +* `SortinoHyperOptLossDaily` - optimizes Sortino Ratio calculated on **daily** trade returns relative to **downside** standard deviation. +* `MaxDrawDownHyperOptLoss` - Optimizes Maximum drawdown. Creation of a custom loss function is covered in the [Advanced Hyperopt](advanced-hyperopt.md) part of the documentation. diff --git a/freqtrade/constants.py b/freqtrade/constants.py index fca319a0f..c6b8f0e62 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -24,7 +24,8 @@ ORDERTYPE_POSSIBILITIES = ['limit', 'market'] ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc'] HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss', 'SharpeHyperOptLoss', 'SharpeHyperOptLossDaily', - 'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily'] + 'SortinoHyperOptLoss', 'SortinoHyperOptLossDaily', + 'MaxDrawDownHyperOptLoss'] AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'AgeFilter', 'OffsetFilter', 'PerformanceFilter', 'PrecisionFilter', 'PriceFilter', 'RangeStabilityFilter', diff --git a/tests/optimize/test_hyperoptloss.py b/tests/optimize/test_hyperoptloss.py index 923e3fc32..a39190934 100644 --- a/tests/optimize/test_hyperoptloss.py +++ b/tests/optimize/test_hyperoptloss.py @@ -84,13 +84,14 @@ def test_loss_calculation_has_limited_profit(hyperopt_conf, hyperopt_results) -> "SortinoHyperOptLossDaily", "SharpeHyperOptLoss", "SharpeHyperOptLossDaily", + "MaxDrawDownHyperOptLoss", ]) def test_loss_functions_better_profits(default_conf, hyperopt_results, lossfunction) -> None: results_over = hyperopt_results.copy() - results_over['profit_abs'] = hyperopt_results['profit_abs'] * 2 + results_over['profit_abs'] = hyperopt_results['profit_abs'] * 2 + 0.2 results_over['profit_ratio'] = hyperopt_results['profit_ratio'] * 2 results_under = hyperopt_results.copy() - results_under['profit_abs'] = hyperopt_results['profit_abs'] / 2 + results_under['profit_abs'] = hyperopt_results['profit_abs'] / 2 - 0.2 results_under['profit_ratio'] = hyperopt_results['profit_ratio'] / 2 default_conf.update({'hyperopt_loss': lossfunction}) From 30bc96cf3f802a882e4e5e35e0c0df8db876acfc Mon Sep 17 00:00:00 2001 From: sid Date: Sat, 9 Oct 2021 06:36:23 +0530 Subject: [PATCH 5/5] simplify expression --- freqtrade/optimize/hyperopt_loss_max_drawdown.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/freqtrade/optimize/hyperopt_loss_max_drawdown.py b/freqtrade/optimize/hyperopt_loss_max_drawdown.py index 6777fb2e8..ce955d928 100644 --- a/freqtrade/optimize/hyperopt_loss_max_drawdown.py +++ b/freqtrade/optimize/hyperopt_loss_max_drawdown.py @@ -38,6 +38,4 @@ class MaxDrawDownHyperOptLoss(IHyperOptLoss): except ValueError: # No losing trade, therefore no drawdown. return -total_profit - max_drawdown_rev = 1 / max_drawdown[0] - ret = max_drawdown_rev * total_profit - return -ret + return -total_profit / max_drawdown[0]