Merge pull request #4596 from rokups/rk/hyper-strategy
Support for creating auto-hyperoptable strategies.
This commit is contained in:
@@ -26,6 +26,7 @@ from freqtrade.data.history import get_timerange
|
||||
from freqtrade.misc import file_dump_json, plural
|
||||
from freqtrade.optimize.backtesting import Backtesting
|
||||
# Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules
|
||||
from freqtrade.optimize.hyperopt_auto import HyperOptAuto
|
||||
from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F401
|
||||
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F401
|
||||
from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
||||
@@ -61,14 +62,18 @@ class Hyperopt:
|
||||
hyperopt = Hyperopt(config)
|
||||
hyperopt.start()
|
||||
"""
|
||||
custom_hyperopt: IHyperOpt
|
||||
|
||||
def __init__(self, config: Dict[str, Any]) -> None:
|
||||
self.config = config
|
||||
|
||||
self.backtesting = Backtesting(self.config)
|
||||
|
||||
self.custom_hyperopt = HyperOptResolver.load_hyperopt(self.config)
|
||||
self.custom_hyperopt.__class__.strategy = self.backtesting.strategy
|
||||
if not self.config.get('hyperopt'):
|
||||
self.custom_hyperopt = HyperOptAuto(self.config)
|
||||
else:
|
||||
self.custom_hyperopt = HyperOptResolver.load_hyperopt(self.config)
|
||||
self.custom_hyperopt.strategy = self.backtesting.strategy
|
||||
|
||||
self.custom_hyperoptloss = HyperOptLossResolver.load_hyperoptloss(self.config)
|
||||
self.calculate_loss = self.custom_hyperoptloss.hyperopt_loss_function
|
||||
|
89
freqtrade/optimize/hyperopt_auto.py
Normal file
89
freqtrade/optimize/hyperopt_auto.py
Normal file
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
HyperOptAuto class.
|
||||
This module implements a convenience auto-hyperopt class, which can be used together with strategies
|
||||
that implement IHyperStrategy interface.
|
||||
"""
|
||||
from contextlib import suppress
|
||||
from typing import Any, Callable, Dict, List
|
||||
|
||||
from pandas import DataFrame
|
||||
|
||||
|
||||
with suppress(ImportError):
|
||||
from skopt.space import Dimension
|
||||
|
||||
from freqtrade.optimize.hyperopt_interface import IHyperOpt
|
||||
|
||||
|
||||
class HyperOptAuto(IHyperOpt):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
def buy_strategy_generator(self, params: Dict[str, Any]) -> Callable:
|
||||
def populate_buy_trend(dataframe: DataFrame, metadata: dict):
|
||||
for attr_name, attr in self.strategy.enumerate_parameters('buy'):
|
||||
if attr.optimize:
|
||||
# noinspection PyProtectedMember
|
||||
attr._set_value(params[attr_name])
|
||||
return self.strategy.populate_buy_trend(dataframe, metadata)
|
||||
|
||||
return populate_buy_trend
|
||||
|
||||
def sell_strategy_generator(self, params: Dict[str, Any]) -> Callable:
|
||||
def populate_buy_trend(dataframe: DataFrame, metadata: dict):
|
||||
for attr_name, attr in self.strategy.enumerate_parameters('sell'):
|
||||
if attr.optimize:
|
||||
# noinspection PyProtectedMember
|
||||
attr._set_value(params[attr_name])
|
||||
return self.strategy.populate_sell_trend(dataframe, metadata)
|
||||
|
||||
return populate_buy_trend
|
||||
|
||||
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.
|
||||
"""
|
||||
hyperopt_cls = getattr(self.strategy, 'HyperOpt', None)
|
||||
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):
|
||||
if attr.optimize:
|
||||
yield attr.get_space(attr_name)
|
||||
|
||||
def _get_indicator_space(self, category, fallback_method_name):
|
||||
indicator_space = list(self._generate_indicator_space(category))
|
||||
if len(indicator_space) > 0:
|
||||
return indicator_space
|
||||
else:
|
||||
return self._get_func(fallback_method_name)()
|
||||
|
||||
def indicator_space(self) -> List['Dimension']:
|
||||
return self._get_indicator_space('buy', 'indicator_space')
|
||||
|
||||
def sell_indicator_space(self) -> List['Dimension']:
|
||||
return self._get_indicator_space('sell', 'sell_indicator_space')
|
||||
|
||||
def generate_roi_table(self, params: Dict) -> Dict[int, float]:
|
||||
return self._get_func('generate_roi_table')(params)
|
||||
|
||||
def roi_space(self) -> List['Dimension']:
|
||||
return self._get_func('roi_space')()
|
||||
|
||||
def stoploss_space(self) -> List['Dimension']:
|
||||
return self._get_func('stoploss_space')()
|
||||
|
||||
def generate_trailing_params(self, params: Dict) -> Dict:
|
||||
return self._get_func('generate_trailing_params')(params)
|
||||
|
||||
def trailing_space(self) -> List['Dimension']:
|
||||
return self._get_func('trailing_space')()
|
@@ -44,36 +44,31 @@ class IHyperOpt(ABC):
|
||||
IHyperOpt.ticker_interval = str(config['timeframe']) # DEPRECATED
|
||||
IHyperOpt.timeframe = str(config['timeframe'])
|
||||
|
||||
@staticmethod
|
||||
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
|
||||
def buy_strategy_generator(self, params: Dict[str, Any]) -> Callable:
|
||||
"""
|
||||
Create a buy strategy generator.
|
||||
"""
|
||||
raise OperationalException(_format_exception_message('buy_strategy_generator', 'buy'))
|
||||
|
||||
@staticmethod
|
||||
def sell_strategy_generator(params: Dict[str, Any]) -> Callable:
|
||||
def sell_strategy_generator(self, params: Dict[str, Any]) -> Callable:
|
||||
"""
|
||||
Create a sell strategy generator.
|
||||
"""
|
||||
raise OperationalException(_format_exception_message('sell_strategy_generator', 'sell'))
|
||||
|
||||
@staticmethod
|
||||
def indicator_space() -> List[Dimension]:
|
||||
def indicator_space(self) -> List[Dimension]:
|
||||
"""
|
||||
Create an indicator space.
|
||||
"""
|
||||
raise OperationalException(_format_exception_message('indicator_space', 'buy'))
|
||||
|
||||
@staticmethod
|
||||
def sell_indicator_space() -> List[Dimension]:
|
||||
def sell_indicator_space(self) -> List[Dimension]:
|
||||
"""
|
||||
Create a sell indicator space.
|
||||
"""
|
||||
raise OperationalException(_format_exception_message('sell_indicator_space', 'sell'))
|
||||
|
||||
@staticmethod
|
||||
def generate_roi_table(params: Dict) -> Dict[int, float]:
|
||||
def generate_roi_table(self, params: Dict) -> Dict[int, float]:
|
||||
"""
|
||||
Create a ROI table.
|
||||
|
||||
@@ -88,8 +83,7 @@ class IHyperOpt(ABC):
|
||||
|
||||
return roi_table
|
||||
|
||||
@staticmethod
|
||||
def roi_space() -> List[Dimension]:
|
||||
def roi_space(self) -> List[Dimension]:
|
||||
"""
|
||||
Create a ROI space.
|
||||
|
||||
@@ -109,7 +103,7 @@ class IHyperOpt(ABC):
|
||||
roi_t_alpha = 1.0
|
||||
roi_p_alpha = 1.0
|
||||
|
||||
timeframe_min = timeframe_to_minutes(IHyperOpt.ticker_interval)
|
||||
timeframe_min = timeframe_to_minutes(self.timeframe)
|
||||
|
||||
# We define here limits for the ROI space parameters automagically adapted to the
|
||||
# timeframe used by the bot:
|
||||
@@ -145,7 +139,7 @@ class IHyperOpt(ABC):
|
||||
'roi_p2': roi_limits['roi_p2_min'],
|
||||
'roi_p3': roi_limits['roi_p3_min'],
|
||||
}
|
||||
logger.info(f"Min roi table: {round_dict(IHyperOpt.generate_roi_table(p), 5)}")
|
||||
logger.info(f"Min roi table: {round_dict(self.generate_roi_table(p), 5)}")
|
||||
p = {
|
||||
'roi_t1': roi_limits['roi_t1_max'],
|
||||
'roi_t2': roi_limits['roi_t2_max'],
|
||||
@@ -154,7 +148,7 @@ class IHyperOpt(ABC):
|
||||
'roi_p2': roi_limits['roi_p2_max'],
|
||||
'roi_p3': roi_limits['roi_p3_max'],
|
||||
}
|
||||
logger.info(f"Max roi table: {round_dict(IHyperOpt.generate_roi_table(p), 5)}")
|
||||
logger.info(f"Max roi table: {round_dict(self.generate_roi_table(p), 5)}")
|
||||
|
||||
return [
|
||||
Integer(roi_limits['roi_t1_min'], roi_limits['roi_t1_max'], name='roi_t1'),
|
||||
@@ -165,8 +159,7 @@ class IHyperOpt(ABC):
|
||||
Real(roi_limits['roi_p3_min'], roi_limits['roi_p3_max'], name='roi_p3'),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def stoploss_space() -> List[Dimension]:
|
||||
def stoploss_space(self) -> List[Dimension]:
|
||||
"""
|
||||
Create a stoploss space.
|
||||
|
||||
@@ -177,8 +170,7 @@ class IHyperOpt(ABC):
|
||||
Real(-0.35, -0.02, name='stoploss'),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def generate_trailing_params(params: Dict) -> Dict:
|
||||
def generate_trailing_params(self, params: Dict) -> Dict:
|
||||
"""
|
||||
Create dict with trailing stop parameters.
|
||||
"""
|
||||
@@ -190,8 +182,7 @@ class IHyperOpt(ABC):
|
||||
'trailing_only_offset_is_reached': params['trailing_only_offset_is_reached'],
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def trailing_space() -> List[Dimension]:
|
||||
def trailing_space(self) -> List[Dimension]:
|
||||
"""
|
||||
Create a trailing stoploss space.
|
||||
|
||||
|
Reference in New Issue
Block a user