diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index e52cb8faf..ceed704c2 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -183,8 +183,7 @@ class Hyperopt: result['stoploss'] = {p.name: params.get(p.name) for p in self.hyperopt_space('stoploss')} if self.has_space('trailing'): - result['trailing'] = {p.name: params.get(p.name) - for p in self.hyperopt_space('trailing')} + result['trailing'] = self.custom_hyperopt.generate_trailing_params(params) return result @@ -359,13 +358,13 @@ class Hyperopt: self.backtesting.strategy.stoploss = params_dict['stoploss'] if self.has_space('trailing'): - self.backtesting.strategy.trailing_stop = params_dict['trailing_stop'] - self.backtesting.strategy.trailing_stop_positive = \ - params_dict['trailing_stop_positive'] + d = self.custom_hyperopt.generate_trailing_params(params_dict) + self.backtesting.strategy.trailing_stop = d['trailing_stop'] + self.backtesting.strategy.trailing_stop_positive = d['trailing_stop_positive'] self.backtesting.strategy.trailing_stop_positive_offset = \ - params_dict['trailing_stop_positive_offset'] + d['trailing_stop_positive_offset'] self.backtesting.strategy.trailing_only_offset_is_reached = \ - params_dict['trailing_only_offset_is_reached'] + d['trailing_only_offset_is_reached'] processed = load(self.tickerdata_pickle) diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index 958fb8d66..dbbdb8b91 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -174,6 +174,19 @@ class IHyperOpt(ABC): Real(-0.35, -0.02, name='stoploss'), ] + @staticmethod + def generate_trailing_params(params: Dict) -> Dict: + """ + Create dict with trailing stop parameters. + """ + return { + 'trailing_stop': params['trailing_stop'], + 'trailing_stop_positive': params['trailing_stop_positive'], + 'trailing_stop_positive_offset': (params['trailing_stop_positive'] + + params['trailing_stop_positive_offset_p1']), + 'trailing_only_offset_is_reached': params['trailing_only_offset_is_reached'], + } + @staticmethod def trailing_space() -> List[Dimension]: """ @@ -190,8 +203,15 @@ class IHyperOpt(ABC): # other 'trailing' hyperspace parameters. Categorical([True], name='trailing_stop'), - Real(0.02, 0.35, name='trailing_stop_positive'), - Real(0.01, 0.1, name='trailing_stop_positive_offset'), + Real(0.01, 0.35, name='trailing_stop_positive'), + + # 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive', + # so this intermediate parameter is used as the value of the difference between + # them. The value of the 'trailing_stop_positive_offset' is constructed in the + # generate_trailing_params() method. + # # This is similar to the hyperspace dimensions used for constructing the ROI tables. + Real(0.001, 0.1, name='trailing_stop_positive_offset_p1'), + Categorical([True, False], name='trailing_only_offset_is_reached'), ] diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 6a8daab8b..60b445323 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -636,7 +636,7 @@ def test_generate_optimizer(mocker, default_conf) -> None: 'stoploss': -0.4, 'trailing_stop': True, 'trailing_stop_positive': 0.02, - 'trailing_stop_positive_offset': 0.1, + 'trailing_stop_positive_offset_p1': 0.05, 'trailing_only_offset_is_reached': False, } response_expected = { @@ -670,7 +670,7 @@ def test_generate_optimizer(mocker, default_conf) -> None: 'trailing': {'trailing_only_offset_is_reached': False, 'trailing_stop': True, 'trailing_stop_positive': 0.02, - 'trailing_stop_positive_offset': 0.1}}, + 'trailing_stop_positive_offset': 0.07}}, 'params_dict': optimizer_param, 'results_metrics': {'avg_profit': 2.3117, 'duration': 100.0,