Merge pull request #5566 from freqtrade/remove_hyperopt

Remove legacy hyperopt
This commit is contained in:
Matthias
2021-09-14 19:20:58 +02:00
committed by GitHub
37 changed files with 163 additions and 1715 deletions

View File

@@ -11,11 +11,11 @@ from freqtrade.commands.build_config_commands import start_new_config
from freqtrade.commands.data_commands import (start_convert_data, start_download_data,
start_list_data)
from freqtrade.commands.deploy_commands import (start_create_userdir, start_install_ui,
start_new_hyperopt, start_new_strategy)
start_new_strategy)
from freqtrade.commands.hyperopt_commands import start_hyperopt_list, start_hyperopt_show
from freqtrade.commands.list_commands import (start_list_exchanges, start_list_hyperopts,
start_list_markets, start_list_strategies,
start_list_timeframes, start_show_trades)
from freqtrade.commands.list_commands import (start_list_exchanges, start_list_markets,
start_list_strategies, start_list_timeframes,
start_show_trades)
from freqtrade.commands.optimize_commands import start_backtesting, start_edge, start_hyperopt
from freqtrade.commands.pairlist_commands import start_test_pairlist
from freqtrade.commands.plot_commands import start_plot_dataframe, start_plot_profit

View File

@@ -55,8 +55,6 @@ ARGS_BUILD_CONFIG = ["config"]
ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"]
ARGS_BUILD_HYPEROPT = ["user_data_dir", "hyperopt", "template"]
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"]
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"]
@@ -92,10 +90,10 @@ ARGS_HYPEROPT_SHOW = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperop
NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list-timeframes",
"list-markets", "list-pairs", "list-strategies", "list-data",
"list-hyperopts", "hyperopt-list", "hyperopt-show",
"hyperopt-list", "hyperopt-show",
"plot-dataframe", "plot-profit", "show-trades"]
NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-hyperopt", "new-strategy"]
NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-strategy"]
class Arguments:
@@ -174,12 +172,11 @@ class Arguments:
from freqtrade.commands import (start_backtesting, start_convert_data, start_create_userdir,
start_download_data, start_edge, start_hyperopt,
start_hyperopt_list, start_hyperopt_show, start_install_ui,
start_list_data, start_list_exchanges, start_list_hyperopts,
start_list_markets, start_list_strategies,
start_list_timeframes, start_new_config, start_new_hyperopt,
start_new_strategy, start_plot_dataframe, start_plot_profit,
start_show_trades, start_test_pairlist, start_trading,
start_webserver)
start_list_data, start_list_exchanges, start_list_markets,
start_list_strategies, start_list_timeframes,
start_new_config, start_new_strategy, start_plot_dataframe,
start_plot_profit, start_show_trades, start_test_pairlist,
start_trading, start_webserver)
subparsers = self.parser.add_subparsers(dest='command',
# Use custom message when no subhandler is added
@@ -206,12 +203,6 @@ class Arguments:
build_config_cmd.set_defaults(func=start_new_config)
self._build_args(optionlist=ARGS_BUILD_CONFIG, parser=build_config_cmd)
# add new-hyperopt subcommand
build_hyperopt_cmd = subparsers.add_parser('new-hyperopt',
help="Create new hyperopt")
build_hyperopt_cmd.set_defaults(func=start_new_hyperopt)
self._build_args(optionlist=ARGS_BUILD_HYPEROPT, parser=build_hyperopt_cmd)
# add new-strategy subcommand
build_strategy_cmd = subparsers.add_parser('new-strategy',
help="Create new strategy")
@@ -300,15 +291,6 @@ class Arguments:
list_exchanges_cmd.set_defaults(func=start_list_exchanges)
self._build_args(optionlist=ARGS_LIST_EXCHANGES, parser=list_exchanges_cmd)
# Add list-hyperopts subcommand
list_hyperopts_cmd = subparsers.add_parser(
'list-hyperopts',
help='Print available hyperopt classes.',
parents=[_common_parser],
)
list_hyperopts_cmd.set_defaults(func=start_list_hyperopts)
self._build_args(optionlist=ARGS_LIST_HYPEROPTS, parser=list_hyperopts_cmd)
# Add list-markets subcommand
list_markets_cmd = subparsers.add_parser(
'list-markets',

View File

@@ -1,7 +1,7 @@
"""
Definition of cli arguments used in arguments.py
"""
from argparse import ArgumentTypeError
from argparse import SUPPRESS, ArgumentTypeError
from freqtrade import __version__, constants
from freqtrade.constants import HYPEROPT_LOSS_BUILTIN
@@ -203,13 +203,13 @@ AVAILABLE_CLI_OPTIONS = {
# Hyperopt
"hyperopt": Arg(
'--hyperopt',
help='Specify hyperopt class name which will be used by the bot.',
help=SUPPRESS,
metavar='NAME',
required=False,
),
"hyperopt_path": Arg(
'--hyperopt-path',
help='Specify additional lookup path for Hyperopt and Hyperopt Loss functions.',
help='Specify additional lookup path for Hyperopt Loss functions.',
metavar='PATH',
),
"epochs": Arg(

View File

@@ -7,7 +7,7 @@ import requests
from freqtrade.configuration import setup_utils_configuration
from freqtrade.configuration.directory_operations import copy_sample_files, create_userdata_dir
from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGIES
from freqtrade.constants import USERPATH_STRATEGIES
from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException
from freqtrade.misc import render_template, render_template_with_fallback
@@ -87,56 +87,6 @@ def start_new_strategy(args: Dict[str, Any]) -> None:
raise OperationalException("`new-strategy` requires --strategy to be set.")
def deploy_new_hyperopt(hyperopt_name: str, hyperopt_path: Path, subtemplate: str) -> None:
"""
Deploys a new hyperopt template to hyperopt_path
"""
fallback = 'full'
buy_guards = render_template_with_fallback(
templatefile=f"subtemplates/hyperopt_buy_guards_{subtemplate}.j2",
templatefallbackfile=f"subtemplates/hyperopt_buy_guards_{fallback}.j2",
)
sell_guards = render_template_with_fallback(
templatefile=f"subtemplates/hyperopt_sell_guards_{subtemplate}.j2",
templatefallbackfile=f"subtemplates/hyperopt_sell_guards_{fallback}.j2",
)
buy_space = render_template_with_fallback(
templatefile=f"subtemplates/hyperopt_buy_space_{subtemplate}.j2",
templatefallbackfile=f"subtemplates/hyperopt_buy_space_{fallback}.j2",
)
sell_space = render_template_with_fallback(
templatefile=f"subtemplates/hyperopt_sell_space_{subtemplate}.j2",
templatefallbackfile=f"subtemplates/hyperopt_sell_space_{fallback}.j2",
)
strategy_text = render_template(templatefile='base_hyperopt.py.j2',
arguments={"hyperopt": hyperopt_name,
"buy_guards": buy_guards,
"sell_guards": sell_guards,
"buy_space": buy_space,
"sell_space": sell_space,
})
logger.info(f"Writing hyperopt to `{hyperopt_path}`.")
hyperopt_path.write_text(strategy_text)
def start_new_hyperopt(args: Dict[str, Any]) -> None:
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
if 'hyperopt' in args and args['hyperopt']:
new_path = config['user_data_dir'] / USERPATH_HYPEROPTS / (args['hyperopt'] + '.py')
if new_path.exists():
raise OperationalException(f"`{new_path}` already exists. "
"Please choose another Hyperopt Name.")
deploy_new_hyperopt(args['hyperopt'], new_path, args['template'])
else:
raise OperationalException("`new-hyperopt` requires --hyperopt to be set.")
def clean_ui_subdir(directory: Path):
if directory.is_dir():
logger.info("Removing UI directory content.")

View File

@@ -10,7 +10,7 @@ from colorama import init as colorama_init
from tabulate import tabulate
from freqtrade.configuration import setup_utils_configuration
from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGIES
from freqtrade.constants import USERPATH_STRATEGIES
from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException
from freqtrade.exchange import market_is_active, validate_exchanges
@@ -92,25 +92,6 @@ def start_list_strategies(args: Dict[str, Any]) -> None:
_print_objs_tabular(strategy_objs, config.get('print_colorized', False))
def start_list_hyperopts(args: Dict[str, Any]) -> None:
"""
Print files with HyperOpt custom classes available in the directory
"""
from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
directory = Path(config.get('hyperopt_path', config['user_data_dir'] / USERPATH_HYPEROPTS))
hyperopt_objs = HyperOptResolver.search_all_objects(directory, not args['print_one_column'])
# Sort alphabetically
hyperopt_objs = sorted(hyperopt_objs, key=lambda x: x['name'])
if args['print_one_column']:
print('\n'.join([s['name'] for s in hyperopt_objs]))
else:
_print_objs_tabular(hyperopt_objs, config.get('print_colorized', False))
def start_list_timeframes(args: Dict[str, Any]) -> None:
"""
Print timeframes available on Exchange

View File

@@ -69,9 +69,7 @@ DUST_PER_COIN = {
# Source files with destination directories within user-directory
USER_DATA_FILES = {
'sample_strategy.py': USERPATH_STRATEGIES,
'sample_hyperopt_advanced.py': USERPATH_HYPEROPTS,
'sample_hyperopt_loss.py': USERPATH_HYPEROPTS,
'sample_hyperopt.py': USERPATH_HYPEROPTS,
'strategy_analysis_example.ipynb': USERPATH_NOTEBOOKS,
}

View File

@@ -22,6 +22,7 @@ from pandas import DataFrame
from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN
from freqtrade.data.converter import trim_dataframes
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.optimize.backtesting import Backtesting
# 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_tools import HyperoptTools, hyperopt_serializer
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
@@ -78,10 +79,10 @@ class Hyperopt:
if not self.config.get('hyperopt'):
self.custom_hyperopt = HyperOptAuto(self.config)
self.auto_hyperopt = True
else:
self.custom_hyperopt = HyperOptResolver.load_hyperopt(self.config)
self.auto_hyperopt = False
raise OperationalException(
"Using separate 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.custom_hyperopt.strategy = self.backtesting.strategy
@@ -103,31 +104,6 @@ class Hyperopt:
self.num_epochs_saved = 0
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
if self.config.get('use_max_market_positions', True):
self.max_open_trades = self.config['max_open_trades']
@@ -256,7 +232,7 @@ class Hyperopt:
"""
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
logger.debug("Hyperopt has 'protection' space")
# Enable Protections if protection space is selected.
@@ -285,6 +261,15 @@ class Hyperopt:
self.dimensions = (self.buy_space + self.sell_space + self.protection_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:
"""
Used Optimize function.
@@ -296,18 +281,13 @@ class Hyperopt:
# Apply parameters
if HyperoptTools.has_space(self.config, 'buy'):
self.backtesting.strategy.advise_buy = ( # type: ignore
self.custom_hyperopt.buy_strategy_generator(params_dict))
self.assign_params(params_dict, 'buy')
if HyperoptTools.has_space(self.config, 'sell'):
self.backtesting.strategy.advise_sell = ( # type: ignore
self.custom_hyperopt.sell_strategy_generator(params_dict))
self.assign_params(params_dict, 'sell')
if HyperoptTools.has_space(self.config, 'protection'):
for attr_name, attr in self.backtesting.strategy.enumerate_parameters('protection'):
if attr.optimize:
# noinspection PyProtectedMember
attr.value = params_dict[attr_name]
self.assign_params(params_dict, 'protection')
if HyperoptTools.has_space(self.config, 'roi'):
self.backtesting.strategy.minimal_roi = ( # type: ignore
@@ -517,11 +497,10 @@ class Hyperopt:
f"saved to '{self.results_file}'.")
if self.current_best_epoch:
if self.auto_hyperopt:
HyperoptTools.try_export_params(
self.config,
self.backtesting.strategy.get_strategy_name(),
self.current_best_epoch)
HyperoptTools.try_export_params(
self.config,
self.backtesting.strategy.get_strategy_name(),
self.current_best_epoch)
HyperoptTools.show_epoch_details(self.current_best_epoch, self.total_epochs,
self.print_json)

View File

@@ -4,9 +4,9 @@ This module implements a convenience auto-hyperopt class, which can be used toge
that implement IHyperStrategy interface.
"""
from contextlib import suppress
from typing import Any, Callable, Dict, List
from typing import Callable, Dict, List
from pandas import DataFrame
from freqtrade.exceptions import OperationalException
with suppress(ImportError):
@@ -15,6 +15,14 @@ with suppress(ImportError):
from freqtrade.optimize.hyperopt_interface import IHyperOpt
def _format_exception_message(space: str) -> str:
raise OperationalException(
f"The '{space}' space is included into the hyperoptimization "
f"but no parameter for this space was not found in your Strategy. "
f"Please make sure to have parameters for this space enabled for optimization "
f"or remove the '{space}' space from hyperoptimization.")
class HyperOptAuto(IHyperOpt):
"""
This class delegates functionality to Strategy(IHyperStrategy) and Strategy.HyperOpt classes.
@@ -22,26 +30,6 @@ class HyperOptAuto(IHyperOpt):
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:
"""
Return a function defined in Strategy.HyperOpt class, or one defined in super() class.
@@ -60,21 +48,22 @@ class HyperOptAuto(IHyperOpt):
if attr.optimize:
yield attr.get_space(attr_name)
def _get_indicator_space(self, category, fallback_method_name):
def _get_indicator_space(self, category):
# TODO: is this necessary, or can we call "generate_space" directly?
indicator_space = list(self._generate_indicator_space(category))
if len(indicator_space) > 0:
return indicator_space
else:
return self._get_func(fallback_method_name)()
_format_exception_message(category)
def indicator_space(self) -> List['Dimension']:
return self._get_indicator_space('buy', 'indicator_space')
return self._get_indicator_space('buy')
def sell_indicator_space(self) -> List['Dimension']:
return self._get_indicator_space('sell', 'sell_indicator_space')
return self._get_indicator_space('sell')
def protection_space(self) -> List['Dimension']:
return self._get_indicator_space('protection', 'protection_space')
return self._get_indicator_space('protection')
def generate_roi_table(self, params: Dict) -> Dict[int, float]:
return self._get_func('generate_roi_table')(params)

View File

@@ -5,11 +5,10 @@ This module defines the interface to apply for hyperopt
import logging
import math
from abc import ABC
from typing import Any, Callable, Dict, List
from typing import Dict, List
from skopt.space import Categorical, Dimension, Integer
from freqtrade.exceptions import OperationalException
from freqtrade.exchange import timeframe_to_minutes
from freqtrade.misc import round_dict
from freqtrade.optimize.space import SKDecimal
@@ -19,13 +18,6 @@ from freqtrade.strategy import IStrategy
logger = logging.getLogger(__name__)
def _format_exception_message(method: str, space: str) -> str:
return (f"The '{space}' space is included into the hyperoptimization "
f"but {method}() method is not found in your "
f"custom Hyperopt class. You should either implement this "
f"method or remove the '{space}' space from hyperoptimization.")
class IHyperOpt(ABC):
"""
Interface for freqtrade hyperopt
@@ -45,37 +37,6 @@ class IHyperOpt(ABC):
IHyperOpt.ticker_interval = str(config['timeframe']) # DEPRECATED
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]:
"""
Create a protection space.
Only supported by the Parameter interface.
"""
raise OperationalException(_format_exception_message('indicator_space', 'protection'))
def indicator_space(self) -> List[Dimension]:
"""
Create an indicator space.
"""
raise OperationalException(_format_exception_message('indicator_space', 'buy'))
def sell_indicator_space(self) -> List[Dimension]:
"""
Create a sell indicator space.
"""
raise OperationalException(_format_exception_message('sell_indicator_space', 'sell'))
def generate_roi_table(self, params: Dict) -> Dict[int, float]:
"""
Create a ROI table.

View File

@@ -9,7 +9,6 @@ from typing import Dict
from freqtrade.constants import HYPEROPT_LOSS_BUILTIN, USERPATH_HYPEROPTS
from freqtrade.exceptions import OperationalException
from freqtrade.optimize.hyperopt_interface import IHyperOpt
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss
from freqtrade.resolvers import IResolver
@@ -17,43 +16,6 @@ from freqtrade.resolvers import IResolver
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):
"""
This class contains all the logic to load custom hyperopt loss class

View File

@@ -1,137 +0,0 @@
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# --- 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 {{ hyperopt }}(IHyperOpt):
"""
This is a Hyperopt template to get you started.
More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/
You should:
- 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 '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 [
{{ buy_space | indent(12) }}
]
@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
{{ buy_guards | indent(12) }}
# 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 the candle had volume
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 [
{{ sell_space | indent(12) }}
]
@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
{{ sell_guards | indent(12) }}
# 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 the candle had volume
conditions.append(dataframe['volume'] > 0)
if conditions:
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'sell'] = 1
return dataframe
return populate_sell_trend

View File

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

View File

@@ -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'),
]

View File

@@ -1,8 +0,0 @@
if params.get('mfi-enabled'):
conditions.append(dataframe['mfi'] < params['mfi-value'])
if params.get('fastd-enabled'):
conditions.append(dataframe['fastd'] < params['fastd-value'])
if params.get('adx-enabled'):
conditions.append(dataframe['adx'] > params['adx-value'])
if params.get('rsi-enabled'):
conditions.append(dataframe['rsi'] < params['rsi-value'])

View File

@@ -1,2 +0,0 @@
if params.get('rsi-enabled'):
conditions.append(dataframe['rsi'] < params['rsi-value'])

View File

@@ -1,9 +0,0 @@
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')

View File

@@ -1,3 +0,0 @@
Integer(20, 40, name='rsi-value'),
Categorical([True, False], name='rsi-enabled'),
Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger')

View File

@@ -1,8 +0,0 @@
if params.get('sell-mfi-enabled'):
conditions.append(dataframe['mfi'] > params['sell-mfi-value'])
if params.get('sell-fastd-enabled'):
conditions.append(dataframe['fastd'] > params['sell-fastd-value'])
if params.get('sell-adx-enabled'):
conditions.append(dataframe['adx'] < params['sell-adx-value'])
if params.get('sell-rsi-enabled'):
conditions.append(dataframe['rsi'] > params['sell-rsi-value'])

View File

@@ -1,2 +0,0 @@
if params.get('sell-rsi-enabled'):
conditions.append(dataframe['rsi'] > params['sell-rsi-value'])

View File

@@ -1,11 +0,0 @@
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')

View File

@@ -1,5 +0,0 @@
Integer(60, 100, name='sell-rsi-value'),
Categorical([True, False], name='sell-rsi-enabled'),
Categorical(['sell-bb_upper',
'sell-macd_cross_signal',
'sell-sar_reversal'], name='sell-trigger')