Add trailing stoploss hyperspace
This commit is contained in:
@@ -149,7 +149,7 @@ class Hyperopt:
|
||||
self.trials_file.unlink()
|
||||
return trials
|
||||
|
||||
def log_trials_result(self) -> None:
|
||||
def log_trials_result(self) -> None: # noqa: C901
|
||||
"""
|
||||
Display Best hyperopt result
|
||||
"""
|
||||
@@ -161,14 +161,16 @@ class Hyperopt:
|
||||
|
||||
if self.config.get('print_json'):
|
||||
result_dict: Dict = {}
|
||||
|
||||
if self.has_space('buy') or self.has_space('sell'):
|
||||
result_dict['params'] = {}
|
||||
|
||||
if self.has_space('buy'):
|
||||
result_dict['params'].update({p.name: params.get(p.name)
|
||||
for p in self.hyperopt_space('buy')})
|
||||
result_dict['params'].update(self.space_params(params, 'buy'))
|
||||
|
||||
if self.has_space('sell'):
|
||||
result_dict['params'].update({p.name: params.get(p.name)
|
||||
for p in self.hyperopt_space('sell')})
|
||||
result_dict['params'].update(self.space_params(params, 'sell'))
|
||||
|
||||
if self.has_space('roi'):
|
||||
# Convert keys in min_roi dict to strings because
|
||||
# rapidjson cannot dump dicts with integer keys...
|
||||
@@ -177,25 +179,35 @@ class Hyperopt:
|
||||
result_dict['minimal_roi'] = OrderedDict(
|
||||
(str(k), v) for k, v in self.custom_hyperopt.generate_roi_table(params).items()
|
||||
)
|
||||
|
||||
if self.has_space('stoploss'):
|
||||
result_dict['stoploss'] = params.get('stoploss')
|
||||
result_dict.update(self.space_params(params, 'stoploss'))
|
||||
|
||||
if self.has_space('trailing'):
|
||||
result_dict.update(self.space_params(params, 'trailing'))
|
||||
|
||||
print(rapidjson.dumps(result_dict, default=str, number_mode=rapidjson.NM_NATIVE))
|
||||
else:
|
||||
if self.has_space('buy'):
|
||||
print('Buy hyperspace params:')
|
||||
pprint({p.name: params.get(p.name) for p in self.hyperopt_space('buy')},
|
||||
indent=4)
|
||||
pprint(self.space_params(params, 'buy', 5), indent=4)
|
||||
|
||||
if self.has_space('sell'):
|
||||
print('Sell hyperspace params:')
|
||||
pprint({p.name: params.get(p.name) for p in self.hyperopt_space('sell')},
|
||||
indent=4)
|
||||
pprint(self.space_params(params, 'sell', 5), indent=4)
|
||||
|
||||
if self.has_space('roi'):
|
||||
print("ROI table:")
|
||||
# Round printed values to 5 digits after the decimal point
|
||||
pprint(round_dict(self.custom_hyperopt.generate_roi_table(params), 5), indent=4)
|
||||
|
||||
if self.has_space('stoploss'):
|
||||
# Also round to 5 digits after the decimal point
|
||||
print(f"Stoploss: {round(params.get('stoploss'), 5)}")
|
||||
print(f"Stoploss:")
|
||||
pprint(self.space_params(params, 'stoploss', 5), indent=4)
|
||||
|
||||
if self.has_space('trailing'):
|
||||
print('Trailing stop:')
|
||||
pprint(self.space_params(params, 'trailing', 5), indent=4)
|
||||
|
||||
def log_results(self, results) -> None:
|
||||
"""
|
||||
@@ -233,9 +245,13 @@ class Hyperopt:
|
||||
|
||||
def has_space(self, space: str) -> bool:
|
||||
"""
|
||||
Tell if a space value is contained in the configuration
|
||||
Tell if the space value is contained in the configuration
|
||||
"""
|
||||
return any(s in self.config['spaces'] for s in [space, 'all'])
|
||||
# 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]:
|
||||
"""
|
||||
@@ -245,20 +261,34 @@ class Hyperopt:
|
||||
for all hyperspaces used.
|
||||
"""
|
||||
spaces: List[Dimension] = []
|
||||
|
||||
if space == 'buy' or (space is None and self.has_space('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')):
|
||||
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')):
|
||||
logger.debug("Hyperopt has 'roi' space")
|
||||
spaces += self.custom_hyperopt.roi_space()
|
||||
|
||||
if space == 'stoploss' or (space is None and self.has_space('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')):
|
||||
logger.debug("Hyperopt has 'trailing' space")
|
||||
spaces += self.custom_hyperopt.trailing_space()
|
||||
|
||||
return spaces
|
||||
|
||||
def space_params(self, params, space: str, r: int = None) -> Dict:
|
||||
d = {p.name: params.get(p.name) for p in self.hyperopt_space(space)}
|
||||
# Round floats to `r` digits after the decimal point if requested
|
||||
return round_dict(d, r) if r else d
|
||||
|
||||
def generate_optimizer(self, _params: Dict, iteration=None) -> Dict:
|
||||
"""
|
||||
Used Optimize function. Called once per epoch to optimize whatever is configured.
|
||||
@@ -281,6 +311,15 @@ class Hyperopt:
|
||||
if self.has_space('stoploss'):
|
||||
self.backtesting.strategy.stoploss = params['stoploss']
|
||||
|
||||
if self.has_space('trailing'):
|
||||
self.backtesting.strategy.trailing_stop = params['trailing_stop']
|
||||
self.backtesting.strategy.trailing_stop_positive = \
|
||||
params['trailing_stop_positive']
|
||||
self.backtesting.strategy.trailing_stop_positive_offset = \
|
||||
params['trailing_stop_positive_offset']
|
||||
self.backtesting.strategy.trailing_only_offset_is_reached = \
|
||||
params['trailing_only_offset_is_reached']
|
||||
|
||||
processed = load(self.tickerdata_pickle)
|
||||
|
||||
min_date, max_date = get_timeframe(processed)
|
||||
|
@@ -8,7 +8,7 @@ import math
|
||||
from abc import ABC
|
||||
from typing import Dict, Any, Callable, List
|
||||
|
||||
from skopt.space import Dimension, Integer, Real
|
||||
from skopt.space import Categorical, Dimension, Integer, Real
|
||||
|
||||
from freqtrade import OperationalException
|
||||
from freqtrade.exchange import timeframe_to_minutes
|
||||
@@ -174,6 +174,20 @@ class IHyperOpt(ABC):
|
||||
Real(-0.35, -0.02, name='stoploss'),
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def trailing_space() -> List[Dimension]:
|
||||
"""
|
||||
Create a trailing stoploss space.
|
||||
|
||||
You may override it in your custom Hyperopt class.
|
||||
"""
|
||||
return [
|
||||
Categorical([True, False], name='trailing_stop'),
|
||||
Real(-0.35, -0.02, name='trailing_stop_positive'),
|
||||
Real(0.01, 0.1, name='trailing_stop_positive_offset'),
|
||||
Categorical([True, False], name='trailing_only_offset_is_reached'),
|
||||
]
|
||||
|
||||
# This is needed for proper unpickling the class attribute ticker_interval
|
||||
# which is set to the actual value by the resolver.
|
||||
# Why do I still need such shamanic mantras in modern python?
|
||||
|
Reference in New Issue
Block a user