Remove legacy hyperopt interface from hyperopt.py
This commit is contained in:
parent
ebb0b8aa3f
commit
dad4a49e81
@ -209,7 +209,7 @@ AVAILABLE_CLI_OPTIONS = {
|
|||||||
),
|
),
|
||||||
"hyperopt_path": Arg(
|
"hyperopt_path": Arg(
|
||||||
'--hyperopt-path',
|
'--hyperopt-path',
|
||||||
help='Specify additional lookup path for Hyperopt and Hyperopt Loss functions.',
|
help='Specify additional lookup path for Hyperopt Loss functions.',
|
||||||
metavar='PATH',
|
metavar='PATH',
|
||||||
),
|
),
|
||||||
"epochs": Arg(
|
"epochs": Arg(
|
||||||
|
@ -22,6 +22,7 @@ from pandas import DataFrame
|
|||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN
|
from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN
|
||||||
from freqtrade.data.converter import trim_dataframes
|
from freqtrade.data.converter import trim_dataframes
|
||||||
from freqtrade.data.history import get_timerange
|
from freqtrade.data.history import get_timerange
|
||||||
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.misc import deep_merge_dicts, file_dump_json, plural
|
from freqtrade.misc import deep_merge_dicts, file_dump_json, plural
|
||||||
from freqtrade.optimize.backtesting import Backtesting
|
from freqtrade.optimize.backtesting import Backtesting
|
||||||
# Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules
|
# Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules
|
||||||
@ -30,7 +31,7 @@ from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F401
|
|||||||
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F401
|
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F401
|
||||||
from freqtrade.optimize.hyperopt_tools import HyperoptTools, hyperopt_serializer
|
from freqtrade.optimize.hyperopt_tools import HyperoptTools, hyperopt_serializer
|
||||||
from freqtrade.optimize.optimize_reports import generate_strategy_stats
|
from freqtrade.optimize.optimize_reports import generate_strategy_stats
|
||||||
from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver, HyperOptResolver
|
from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver
|
||||||
|
|
||||||
|
|
||||||
# Suppress scikit-learn FutureWarnings from skopt
|
# Suppress scikit-learn FutureWarnings from skopt
|
||||||
@ -80,8 +81,9 @@ class Hyperopt:
|
|||||||
self.custom_hyperopt = HyperOptAuto(self.config)
|
self.custom_hyperopt = HyperOptAuto(self.config)
|
||||||
self.auto_hyperopt = True
|
self.auto_hyperopt = True
|
||||||
else:
|
else:
|
||||||
self.custom_hyperopt = HyperOptResolver.load_hyperopt(self.config)
|
raise OperationalException(
|
||||||
self.auto_hyperopt = False
|
"Using seperate Hyperopt files has been removed in 2021.9. Please convert "
|
||||||
|
"your existing Hyperopt file to the new Hyperoptable strategy interface")
|
||||||
|
|
||||||
self.backtesting._set_strategy(self.backtesting.strategylist[0])
|
self.backtesting._set_strategy(self.backtesting.strategylist[0])
|
||||||
self.custom_hyperopt.strategy = self.backtesting.strategy
|
self.custom_hyperopt.strategy = self.backtesting.strategy
|
||||||
@ -103,31 +105,6 @@ class Hyperopt:
|
|||||||
self.num_epochs_saved = 0
|
self.num_epochs_saved = 0
|
||||||
self.current_best_epoch: Optional[Dict[str, Any]] = None
|
self.current_best_epoch: Optional[Dict[str, Any]] = None
|
||||||
|
|
||||||
if not self.auto_hyperopt:
|
|
||||||
# Populate "fallback" functions here
|
|
||||||
# (hasattr is slow so should not be run during "regular" operations)
|
|
||||||
if hasattr(self.custom_hyperopt, 'populate_indicators'):
|
|
||||||
logger.warning(
|
|
||||||
"DEPRECATED: Using `populate_indicators()` in the hyperopt file is deprecated. "
|
|
||||||
"Please move these methods to your strategy."
|
|
||||||
)
|
|
||||||
self.backtesting.strategy.populate_indicators = ( # type: ignore
|
|
||||||
self.custom_hyperopt.populate_indicators) # type: ignore
|
|
||||||
if hasattr(self.custom_hyperopt, 'populate_buy_trend'):
|
|
||||||
logger.warning(
|
|
||||||
"DEPRECATED: Using `populate_buy_trend()` in the hyperopt file is deprecated. "
|
|
||||||
"Please move these methods to your strategy."
|
|
||||||
)
|
|
||||||
self.backtesting.strategy.populate_buy_trend = ( # type: ignore
|
|
||||||
self.custom_hyperopt.populate_buy_trend) # type: ignore
|
|
||||||
if hasattr(self.custom_hyperopt, 'populate_sell_trend'):
|
|
||||||
logger.warning(
|
|
||||||
"DEPRECATED: Using `populate_sell_trend()` in the hyperopt file is deprecated. "
|
|
||||||
"Please move these methods to your strategy."
|
|
||||||
)
|
|
||||||
self.backtesting.strategy.populate_sell_trend = ( # type: ignore
|
|
||||||
self.custom_hyperopt.populate_sell_trend) # type: ignore
|
|
||||||
|
|
||||||
# Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set
|
# Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set
|
||||||
if self.config.get('use_max_market_positions', True):
|
if self.config.get('use_max_market_positions', True):
|
||||||
self.max_open_trades = self.config['max_open_trades']
|
self.max_open_trades = self.config['max_open_trades']
|
||||||
@ -256,7 +233,7 @@ class Hyperopt:
|
|||||||
"""
|
"""
|
||||||
Assign the dimensions in the hyperoptimization space.
|
Assign the dimensions in the hyperoptimization space.
|
||||||
"""
|
"""
|
||||||
if self.auto_hyperopt and HyperoptTools.has_space(self.config, 'protection'):
|
if HyperoptTools.has_space(self.config, 'protection'):
|
||||||
# Protections can only be optimized when using the Parameter interface
|
# Protections can only be optimized when using the Parameter interface
|
||||||
logger.debug("Hyperopt has 'protection' space")
|
logger.debug("Hyperopt has 'protection' space")
|
||||||
# Enable Protections if protection space is selected.
|
# Enable Protections if protection space is selected.
|
||||||
@ -285,6 +262,15 @@ class Hyperopt:
|
|||||||
self.dimensions = (self.buy_space + self.sell_space + self.protection_space
|
self.dimensions = (self.buy_space + self.sell_space + self.protection_space
|
||||||
+ self.roi_space + self.stoploss_space + self.trailing_space)
|
+ self.roi_space + self.stoploss_space + self.trailing_space)
|
||||||
|
|
||||||
|
def assign_params(self, params_dict: Dict, category: str) -> None:
|
||||||
|
"""
|
||||||
|
Assign hyperoptable parameters
|
||||||
|
"""
|
||||||
|
for attr_name, attr in self.backtesting.strategy.enumerate_parameters(category):
|
||||||
|
if attr.optimize:
|
||||||
|
# noinspection PyProtectedMember
|
||||||
|
attr.value = params_dict[attr_name]
|
||||||
|
|
||||||
def generate_optimizer(self, raw_params: List[Any], iteration=None) -> Dict:
|
def generate_optimizer(self, raw_params: List[Any], iteration=None) -> Dict:
|
||||||
"""
|
"""
|
||||||
Used Optimize function.
|
Used Optimize function.
|
||||||
@ -296,18 +282,13 @@ class Hyperopt:
|
|||||||
|
|
||||||
# Apply parameters
|
# Apply parameters
|
||||||
if HyperoptTools.has_space(self.config, 'buy'):
|
if HyperoptTools.has_space(self.config, 'buy'):
|
||||||
self.backtesting.strategy.advise_buy = ( # type: ignore
|
self.assign_params(params_dict, 'buy')
|
||||||
self.custom_hyperopt.buy_strategy_generator(params_dict))
|
|
||||||
|
|
||||||
if HyperoptTools.has_space(self.config, 'sell'):
|
if HyperoptTools.has_space(self.config, 'sell'):
|
||||||
self.backtesting.strategy.advise_sell = ( # type: ignore
|
self.assign_params(params_dict, 'sell')
|
||||||
self.custom_hyperopt.sell_strategy_generator(params_dict))
|
|
||||||
|
|
||||||
if HyperoptTools.has_space(self.config, 'protection'):
|
if HyperoptTools.has_space(self.config, 'protection'):
|
||||||
for attr_name, attr in self.backtesting.strategy.enumerate_parameters('protection'):
|
self.assign_params(params_dict, 'protection')
|
||||||
if attr.optimize:
|
|
||||||
# noinspection PyProtectedMember
|
|
||||||
attr.value = params_dict[attr_name]
|
|
||||||
|
|
||||||
if HyperoptTools.has_space(self.config, 'roi'):
|
if HyperoptTools.has_space(self.config, 'roi'):
|
||||||
self.backtesting.strategy.minimal_roi = ( # type: ignore
|
self.backtesting.strategy.minimal_roi = ( # type: ignore
|
||||||
@ -517,11 +498,10 @@ class Hyperopt:
|
|||||||
f"saved to '{self.results_file}'.")
|
f"saved to '{self.results_file}'.")
|
||||||
|
|
||||||
if self.current_best_epoch:
|
if self.current_best_epoch:
|
||||||
if self.auto_hyperopt:
|
HyperoptTools.try_export_params(
|
||||||
HyperoptTools.try_export_params(
|
self.config,
|
||||||
self.config,
|
self.backtesting.strategy.get_strategy_name(),
|
||||||
self.backtesting.strategy.get_strategy_name(),
|
self.current_best_epoch)
|
||||||
self.current_best_epoch)
|
|
||||||
|
|
||||||
HyperoptTools.show_epoch_details(self.current_best_epoch, self.total_epochs,
|
HyperoptTools.show_epoch_details(self.current_best_epoch, self.total_epochs,
|
||||||
self.print_json)
|
self.print_json)
|
||||||
|
@ -22,26 +22,6 @@ class HyperOptAuto(IHyperOpt):
|
|||||||
sell_indicator_space methods, but other hyperopt methods can be overridden as well.
|
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.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_sell_trend(dataframe: DataFrame, metadata: dict):
|
|
||||||
for attr_name, attr in self.strategy.enumerate_parameters('sell'):
|
|
||||||
if attr.optimize:
|
|
||||||
# noinspection PyProtectedMember
|
|
||||||
attr.value = params[attr_name]
|
|
||||||
return self.strategy.populate_sell_trend(dataframe, metadata)
|
|
||||||
|
|
||||||
return populate_sell_trend
|
|
||||||
|
|
||||||
def _get_func(self, name) -> Callable:
|
def _get_func(self, name) -> Callable:
|
||||||
"""
|
"""
|
||||||
Return a function defined in Strategy.HyperOpt class, or one defined in super() class.
|
Return a function defined in Strategy.HyperOpt class, or one defined in super() class.
|
||||||
@ -61,6 +41,7 @@ class HyperOptAuto(IHyperOpt):
|
|||||||
yield attr.get_space(attr_name)
|
yield attr.get_space(attr_name)
|
||||||
|
|
||||||
def _get_indicator_space(self, category, fallback_method_name):
|
def _get_indicator_space(self, category, fallback_method_name):
|
||||||
|
# TODO: is this necessary, or can we call "generate_space" directly?
|
||||||
indicator_space = list(self._generate_indicator_space(category))
|
indicator_space = list(self._generate_indicator_space(category))
|
||||||
if len(indicator_space) > 0:
|
if len(indicator_space) > 0:
|
||||||
return indicator_space
|
return indicator_space
|
||||||
|
@ -5,7 +5,7 @@ This module defines the interface to apply for hyperopt
|
|||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
from typing import Any, Callable, Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
from skopt.space import Categorical, Dimension, Integer
|
from skopt.space import Categorical, Dimension, Integer
|
||||||
|
|
||||||
@ -45,18 +45,6 @@ class IHyperOpt(ABC):
|
|||||||
IHyperOpt.ticker_interval = str(config['timeframe']) # DEPRECATED
|
IHyperOpt.ticker_interval = str(config['timeframe']) # DEPRECATED
|
||||||
IHyperOpt.timeframe = str(config['timeframe'])
|
IHyperOpt.timeframe = str(config['timeframe'])
|
||||||
|
|
||||||
def buy_strategy_generator(self, params: Dict[str, Any]) -> Callable:
|
|
||||||
"""
|
|
||||||
Create a buy strategy generator.
|
|
||||||
"""
|
|
||||||
raise OperationalException(_format_exception_message('buy_strategy_generator', 'buy'))
|
|
||||||
|
|
||||||
def sell_strategy_generator(self, params: Dict[str, Any]) -> Callable:
|
|
||||||
"""
|
|
||||||
Create a sell strategy generator.
|
|
||||||
"""
|
|
||||||
raise OperationalException(_format_exception_message('sell_strategy_generator', 'sell'))
|
|
||||||
|
|
||||||
def protection_space(self) -> List[Dimension]:
|
def protection_space(self) -> List[Dimension]:
|
||||||
"""
|
"""
|
||||||
Create a protection space.
|
Create a protection space.
|
||||||
|
@ -9,7 +9,6 @@ from typing import Dict
|
|||||||
|
|
||||||
from freqtrade.constants import HYPEROPT_LOSS_BUILTIN, USERPATH_HYPEROPTS
|
from freqtrade.constants import HYPEROPT_LOSS_BUILTIN, USERPATH_HYPEROPTS
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.optimize.hyperopt_interface import IHyperOpt
|
|
||||||
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss
|
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss
|
||||||
from freqtrade.resolvers import IResolver
|
from freqtrade.resolvers import IResolver
|
||||||
|
|
||||||
@ -17,43 +16,6 @@ from freqtrade.resolvers import IResolver
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class HyperOptResolver(IResolver):
|
|
||||||
"""
|
|
||||||
This class contains all the logic to load custom hyperopt class
|
|
||||||
"""
|
|
||||||
object_type = IHyperOpt
|
|
||||||
object_type_str = "Hyperopt"
|
|
||||||
user_subdir = USERPATH_HYPEROPTS
|
|
||||||
initial_search_path = None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def load_hyperopt(config: Dict) -> IHyperOpt:
|
|
||||||
"""
|
|
||||||
Load the custom hyperopt class from config parameter
|
|
||||||
:param config: configuration dictionary
|
|
||||||
"""
|
|
||||||
if not config.get('hyperopt'):
|
|
||||||
raise OperationalException("No Hyperopt set. Please use `--hyperopt` to specify "
|
|
||||||
"the Hyperopt class to use.")
|
|
||||||
|
|
||||||
hyperopt_name = config['hyperopt']
|
|
||||||
|
|
||||||
hyperopt = HyperOptResolver.load_object(hyperopt_name, config,
|
|
||||||
kwargs={'config': config},
|
|
||||||
extra_dir=config.get('hyperopt_path'))
|
|
||||||
|
|
||||||
if not hasattr(hyperopt, 'populate_indicators'):
|
|
||||||
logger.info("Hyperopt class does not provide populate_indicators() method. "
|
|
||||||
"Using populate_indicators from the strategy.")
|
|
||||||
if not hasattr(hyperopt, 'populate_buy_trend'):
|
|
||||||
logger.info("Hyperopt class does not provide populate_buy_trend() method. "
|
|
||||||
"Using populate_buy_trend from the strategy.")
|
|
||||||
if not hasattr(hyperopt, 'populate_sell_trend'):
|
|
||||||
logger.info("Hyperopt class does not provide populate_sell_trend() method. "
|
|
||||||
"Using populate_sell_trend from the strategy.")
|
|
||||||
return hyperopt
|
|
||||||
|
|
||||||
|
|
||||||
class HyperOptLossResolver(IResolver):
|
class HyperOptLossResolver(IResolver):
|
||||||
"""
|
"""
|
||||||
This class contains all the logic to load custom hyperopt loss class
|
This class contains all the logic to load custom hyperopt loss class
|
||||||
|
@ -1,174 +0,0 @@
|
|||||||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
|
||||||
# isort: skip_file
|
|
||||||
|
|
||||||
# --- Do not remove these libs ---
|
|
||||||
from functools import reduce
|
|
||||||
from typing import Any, Callable, Dict, List
|
|
||||||
|
|
||||||
import numpy as np # noqa
|
|
||||||
import pandas as pd # noqa
|
|
||||||
from pandas import DataFrame
|
|
||||||
from skopt.space import Categorical, Dimension, Integer, Real # noqa
|
|
||||||
|
|
||||||
from freqtrade.optimize.hyperopt_interface import IHyperOpt
|
|
||||||
|
|
||||||
# --------------------------------
|
|
||||||
# Add your lib to import here
|
|
||||||
import talib.abstract as ta # noqa
|
|
||||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
|
||||||
|
|
||||||
|
|
||||||
class SampleHyperOpt(IHyperOpt):
|
|
||||||
"""
|
|
||||||
This is a sample Hyperopt to inspire you.
|
|
||||||
|
|
||||||
More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/
|
|
||||||
|
|
||||||
You should:
|
|
||||||
- Rename the class name to some unique name.
|
|
||||||
- Add any methods you want to build your hyperopt.
|
|
||||||
- Add any lib you need to build your hyperopt.
|
|
||||||
|
|
||||||
An easier way to get a new hyperopt file is by using
|
|
||||||
`freqtrade new-hyperopt --hyperopt MyCoolHyperopt`.
|
|
||||||
|
|
||||||
You must keep:
|
|
||||||
- The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator.
|
|
||||||
|
|
||||||
The methods roi_space, generate_roi_table and stoploss_space are not required
|
|
||||||
and are provided by default.
|
|
||||||
However, you may override them if you need 'roi' and 'stoploss' spaces that
|
|
||||||
differ from the defaults offered by Freqtrade.
|
|
||||||
Sample implementation of these methods will be copied to `user_data/hyperopts` when
|
|
||||||
creating the user-data directory using `freqtrade create-userdir --userdir user_data`,
|
|
||||||
or is available online under the following URL:
|
|
||||||
https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def indicator_space() -> List[Dimension]:
|
|
||||||
"""
|
|
||||||
Define your Hyperopt space for searching buy strategy parameters.
|
|
||||||
"""
|
|
||||||
return [
|
|
||||||
Integer(10, 25, name='mfi-value'),
|
|
||||||
Integer(15, 45, name='fastd-value'),
|
|
||||||
Integer(20, 50, name='adx-value'),
|
|
||||||
Integer(20, 40, name='rsi-value'),
|
|
||||||
Categorical([True, False], name='mfi-enabled'),
|
|
||||||
Categorical([True, False], name='fastd-enabled'),
|
|
||||||
Categorical([True, False], name='adx-enabled'),
|
|
||||||
Categorical([True, False], name='rsi-enabled'),
|
|
||||||
Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger')
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
|
|
||||||
"""
|
|
||||||
Define the buy strategy parameters to be used by Hyperopt.
|
|
||||||
"""
|
|
||||||
def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
||||||
"""
|
|
||||||
Buy strategy Hyperopt will build and use.
|
|
||||||
"""
|
|
||||||
conditions = []
|
|
||||||
|
|
||||||
# GUARDS AND TRENDS
|
|
||||||
if 'mfi-enabled' in params and params['mfi-enabled']:
|
|
||||||
conditions.append(dataframe['mfi'] < params['mfi-value'])
|
|
||||||
if 'fastd-enabled' in params and params['fastd-enabled']:
|
|
||||||
conditions.append(dataframe['fastd'] < params['fastd-value'])
|
|
||||||
if 'adx-enabled' in params and params['adx-enabled']:
|
|
||||||
conditions.append(dataframe['adx'] > params['adx-value'])
|
|
||||||
if 'rsi-enabled' in params and params['rsi-enabled']:
|
|
||||||
conditions.append(dataframe['rsi'] < params['rsi-value'])
|
|
||||||
|
|
||||||
# TRIGGERS
|
|
||||||
if 'trigger' in params:
|
|
||||||
if params['trigger'] == 'bb_lower':
|
|
||||||
conditions.append(dataframe['close'] < dataframe['bb_lowerband'])
|
|
||||||
if params['trigger'] == 'macd_cross_signal':
|
|
||||||
conditions.append(qtpylib.crossed_above(
|
|
||||||
dataframe['macd'], dataframe['macdsignal']
|
|
||||||
))
|
|
||||||
if params['trigger'] == 'sar_reversal':
|
|
||||||
conditions.append(qtpylib.crossed_above(
|
|
||||||
dataframe['close'], dataframe['sar']
|
|
||||||
))
|
|
||||||
|
|
||||||
# Check that volume is not 0
|
|
||||||
conditions.append(dataframe['volume'] > 0)
|
|
||||||
|
|
||||||
if conditions:
|
|
||||||
dataframe.loc[
|
|
||||||
reduce(lambda x, y: x & y, conditions),
|
|
||||||
'buy'] = 1
|
|
||||||
|
|
||||||
return dataframe
|
|
||||||
|
|
||||||
return populate_buy_trend
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def sell_indicator_space() -> List[Dimension]:
|
|
||||||
"""
|
|
||||||
Define your Hyperopt space for searching sell strategy parameters.
|
|
||||||
"""
|
|
||||||
return [
|
|
||||||
Integer(75, 100, name='sell-mfi-value'),
|
|
||||||
Integer(50, 100, name='sell-fastd-value'),
|
|
||||||
Integer(50, 100, name='sell-adx-value'),
|
|
||||||
Integer(60, 100, name='sell-rsi-value'),
|
|
||||||
Categorical([True, False], name='sell-mfi-enabled'),
|
|
||||||
Categorical([True, False], name='sell-fastd-enabled'),
|
|
||||||
Categorical([True, False], name='sell-adx-enabled'),
|
|
||||||
Categorical([True, False], name='sell-rsi-enabled'),
|
|
||||||
Categorical(['sell-bb_upper',
|
|
||||||
'sell-macd_cross_signal',
|
|
||||||
'sell-sar_reversal'], name='sell-trigger')
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def sell_strategy_generator(params: Dict[str, Any]) -> Callable:
|
|
||||||
"""
|
|
||||||
Define the sell strategy parameters to be used by Hyperopt.
|
|
||||||
"""
|
|
||||||
def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
||||||
"""
|
|
||||||
Sell strategy Hyperopt will build and use.
|
|
||||||
"""
|
|
||||||
conditions = []
|
|
||||||
|
|
||||||
# GUARDS AND TRENDS
|
|
||||||
if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']:
|
|
||||||
conditions.append(dataframe['mfi'] > params['sell-mfi-value'])
|
|
||||||
if 'sell-fastd-enabled' in params and params['sell-fastd-enabled']:
|
|
||||||
conditions.append(dataframe['fastd'] > params['sell-fastd-value'])
|
|
||||||
if 'sell-adx-enabled' in params and params['sell-adx-enabled']:
|
|
||||||
conditions.append(dataframe['adx'] < params['sell-adx-value'])
|
|
||||||
if 'sell-rsi-enabled' in params and params['sell-rsi-enabled']:
|
|
||||||
conditions.append(dataframe['rsi'] > params['sell-rsi-value'])
|
|
||||||
|
|
||||||
# TRIGGERS
|
|
||||||
if 'sell-trigger' in params:
|
|
||||||
if params['sell-trigger'] == 'sell-bb_upper':
|
|
||||||
conditions.append(dataframe['close'] > dataframe['bb_upperband'])
|
|
||||||
if params['sell-trigger'] == 'sell-macd_cross_signal':
|
|
||||||
conditions.append(qtpylib.crossed_above(
|
|
||||||
dataframe['macdsignal'], dataframe['macd']
|
|
||||||
))
|
|
||||||
if params['sell-trigger'] == 'sell-sar_reversal':
|
|
||||||
conditions.append(qtpylib.crossed_above(
|
|
||||||
dataframe['sar'], dataframe['close']
|
|
||||||
))
|
|
||||||
|
|
||||||
# Check that volume is not 0
|
|
||||||
conditions.append(dataframe['volume'] > 0)
|
|
||||||
|
|
||||||
if conditions:
|
|
||||||
dataframe.loc[
|
|
||||||
reduce(lambda x, y: x & y, conditions),
|
|
||||||
'sell'] = 1
|
|
||||||
|
|
||||||
return dataframe
|
|
||||||
|
|
||||||
return populate_sell_trend
|
|
@ -1,269 +0,0 @@
|
|||||||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
|
||||||
# isort: skip_file
|
|
||||||
# --- Do not remove these libs ---
|
|
||||||
from functools import reduce
|
|
||||||
from typing import Any, Callable, Dict, List
|
|
||||||
|
|
||||||
import numpy as np # noqa
|
|
||||||
import pandas as pd # noqa
|
|
||||||
from pandas import DataFrame
|
|
||||||
from freqtrade.optimize.space import Categorical, Dimension, Integer, SKDecimal, Real # noqa
|
|
||||||
|
|
||||||
from freqtrade.optimize.hyperopt_interface import IHyperOpt
|
|
||||||
|
|
||||||
# --------------------------------
|
|
||||||
# Add your lib to import here
|
|
||||||
import talib.abstract as ta # noqa
|
|
||||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
|
||||||
|
|
||||||
|
|
||||||
class AdvancedSampleHyperOpt(IHyperOpt):
|
|
||||||
"""
|
|
||||||
This is a sample hyperopt to inspire you.
|
|
||||||
Feel free to customize it.
|
|
||||||
|
|
||||||
More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/
|
|
||||||
|
|
||||||
You should:
|
|
||||||
- Rename the class name to some unique name.
|
|
||||||
- Add any methods you want to build your hyperopt.
|
|
||||||
- Add any lib you need to build your hyperopt.
|
|
||||||
|
|
||||||
You must keep:
|
|
||||||
- The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator.
|
|
||||||
|
|
||||||
The methods roi_space, generate_roi_table and stoploss_space are not required
|
|
||||||
and are provided by default.
|
|
||||||
However, you may override them if you need the
|
|
||||||
'roi' and the 'stoploss' spaces that differ from the defaults offered by Freqtrade.
|
|
||||||
|
|
||||||
This sample illustrates how to override these methods.
|
|
||||||
"""
|
|
||||||
@staticmethod
|
|
||||||
def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
||||||
"""
|
|
||||||
This method can also be loaded from the strategy, if it doesn't exist in the hyperopt class.
|
|
||||||
"""
|
|
||||||
dataframe['adx'] = ta.ADX(dataframe)
|
|
||||||
macd = ta.MACD(dataframe)
|
|
||||||
dataframe['macd'] = macd['macd']
|
|
||||||
dataframe['macdsignal'] = macd['macdsignal']
|
|
||||||
dataframe['mfi'] = ta.MFI(dataframe)
|
|
||||||
dataframe['rsi'] = ta.RSI(dataframe)
|
|
||||||
stoch_fast = ta.STOCHF(dataframe)
|
|
||||||
dataframe['fastd'] = stoch_fast['fastd']
|
|
||||||
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
|
||||||
# Bollinger bands
|
|
||||||
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
|
|
||||||
dataframe['bb_lowerband'] = bollinger['lower']
|
|
||||||
dataframe['bb_upperband'] = bollinger['upper']
|
|
||||||
dataframe['sar'] = ta.SAR(dataframe)
|
|
||||||
return dataframe
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def indicator_space() -> List[Dimension]:
|
|
||||||
"""
|
|
||||||
Define your Hyperopt space for searching buy strategy parameters.
|
|
||||||
"""
|
|
||||||
return [
|
|
||||||
Integer(10, 25, name='mfi-value'),
|
|
||||||
Integer(15, 45, name='fastd-value'),
|
|
||||||
Integer(20, 50, name='adx-value'),
|
|
||||||
Integer(20, 40, name='rsi-value'),
|
|
||||||
Categorical([True, False], name='mfi-enabled'),
|
|
||||||
Categorical([True, False], name='fastd-enabled'),
|
|
||||||
Categorical([True, False], name='adx-enabled'),
|
|
||||||
Categorical([True, False], name='rsi-enabled'),
|
|
||||||
Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger')
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
|
|
||||||
"""
|
|
||||||
Define the buy strategy parameters to be used by hyperopt
|
|
||||||
"""
|
|
||||||
def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
||||||
"""
|
|
||||||
Buy strategy Hyperopt will build and use
|
|
||||||
"""
|
|
||||||
conditions = []
|
|
||||||
# GUARDS AND TRENDS
|
|
||||||
if 'mfi-enabled' in params and params['mfi-enabled']:
|
|
||||||
conditions.append(dataframe['mfi'] < params['mfi-value'])
|
|
||||||
if 'fastd-enabled' in params and params['fastd-enabled']:
|
|
||||||
conditions.append(dataframe['fastd'] < params['fastd-value'])
|
|
||||||
if 'adx-enabled' in params and params['adx-enabled']:
|
|
||||||
conditions.append(dataframe['adx'] > params['adx-value'])
|
|
||||||
if 'rsi-enabled' in params and params['rsi-enabled']:
|
|
||||||
conditions.append(dataframe['rsi'] < params['rsi-value'])
|
|
||||||
|
|
||||||
# TRIGGERS
|
|
||||||
if 'trigger' in params:
|
|
||||||
if params['trigger'] == 'bb_lower':
|
|
||||||
conditions.append(dataframe['close'] < dataframe['bb_lowerband'])
|
|
||||||
if params['trigger'] == 'macd_cross_signal':
|
|
||||||
conditions.append(qtpylib.crossed_above(
|
|
||||||
dataframe['macd'], dataframe['macdsignal']
|
|
||||||
))
|
|
||||||
if params['trigger'] == 'sar_reversal':
|
|
||||||
conditions.append(qtpylib.crossed_above(
|
|
||||||
dataframe['close'], dataframe['sar']
|
|
||||||
))
|
|
||||||
|
|
||||||
# Check that volume is not 0
|
|
||||||
conditions.append(dataframe['volume'] > 0)
|
|
||||||
|
|
||||||
if conditions:
|
|
||||||
dataframe.loc[
|
|
||||||
reduce(lambda x, y: x & y, conditions),
|
|
||||||
'buy'] = 1
|
|
||||||
|
|
||||||
return dataframe
|
|
||||||
|
|
||||||
return populate_buy_trend
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def sell_indicator_space() -> List[Dimension]:
|
|
||||||
"""
|
|
||||||
Define your Hyperopt space for searching sell strategy parameters.
|
|
||||||
"""
|
|
||||||
return [
|
|
||||||
Integer(75, 100, name='sell-mfi-value'),
|
|
||||||
Integer(50, 100, name='sell-fastd-value'),
|
|
||||||
Integer(50, 100, name='sell-adx-value'),
|
|
||||||
Integer(60, 100, name='sell-rsi-value'),
|
|
||||||
Categorical([True, False], name='sell-mfi-enabled'),
|
|
||||||
Categorical([True, False], name='sell-fastd-enabled'),
|
|
||||||
Categorical([True, False], name='sell-adx-enabled'),
|
|
||||||
Categorical([True, False], name='sell-rsi-enabled'),
|
|
||||||
Categorical(['sell-bb_upper',
|
|
||||||
'sell-macd_cross_signal',
|
|
||||||
'sell-sar_reversal'], name='sell-trigger')
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def sell_strategy_generator(params: Dict[str, Any]) -> Callable:
|
|
||||||
"""
|
|
||||||
Define the sell strategy parameters to be used by hyperopt
|
|
||||||
"""
|
|
||||||
def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
||||||
"""
|
|
||||||
Sell strategy Hyperopt will build and use
|
|
||||||
"""
|
|
||||||
# print(params)
|
|
||||||
conditions = []
|
|
||||||
# GUARDS AND TRENDS
|
|
||||||
if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']:
|
|
||||||
conditions.append(dataframe['mfi'] > params['sell-mfi-value'])
|
|
||||||
if 'sell-fastd-enabled' in params and params['sell-fastd-enabled']:
|
|
||||||
conditions.append(dataframe['fastd'] > params['sell-fastd-value'])
|
|
||||||
if 'sell-adx-enabled' in params and params['sell-adx-enabled']:
|
|
||||||
conditions.append(dataframe['adx'] < params['sell-adx-value'])
|
|
||||||
if 'sell-rsi-enabled' in params and params['sell-rsi-enabled']:
|
|
||||||
conditions.append(dataframe['rsi'] > params['sell-rsi-value'])
|
|
||||||
|
|
||||||
# TRIGGERS
|
|
||||||
if 'sell-trigger' in params:
|
|
||||||
if params['sell-trigger'] == 'sell-bb_upper':
|
|
||||||
conditions.append(dataframe['close'] > dataframe['bb_upperband'])
|
|
||||||
if params['sell-trigger'] == 'sell-macd_cross_signal':
|
|
||||||
conditions.append(qtpylib.crossed_above(
|
|
||||||
dataframe['macdsignal'], dataframe['macd']
|
|
||||||
))
|
|
||||||
if params['sell-trigger'] == 'sell-sar_reversal':
|
|
||||||
conditions.append(qtpylib.crossed_above(
|
|
||||||
dataframe['sar'], dataframe['close']
|
|
||||||
))
|
|
||||||
|
|
||||||
# Check that volume is not 0
|
|
||||||
conditions.append(dataframe['volume'] > 0)
|
|
||||||
|
|
||||||
if conditions:
|
|
||||||
dataframe.loc[
|
|
||||||
reduce(lambda x, y: x & y, conditions),
|
|
||||||
'sell'] = 1
|
|
||||||
|
|
||||||
return dataframe
|
|
||||||
|
|
||||||
return populate_sell_trend
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def generate_roi_table(params: Dict) -> Dict[int, float]:
|
|
||||||
"""
|
|
||||||
Generate the ROI table that will be used by Hyperopt
|
|
||||||
|
|
||||||
This implementation generates the default legacy Freqtrade ROI tables.
|
|
||||||
|
|
||||||
Change it if you need different number of steps in the generated
|
|
||||||
ROI tables or other structure of the ROI tables.
|
|
||||||
|
|
||||||
Please keep it aligned with parameters in the 'roi' optimization
|
|
||||||
hyperspace defined by the roi_space method.
|
|
||||||
"""
|
|
||||||
roi_table = {}
|
|
||||||
roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3']
|
|
||||||
roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2']
|
|
||||||
roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1']
|
|
||||||
roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0
|
|
||||||
|
|
||||||
return roi_table
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def roi_space() -> List[Dimension]:
|
|
||||||
"""
|
|
||||||
Values to search for each ROI steps
|
|
||||||
|
|
||||||
Override it if you need some different ranges for the parameters in the
|
|
||||||
'roi' optimization hyperspace.
|
|
||||||
|
|
||||||
Please keep it aligned with the implementation of the
|
|
||||||
generate_roi_table method.
|
|
||||||
"""
|
|
||||||
return [
|
|
||||||
Integer(10, 120, name='roi_t1'),
|
|
||||||
Integer(10, 60, name='roi_t2'),
|
|
||||||
Integer(10, 40, name='roi_t3'),
|
|
||||||
SKDecimal(0.01, 0.04, decimals=3, name='roi_p1'),
|
|
||||||
SKDecimal(0.01, 0.07, decimals=3, name='roi_p2'),
|
|
||||||
SKDecimal(0.01, 0.20, decimals=3, name='roi_p3'),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def stoploss_space() -> List[Dimension]:
|
|
||||||
"""
|
|
||||||
Stoploss Value to search
|
|
||||||
|
|
||||||
Override it if you need some different range for the parameter in the
|
|
||||||
'stoploss' optimization hyperspace.
|
|
||||||
"""
|
|
||||||
return [
|
|
||||||
SKDecimal(-0.35, -0.02, decimals=3, name='stoploss'),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def trailing_space() -> List[Dimension]:
|
|
||||||
"""
|
|
||||||
Create a trailing stoploss space.
|
|
||||||
|
|
||||||
You may override it in your custom Hyperopt class.
|
|
||||||
"""
|
|
||||||
return [
|
|
||||||
# It was decided to always set trailing_stop is to True if the 'trailing' hyperspace
|
|
||||||
# is used. Otherwise hyperopt will vary other parameters that won't have effect if
|
|
||||||
# trailing_stop is set False.
|
|
||||||
# This parameter is included into the hyperspace dimensions rather than assigning
|
|
||||||
# it explicitly in the code in order to have it printed in the results along with
|
|
||||||
# other 'trailing' hyperspace parameters.
|
|
||||||
Categorical([True], name='trailing_stop'),
|
|
||||||
|
|
||||||
SKDecimal(0.01, 0.35, decimals=3, name='trailing_stop_positive'),
|
|
||||||
|
|
||||||
# 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive',
|
|
||||||
# so this intermediate parameter is used as the value of the difference between
|
|
||||||
# them. The value of the 'trailing_stop_positive_offset' is constructed in the
|
|
||||||
# generate_trailing_params() method.
|
|
||||||
# This is similar to the hyperspace dimensions used for constructing the ROI tables.
|
|
||||||
SKDecimal(0.001, 0.1, decimals=3, name='trailing_stop_positive_offset_p1'),
|
|
||||||
|
|
||||||
Categorical([True, False], name='trailing_only_offset_is_reached'),
|
|
||||||
]
|
|
Loading…
Reference in New Issue
Block a user