Merge pull request #4844 from freqtrade/improve_hyper
IntParameter should not calculate indicators if it's not being optimized
This commit is contained in:
commit
3c9042c825
@ -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']
|
||||||
|
@ -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:
|
||||||
"""
|
"""
|
||||||
|
@ -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]
|
||||||
|
@ -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)
|
||||||
|
@ -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]
|
||||||
|
Loading…
Reference in New Issue
Block a user