diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index d1dabff36..361480329 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -114,7 +114,7 @@ class Hyperopt: self.max_open_trades = 0 self.position_stacking = self.config.get('position_stacking', False) - if self.has_space('sell'): + if HyperoptTools.has_space(self.config, 'sell'): # Make sure use_sell_signal is enabled if 'ask_strategy' not in self.config: self.config['ask_strategy'] = {} @@ -175,18 +175,18 @@ class Hyperopt: """ result: Dict = {} - if self.has_space('buy'): + if HyperoptTools.has_space(self.config, 'buy'): result['buy'] = {p.name: params.get(p.name) for p in self.hyperopt_space('buy')} - if self.has_space('sell'): + if HyperoptTools.has_space(self.config, 'sell'): result['sell'] = {p.name: params.get(p.name) for p in self.hyperopt_space('sell')} - if self.has_space('roi'): + if HyperoptTools.has_space(self.config, 'roi'): result['roi'] = self.custom_hyperopt.generate_roi_table(params) - if self.has_space('stoploss'): + if HyperoptTools.has_space(self.config, 'stoploss'): result['stoploss'] = {p.name: params.get(p.name) for p in self.hyperopt_space('stoploss')} - if self.has_space('trailing'): + if HyperoptTools.has_space(self.config, 'trailing'): result['trailing'] = self.custom_hyperopt.generate_trailing_params(params) return result @@ -208,16 +208,6 @@ class Hyperopt: ) self.hyperopt_table_header = 2 - def has_space(self, space: str) -> bool: - """ - Tell if the space value is contained in the configuration - """ - # The 'trailing' space is not included in the 'default' set of spaces - if space == 'trailing': - return any(s in self.config['spaces'] for s in [space, 'all']) - else: - return any(s in self.config['spaces'] for s in [space, 'all', 'default']) - def hyperopt_space(self, space: Optional[str] = None) -> List[Dimension]: """ Return the dimensions in the hyperoptimization space. @@ -227,23 +217,25 @@ class Hyperopt: """ spaces: List[Dimension] = [] - if space == 'buy' or (space is None and self.has_space('buy')): + if space == 'buy' or (space is None and HyperoptTools.has_space(self.config, 'buy')): logger.debug("Hyperopt has 'buy' space") spaces += self.custom_hyperopt.indicator_space() - if space == 'sell' or (space is None and self.has_space('sell')): + if space == 'sell' or (space is None and HyperoptTools.has_space(self.config, 'sell')): logger.debug("Hyperopt has 'sell' space") spaces += self.custom_hyperopt.sell_indicator_space() - if space == 'roi' or (space is None and self.has_space('roi')): + if space == 'roi' or (space is None and HyperoptTools.has_space(self.config, 'roi')): logger.debug("Hyperopt has 'roi' space") spaces += self.custom_hyperopt.roi_space() - if space == 'stoploss' or (space is None and self.has_space('stoploss')): + if space == 'stoploss' or (space is None + and HyperoptTools.has_space(self.config, 'stoploss')): logger.debug("Hyperopt has 'stoploss' space") spaces += self.custom_hyperopt.stoploss_space() - if space == 'trailing' or (space is None and self.has_space('trailing')): + if space == 'trailing' or (space is None + and HyperoptTools.has_space(self.config, 'trailing')): logger.debug("Hyperopt has 'trailing' space") spaces += self.custom_hyperopt.trailing_space() @@ -257,22 +249,22 @@ class Hyperopt: params_dict = self._get_params_dict(raw_params) params_details = self._get_params_details(params_dict) - if self.has_space('roi'): + if HyperoptTools.has_space(self.config, 'roi'): self.backtesting.strategy.minimal_roi = ( # type: ignore self.custom_hyperopt.generate_roi_table(params_dict)) - if self.has_space('buy'): + if HyperoptTools.has_space(self.config, 'buy'): self.backtesting.strategy.advise_buy = ( # type: ignore self.custom_hyperopt.buy_strategy_generator(params_dict)) - if self.has_space('sell'): + if HyperoptTools.has_space(self.config, 'sell'): self.backtesting.strategy.advise_sell = ( # type: ignore self.custom_hyperopt.sell_strategy_generator(params_dict)) - if self.has_space('stoploss'): + if HyperoptTools.has_space(self.config, 'stoploss'): self.backtesting.strategy.stoploss = params_dict['stoploss'] - if self.has_space('trailing'): + if HyperoptTools.has_space(self.config, 'trailing'): 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'] diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index d4c347f80..c39e0b943 100644 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -4,7 +4,7 @@ import logging from collections import OrderedDict from pathlib import Path from pprint import pformat -from typing import Dict, List +from typing import Any, Dict, List import rapidjson import tabulate @@ -21,6 +21,17 @@ logger = logging.getLogger(__name__) class HyperoptTools(): + @staticmethod + def has_space(config: Dict[str, Any], space: str) -> bool: + """ + Tell if the space value is contained in the configuration + """ + # The 'trailing' space is not included in the 'default' set of spaces + if space == 'trailing': + return any(s in config['spaces'] for s in [space, 'all']) + else: + return any(s in config['spaces'] for s in [space, 'all', 'default']) + @staticmethod def _read_results(results_file: Path) -> List: """ diff --git a/freqtrade/strategy/hyper.py b/freqtrade/strategy/hyper.py index 32486136d..2714ffb43 100644 --- a/freqtrade/strategy/hyper.py +++ b/freqtrade/strategy/hyper.py @@ -7,6 +7,8 @@ from abc import ABC, abstractmethod from contextlib import suppress from typing import Any, Dict, Iterator, Optional, Sequence, Tuple, Union +from freqtrade.optimize.hyperopt_tools import HyperoptTools + with suppress(ImportError): from skopt.space import Integer, Real, Categorical @@ -26,7 +28,7 @@ class BaseParameter(ABC): category: Optional[str] default: Any value: Any - hyperopt: bool = False + in_space: bool = False def __init__(self, *, default: Any, space: Optional[str] = None, optimize: bool = True, load: bool = True, **kwargs): @@ -131,7 +133,7 @@ class IntParameter(NumericParameter): Returns a List with 1 item (`value`) in "non-hyperopt" mode, to avoid calculating 100ds of indicators. """ - if self.hyperopt: + if self.in_space and self.optimize: # Scikit-optimize ranges are "inclusive", while python's "range" is exclusive return range(self.low, self.high + 1) else: @@ -247,6 +249,7 @@ class HyperStrategyMixin(object): """ Initialize hyperoptable strategy mixin. """ + self.config = config self._load_hyper_params(config.get('runmode') == RunMode.HYPEROPT) def enumerate_parameters(self, category: str = None) -> Iterator[Tuple[str, BaseParameter]]: @@ -284,8 +287,8 @@ class HyperStrategyMixin(object): if not params: logger.info(f"No params for {space} found, using default values.") - for attr_name, attr in self.enumerate_parameters(): - attr.hyperopt = hyperopt + for attr_name, attr in self.enumerate_parameters(space): + attr.in_space = hyperopt and HyperoptTools.has_space(self.config, space) if params and attr_name in params: if attr.load: attr.value = params[attr_name] diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 90ff8a1d0..5a7f0f32e 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -503,10 +503,10 @@ def test_format_results(hyperopt): (['default', 'buy'], {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False}), ]) -def test_has_space(hyperopt, spaces, expected_results): +def test_has_space(hyperopt_conf, spaces, expected_results): for s in ['buy', 'sell', 'roi', 'stoploss', 'trailing']: - hyperopt.config.update({'spaces': spaces}) - assert hyperopt.has_space(s) == expected_results[s] + hyperopt_conf.update({'spaces': spaces}) + assert HyperoptTools.has_space(hyperopt_conf, s) == expected_results[s] def test_populate_indicators(hyperopt, testdatadir) -> None: @@ -1108,7 +1108,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None: assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto) assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter) - assert hyperopt.backtesting.strategy.buy_rsi.hyperopt is True + assert hyperopt.backtesting.strategy.buy_rsi.in_space is True assert hyperopt.backtesting.strategy.buy_rsi.value == 35 buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range assert isinstance(buy_rsi_range, range) diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index bd81bc80c..4ca514349 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -636,7 +636,7 @@ def test_hyperopt_parameters(): assert len(list(intpar.range)) == 1 # Range contains ONLY the default / value. assert list(intpar.range) == [intpar.value] - intpar.hyperopt = True + intpar.in_space = True assert len(list(intpar.range)) == 6 assert list(intpar.range) == [0, 1, 2, 3, 4, 5]