Add trailing stoploss hyperspace

This commit is contained in:
hroff-1902
2019-11-08 01:55:14 +03:00
parent ad2289c34c
commit f90676cfc5
4 changed files with 110 additions and 34 deletions

View File

@@ -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)

View File

@@ -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?