Merge pull request #4844 from freqtrade/improve_hyper

IntParameter should not calculate indicators if it's not being optimized
This commit is contained in:
Matthias 2021-05-01 17:36:29 +02:00 committed by GitHub
commit 3c9042c825
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 42 additions and 36 deletions

View File

@ -114,7 +114,7 @@ class Hyperopt:
self.max_open_trades = 0 self.max_open_trades = 0
self.position_stacking = self.config.get('position_stacking', False) 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 # Make sure use_sell_signal is enabled
if 'ask_strategy' not in self.config: if 'ask_strategy' not in self.config:
self.config['ask_strategy'] = {} self.config['ask_strategy'] = {}
@ -175,18 +175,18 @@ class Hyperopt:
""" """
result: Dict = {} result: Dict = {}
if self.has_space('buy'): if HyperoptTools.has_space(self.config, 'buy'):
result['buy'] = {p.name: params.get(p.name) result['buy'] = {p.name: params.get(p.name)
for p in self.hyperopt_space('buy')} 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) result['sell'] = {p.name: params.get(p.name)
for p in self.hyperopt_space('sell')} 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) 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) result['stoploss'] = {p.name: params.get(p.name)
for p in self.hyperopt_space('stoploss')} 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) result['trailing'] = self.custom_hyperopt.generate_trailing_params(params)
return result return result
@ -208,16 +208,6 @@ class Hyperopt:
) )
self.hyperopt_table_header = 2 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]: def hyperopt_space(self, space: Optional[str] = None) -> List[Dimension]:
""" """
Return the dimensions in the hyperoptimization space. Return the dimensions in the hyperoptimization space.
@ -227,23 +217,25 @@ class Hyperopt:
""" """
spaces: List[Dimension] = [] 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") logger.debug("Hyperopt has 'buy' space")
spaces += self.custom_hyperopt.indicator_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") logger.debug("Hyperopt has 'sell' space")
spaces += self.custom_hyperopt.sell_indicator_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") logger.debug("Hyperopt has 'roi' space")
spaces += self.custom_hyperopt.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") logger.debug("Hyperopt has 'stoploss' space")
spaces += self.custom_hyperopt.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") logger.debug("Hyperopt has 'trailing' space")
spaces += self.custom_hyperopt.trailing_space() spaces += self.custom_hyperopt.trailing_space()
@ -257,22 +249,22 @@ class Hyperopt:
params_dict = self._get_params_dict(raw_params) params_dict = self._get_params_dict(raw_params)
params_details = self._get_params_details(params_dict) 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.backtesting.strategy.minimal_roi = ( # type: ignore
self.custom_hyperopt.generate_roi_table(params_dict)) 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.backtesting.strategy.advise_buy = ( # type: ignore
self.custom_hyperopt.buy_strategy_generator(params_dict)) 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.backtesting.strategy.advise_sell = ( # type: ignore
self.custom_hyperopt.sell_strategy_generator(params_dict)) 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'] 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) d = self.custom_hyperopt.generate_trailing_params(params_dict)
self.backtesting.strategy.trailing_stop = d['trailing_stop'] self.backtesting.strategy.trailing_stop = d['trailing_stop']
self.backtesting.strategy.trailing_stop_positive = d['trailing_stop_positive'] self.backtesting.strategy.trailing_stop_positive = d['trailing_stop_positive']

View File

@ -4,7 +4,7 @@ import logging
from collections import OrderedDict from collections import OrderedDict
from pathlib import Path from pathlib import Path
from pprint import pformat from pprint import pformat
from typing import Dict, List from typing import Any, Dict, List
import rapidjson import rapidjson
import tabulate import tabulate
@ -21,6 +21,17 @@ logger = logging.getLogger(__name__)
class HyperoptTools(): 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 @staticmethod
def _read_results(results_file: Path) -> List: def _read_results(results_file: Path) -> List:
""" """

View File

@ -7,6 +7,8 @@ from abc import ABC, abstractmethod
from contextlib import suppress from contextlib import suppress
from typing import Any, Dict, Iterator, Optional, Sequence, Tuple, Union from typing import Any, Dict, Iterator, Optional, Sequence, Tuple, Union
from freqtrade.optimize.hyperopt_tools import HyperoptTools
with suppress(ImportError): with suppress(ImportError):
from skopt.space import Integer, Real, Categorical from skopt.space import Integer, Real, Categorical
@ -26,7 +28,7 @@ class BaseParameter(ABC):
category: Optional[str] category: Optional[str]
default: Any default: Any
value: Any value: Any
hyperopt: bool = False in_space: bool = False
def __init__(self, *, default: Any, space: Optional[str] = None, def __init__(self, *, default: Any, space: Optional[str] = None,
optimize: bool = True, load: bool = True, **kwargs): 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 Returns a List with 1 item (`value`) in "non-hyperopt" mode, to avoid
calculating 100ds of indicators. 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 # Scikit-optimize ranges are "inclusive", while python's "range" is exclusive
return range(self.low, self.high + 1) return range(self.low, self.high + 1)
else: else:
@ -247,6 +249,7 @@ class HyperStrategyMixin(object):
""" """
Initialize hyperoptable strategy mixin. Initialize hyperoptable strategy mixin.
""" """
self.config = config
self._load_hyper_params(config.get('runmode') == RunMode.HYPEROPT) self._load_hyper_params(config.get('runmode') == RunMode.HYPEROPT)
def enumerate_parameters(self, category: str = None) -> Iterator[Tuple[str, BaseParameter]]: def enumerate_parameters(self, category: str = None) -> Iterator[Tuple[str, BaseParameter]]:
@ -284,8 +287,8 @@ class HyperStrategyMixin(object):
if not params: if not params:
logger.info(f"No params for {space} found, using default values.") logger.info(f"No params for {space} found, using default values.")
for attr_name, attr in self.enumerate_parameters(): for attr_name, attr in self.enumerate_parameters(space):
attr.hyperopt = hyperopt attr.in_space = hyperopt and HyperoptTools.has_space(self.config, space)
if params and attr_name in params: if params and attr_name in params:
if attr.load: if attr.load:
attr.value = params[attr_name] attr.value = params[attr_name]

View File

@ -503,10 +503,10 @@ def test_format_results(hyperopt):
(['default', 'buy'], (['default', 'buy'],
{'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False}), {'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']: for s in ['buy', 'sell', 'roi', 'stoploss', 'trailing']:
hyperopt.config.update({'spaces': spaces}) hyperopt_conf.update({'spaces': spaces})
assert hyperopt.has_space(s) == expected_results[s] assert HyperoptTools.has_space(hyperopt_conf, s) == expected_results[s]
def test_populate_indicators(hyperopt, testdatadir) -> None: 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.custom_hyperopt, HyperOptAuto)
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter) 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 assert hyperopt.backtesting.strategy.buy_rsi.value == 35
buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range
assert isinstance(buy_rsi_range, range) assert isinstance(buy_rsi_range, range)

View File

@ -636,7 +636,7 @@ def test_hyperopt_parameters():
assert len(list(intpar.range)) == 1 assert len(list(intpar.range)) == 1
# Range contains ONLY the default / value. # Range contains ONLY the default / value.
assert list(intpar.range) == [intpar.value] assert list(intpar.range) == [intpar.value]
intpar.hyperopt = True intpar.in_space = True
assert len(list(intpar.range)) == 6 assert len(list(intpar.range)) == 6
assert list(intpar.range) == [0, 1, 2, 3, 4, 5] assert list(intpar.range) == [0, 1, 2, 3, 4, 5]