2021-03-23 08:02:32 +00:00
|
|
|
"""
|
|
|
|
HyperOptAuto class.
|
2021-03-24 08:32:34 +00:00
|
|
|
This module implements a convenience auto-hyperopt class, which can be used together with strategies
|
|
|
|
that implement IHyperStrategy interface.
|
2021-03-23 08:02:32 +00:00
|
|
|
"""
|
2024-05-12 15:16:02 +00:00
|
|
|
|
2021-10-13 17:54:35 +00:00
|
|
|
import logging
|
2021-03-25 08:00:52 +00:00
|
|
|
from contextlib import suppress
|
2021-09-11 15:11:02 +00:00
|
|
|
from typing import Callable, Dict, List
|
2021-03-25 08:00:52 +00:00
|
|
|
|
2021-09-11 15:11:02 +00:00
|
|
|
from freqtrade.exceptions import OperationalException
|
2021-03-27 09:40:48 +00:00
|
|
|
|
|
|
|
|
2021-03-25 08:00:52 +00:00
|
|
|
with suppress(ImportError):
|
|
|
|
from skopt.space import Dimension
|
2021-03-23 08:02:32 +00:00
|
|
|
|
2021-09-15 19:36:53 +00:00
|
|
|
from freqtrade.optimize.hyperopt_interface import EstimatorType, IHyperOpt
|
2021-03-23 08:02:32 +00:00
|
|
|
|
|
|
|
|
2021-10-13 17:54:35 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
def _format_exception_message(space: str, ignore_missing_space: bool) -> None:
|
2024-05-12 15:16:02 +00:00
|
|
|
msg = (
|
|
|
|
f"The '{space}' space is included into the hyperoptimization "
|
|
|
|
f"but no parameter for this space was found in your Strategy. "
|
|
|
|
)
|
2021-10-13 17:54:35 +00:00
|
|
|
if ignore_missing_space:
|
|
|
|
logger.warning(msg + "This space will be ignored.")
|
|
|
|
else:
|
|
|
|
raise OperationalException(
|
|
|
|
msg + f"Please make sure to have parameters for this space enabled for optimization "
|
2024-05-12 15:16:02 +00:00
|
|
|
f"or remove the '{space}' space from hyperoptimization."
|
|
|
|
)
|
2021-09-11 15:11:02 +00:00
|
|
|
|
|
|
|
|
2021-03-23 08:02:32 +00:00
|
|
|
class HyperOptAuto(IHyperOpt):
|
|
|
|
"""
|
2021-03-24 08:32:34 +00:00
|
|
|
This class delegates functionality to Strategy(IHyperStrategy) and Strategy.HyperOpt classes.
|
|
|
|
Most of the time Strategy.HyperOpt class would only implement indicator_space and
|
|
|
|
sell_indicator_space methods, but other hyperopt methods can be overridden as well.
|
2021-03-23 08:02:32 +00:00
|
|
|
"""
|
2021-03-24 08:32:34 +00:00
|
|
|
|
2021-03-23 08:02:32 +00:00
|
|
|
def _get_func(self, name) -> Callable:
|
|
|
|
"""
|
|
|
|
Return a function defined in Strategy.HyperOpt class, or one defined in super() class.
|
|
|
|
:param name: function name.
|
|
|
|
:return: a requested function.
|
|
|
|
"""
|
2024-05-12 15:16:02 +00:00
|
|
|
hyperopt_cls = getattr(self.strategy, "HyperOpt", None)
|
2021-03-23 08:02:32 +00:00
|
|
|
default_func = getattr(super(), name)
|
|
|
|
if hyperopt_cls:
|
|
|
|
return getattr(hyperopt_cls, name, default_func)
|
|
|
|
else:
|
|
|
|
return default_func
|
|
|
|
|
|
|
|
def _generate_indicator_space(self, category):
|
|
|
|
for attr_name, attr in self.strategy.enumerate_parameters(category):
|
2021-03-31 09:31:28 +00:00
|
|
|
if attr.optimize:
|
2021-03-26 12:25:49 +00:00
|
|
|
yield attr.get_space(attr_name)
|
2021-03-23 08:02:32 +00:00
|
|
|
|
2021-10-13 17:54:35 +00:00
|
|
|
def _get_indicator_space(self, category) -> List:
|
2021-09-11 07:06:57 +00:00
|
|
|
# TODO: is this necessary, or can we call "generate_space" directly?
|
2021-03-23 08:02:32 +00:00
|
|
|
indicator_space = list(self._generate_indicator_space(category))
|
|
|
|
if len(indicator_space) > 0:
|
|
|
|
return indicator_space
|
|
|
|
else:
|
2021-10-13 17:54:35 +00:00
|
|
|
_format_exception_message(
|
2024-05-12 15:16:02 +00:00
|
|
|
category, self.config.get("hyperopt_ignore_missing_space", False)
|
|
|
|
)
|
2021-10-13 17:54:35 +00:00
|
|
|
return []
|
2021-03-23 08:02:32 +00:00
|
|
|
|
2024-05-12 15:16:02 +00:00
|
|
|
def buy_indicator_space(self) -> List["Dimension"]:
|
|
|
|
return self._get_indicator_space("buy")
|
2021-03-23 08:02:32 +00:00
|
|
|
|
2024-05-12 15:16:02 +00:00
|
|
|
def sell_indicator_space(self) -> List["Dimension"]:
|
|
|
|
return self._get_indicator_space("sell")
|
2021-03-23 08:02:32 +00:00
|
|
|
|
2024-05-12 15:16:02 +00:00
|
|
|
def protection_space(self) -> List["Dimension"]:
|
|
|
|
return self._get_indicator_space("protection")
|
2021-08-03 05:10:04 +00:00
|
|
|
|
2021-03-23 08:02:32 +00:00
|
|
|
def generate_roi_table(self, params: Dict) -> Dict[int, float]:
|
2024-05-12 15:16:02 +00:00
|
|
|
return self._get_func("generate_roi_table")(params)
|
2021-03-23 08:02:32 +00:00
|
|
|
|
2024-05-12 15:16:02 +00:00
|
|
|
def roi_space(self) -> List["Dimension"]:
|
|
|
|
return self._get_func("roi_space")()
|
2021-03-23 08:02:32 +00:00
|
|
|
|
2024-05-12 15:16:02 +00:00
|
|
|
def stoploss_space(self) -> List["Dimension"]:
|
|
|
|
return self._get_func("stoploss_space")()
|
2021-03-23 08:02:32 +00:00
|
|
|
|
|
|
|
def generate_trailing_params(self, params: Dict) -> Dict:
|
2024-05-12 15:16:02 +00:00
|
|
|
return self._get_func("generate_trailing_params")(params)
|
2021-03-23 08:02:32 +00:00
|
|
|
|
2024-05-12 15:16:02 +00:00
|
|
|
def trailing_space(self) -> List["Dimension"]:
|
|
|
|
return self._get_func("trailing_space")()
|
2021-09-15 19:36:53 +00:00
|
|
|
|
2024-05-12 15:16:02 +00:00
|
|
|
def max_open_trades_space(self) -> List["Dimension"]:
|
|
|
|
return self._get_func("max_open_trades_space")()
|
2023-01-04 09:34:44 +00:00
|
|
|
|
2024-05-12 15:16:02 +00:00
|
|
|
def generate_estimator(self, dimensions: List["Dimension"], **kwargs) -> EstimatorType:
|
|
|
|
return self._get_func("generate_estimator")(dimensions=dimensions, **kwargs)
|