From c4105436ebbad89d37922aad57068bef5c5cca32 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 10 Oct 2019 04:37:32 +0300 Subject: [PATCH] Disable defaulting to DefaultHyperOpts and DefaultHyperOptLoss --- .travis.yml | 2 +- docs/bot-usage.md | 8 +-- freqtrade/configuration/cli_options.py | 7 +- freqtrade/constants.py | 2 - freqtrade/resolvers/hyperopt_resolver.py | 27 +++++--- tests/optimize/test_hyperopt.py | 81 +++++++++++++++++++++--- 6 files changed, 96 insertions(+), 31 deletions(-) diff --git a/.travis.yml b/.travis.yml index b066e7044..7a75a76c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,7 @@ jobs: name: backtest - script: - cp config.json.example config.json - - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy DefaultStrategy + - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy DefaultStrategy --customhyperopt DefaultHyperOpts --hyperopt-loss DefaultHyperOptLoss name: hyperopt - script: flake8 name: flake8 diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 2b66d3c25..fcf82826a 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -285,8 +285,8 @@ optional arguments: --stake_amount STAKE_AMOUNT Specify stake_amount. --customhyperopt NAME - Specify hyperopt class name (default: - `DefaultHyperOpts`). + Specify hyperopt class name which will be used by the + bot. --hyperopt-path PATH Specify additional lookup path for Hyperopts and Hyperopt Loss functions. --eps, --enable-position-stacking @@ -322,8 +322,8 @@ optional arguments: generate completely different results, since the target for optimization is different. Built-in Hyperopt-loss-functions are: DefaultHyperOptLoss, - OnlyProfitHyperOptLoss, SharpeHyperOptLoss.(default: - `DefaultHyperOptLoss`). + OnlyProfitHyperOptLoss, SharpeHyperOptLoss. + Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index 2ecd4cfc5..6928ddfdb 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -153,9 +153,8 @@ AVAILABLE_CLI_OPTIONS = { # Hyperopt "hyperopt": Arg( '--customhyperopt', - help='Specify hyperopt class name (default: `%(default)s`).', + help='Specify hyperopt class name which will be used by the bot.', metavar='NAME', - default=constants.DEFAULT_HYPEROPT, ), "hyperopt_path": Arg( '--hyperopt-path', @@ -232,10 +231,8 @@ AVAILABLE_CLI_OPTIONS = { help='Specify the class name of the hyperopt loss function class (IHyperOptLoss). ' 'Different functions can generate completely different results, ' 'since the target for optimization is different. Built-in Hyperopt-loss-functions are: ' - 'DefaultHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss.' - '(default: `%(default)s`).', + 'DefaultHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss.', metavar='NAME', - default=constants.DEFAULT_HYPEROPT_LOSS, ), # List exchanges "print_one_column": Arg( diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 749ae25b5..2f490c900 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -9,8 +9,6 @@ PROCESS_THROTTLE_SECS = 5 # sec DEFAULT_TICKER_INTERVAL = 5 # min HYPEROPT_EPOCH = 100 # epochs RETRY_TIMEOUT = 30 # sec -DEFAULT_HYPEROPT = 'DefaultHyperOpts' -DEFAULT_HYPEROPT_LOSS = 'DefaultHyperOptLoss' DEFAULT_DB_PROD_URL = 'sqlite:///tradesv3.sqlite' DEFAULT_DB_DRYRUN_URL = 'sqlite://' UNLIMITED_STAKE_AMOUNT = 'unlimited' diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index e96394d69..45fe2548e 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -8,7 +8,6 @@ from pathlib import Path from typing import Optional, Dict from freqtrade import OperationalException -from freqtrade.constants import DEFAULT_HYPEROPT, DEFAULT_HYPEROPT_LOSS from freqtrade.optimize.hyperopt_interface import IHyperOpt from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss from freqtrade.resolvers import IResolver @@ -20,17 +19,21 @@ class HyperOptResolver(IResolver): """ This class contains all the logic to load custom hyperopt class """ - __slots__ = ['hyperopt'] - def __init__(self, config: Dict) -> None: + def __init__(self, config: Dict = None) -> None: """ Load the custom class from config parameter :param config: configuration dictionary """ + config = config or {} + + if not config.get('hyperopt'): + raise OperationalException("No Hyperopt set. Please use `--customhyperopt` to specify " + "the Hyperopt class to use.") + + hyperopt_name = config['hyperopt'] - # Verify the hyperopt is in the configuration, otherwise fallback to the default hyperopt - hyperopt_name = config.get('hyperopt') or DEFAULT_HYPEROPT self.hyperopt = self._load_hyperopt(hyperopt_name, config, extra_dir=config.get('hyperopt_path')) @@ -75,7 +78,6 @@ class HyperOptLossResolver(IResolver): """ This class contains all the logic to load custom hyperopt loss class """ - __slots__ = ['hyperoptloss'] def __init__(self, config: Dict = None) -> None: @@ -85,17 +87,22 @@ class HyperOptLossResolver(IResolver): """ config = config or {} - # Verify the hyperopt is in the configuration, otherwise fallback to the default hyperopt - hyperopt_name = config.get('hyperopt_loss') or DEFAULT_HYPEROPT_LOSS + if not config.get('hyperopt_loss'): + raise OperationalException("No Hyperopt Loss Function set. Please use " + "`--hyperopt-loss` to specify " + "the Hyperopt Loss Function class to use.") + + hyperoptloss_name = config['hyperopt_loss'] + self.hyperoptloss = self._load_hyperoptloss( - hyperopt_name, config, extra_dir=config.get('hyperopt_path')) + hyperoptloss_name, config, extra_dir=config.get('hyperopt_path')) # Assign ticker_interval to be used in hyperopt self.hyperoptloss.__class__.ticker_interval = str(config['ticker_interval']) if not hasattr(self.hyperoptloss, 'hyperopt_loss_function'): raise OperationalException( - f"Found hyperopt {hyperopt_name} does not implement `hyperopt_loss_function`.") + f"Found hyperopt {hyperoptloss_name} does not implement `hyperopt_loss_function`.") def _load_hyperoptloss( self, hyper_loss_name: str, config: Dict, diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 5ff11d5ea..cf211e35b 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -25,7 +25,11 @@ from tests.conftest import (get_args, log_has, log_has_re, patch_exchange, @pytest.fixture(scope='function') def hyperopt(default_conf, mocker): - default_conf.update({'spaces': ['all']}) + default_conf.update({ + 'spaces': ['all'], + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', + }) patch_exchange(mocker) return Hyperopt(default_conf) @@ -70,6 +74,8 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca args = [ 'hyperopt', '--config', 'config.json', + '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt-loss', 'DefaultHyperOptLoss', ] config = setup_configuration(get_args(args), RunMode.HYPEROPT) @@ -101,6 +107,8 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo args = [ 'hyperopt', '--config', 'config.json', + '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt-loss', 'DefaultHyperOptLoss', '--datadir', '/foo/bar', '--ticker-interval', '1m', '--timerange', ':100', @@ -155,7 +163,9 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None: 'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver._load_hyperopt', MagicMock(return_value=hyperopts(default_conf)) ) - x = HyperOptResolver(default_conf, ).hyperopt + default_conf.update({'hyperopt': 'DefaultHyperOpts'}) + default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) + x = HyperOptResolver(default_conf).hyperopt assert not hasattr(x, 'populate_buy_trend') assert not hasattr(x, 'populate_sell_trend') assert log_has("Hyperopt class does not provide populate_sell_trend() method. " @@ -169,7 +179,15 @@ def test_hyperoptresolver_wrongname(mocker, default_conf, caplog) -> None: default_conf.update({'hyperopt': "NonExistingHyperoptClass"}) with pytest.raises(OperationalException, match=r'Impossible to load Hyperopt.*'): - HyperOptResolver(default_conf, ).hyperopt + HyperOptResolver(default_conf).hyperopt + + +def test_hyperoptresolver_noname(default_conf): + default_conf['hyperopt'] = '' + with pytest.raises(OperationalException, + match="No Hyperopt set. Please use `--customhyperopt` to specify " + "the Hyperopt class to use."): + HyperOptResolver(default_conf) def test_hyperoptlossresolver(mocker, default_conf, caplog) -> None: @@ -179,7 +197,8 @@ def test_hyperoptlossresolver(mocker, default_conf, caplog) -> None: 'freqtrade.resolvers.hyperopt_resolver.HyperOptLossResolver._load_hyperoptloss', MagicMock(return_value=hl) ) - x = HyperOptLossResolver(default_conf, ).hyperoptloss + default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) + x = HyperOptLossResolver(default_conf).hyperoptloss assert hasattr(x, "hyperopt_loss_function") @@ -187,7 +206,17 @@ def test_hyperoptlossresolver_wrongname(mocker, default_conf, caplog) -> None: default_conf.update({'hyperopt_loss': "NonExistingLossClass"}) with pytest.raises(OperationalException, match=r'Impossible to load HyperoptLoss.*'): - HyperOptLossResolver(default_conf, ).hyperopt + HyperOptLossResolver(default_conf).hyperopt + + +def test_hyperoptlossresolver_noname(default_conf): + default_conf.update({'hyperopt': 'DefaultHyperOpts'}) + default_conf['hyperopt_loss'] = '' + with pytest.raises(OperationalException, + match="No Hyperopt Loss Function set. Please use " + "`--hyperopt-loss` to specify " + "the Hyperopt Loss Function class to use."): + HyperOptLossResolver(default_conf) def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None: @@ -200,6 +229,8 @@ def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None args = [ 'hyperopt', '--config', 'config.json', + '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt-loss', 'DefaultHyperOptLoss', '--epochs', '5' ] args = get_args(args) @@ -217,6 +248,8 @@ def test_start(mocker, default_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', + '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt-loss', 'DefaultHyperOptLoss', '--epochs', '5' ] args = get_args(args) @@ -239,6 +272,8 @@ def test_start_no_data(mocker, default_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', + '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt-loss', 'DefaultHyperOptLoss', '--epochs', '5' ] args = get_args(args) @@ -256,6 +291,8 @@ def test_start_filelock(mocker, default_conf, caplog) -> None: args = [ 'hyperopt', '--config', 'config.json', + '--customhyperopt', 'DefaultHyperOpts', + '--hyperopt-loss', 'DefaultHyperOptLoss', '--epochs', '5' ] args = get_args(args) @@ -264,6 +301,7 @@ def test_start_filelock(mocker, default_conf, caplog) -> None: def test_loss_calculation_prefer_correct_trade_count(default_conf, hyperopt_results) -> None: + default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) hl = HyperOptLossResolver(default_conf).hyperoptloss correct = hl.hyperopt_loss_function(hyperopt_results, 600) over = hl.hyperopt_loss_function(hyperopt_results, 600 + 100) @@ -276,6 +314,7 @@ def test_loss_calculation_prefer_shorter_trades(default_conf, hyperopt_results) resultsb = hyperopt_results.copy() resultsb.loc[1, 'trade_duration'] = 20 + default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) hl = HyperOptLossResolver(default_conf).hyperoptloss longer = hl.hyperopt_loss_function(hyperopt_results, 100) shorter = hl.hyperopt_loss_function(resultsb, 100) @@ -288,6 +327,7 @@ def test_loss_calculation_has_limited_profit(default_conf, hyperopt_results) -> results_under = hyperopt_results.copy() results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2 + default_conf.update({'hyperopt_loss': 'DefaultHyperOptLoss'}) hl = HyperOptLossResolver(default_conf).hyperoptloss correct = hl.hyperopt_loss_function(hyperopt_results, 600) over = hl.hyperopt_loss_function(results_over, 600) @@ -407,6 +447,8 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None: patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -510,10 +552,13 @@ def test_buy_strategy_generator(hyperopt, testdatadir) -> None: def test_generate_optimizer(mocker, default_conf) -> None: - default_conf.update({'config': 'config.json.example'}) - default_conf.update({'timerange': None}) - default_conf.update({'spaces': 'all'}) - default_conf.update({'hyperopt_min_trades': 1}) + default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', + 'timerange': None, + 'spaces': 'all', + 'hyperopt_min_trades': 1, + }) trades = [ ('POWR/BTC', 0.023117, 0.000233, 100) @@ -576,6 +621,8 @@ def test_generate_optimizer(mocker, default_conf) -> None: def test_clean_hyperopt(mocker, default_conf, caplog): patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -592,6 +639,8 @@ def test_clean_hyperopt(mocker, default_conf, caplog): def test_continue_hyperopt(mocker, default_conf, caplog): patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -621,6 +670,8 @@ def test_print_json_spaces_all(mocker, default_conf, caplog, capsys) -> None: patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -658,6 +709,8 @@ def test_print_json_spaces_roi_stoploss(mocker, default_conf, caplog, capsys) -> patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'roi stoploss', @@ -696,6 +749,8 @@ def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys) patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'roi stoploss', @@ -737,6 +792,8 @@ def test_simplified_interface_all_failed(mocker, default_conf, caplog, capsys) - patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'all', @@ -770,6 +827,8 @@ def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None: patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'buy', @@ -815,6 +874,8 @@ def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': 'sell', @@ -862,6 +923,8 @@ def test_simplified_interface_failed(mocker, default_conf, caplog, capsys, metho patch_exchange(mocker) default_conf.update({'config': 'config.json.example', + 'hyperopt': 'DefaultHyperOpts', + 'hyperopt_loss': 'DefaultHyperOptLoss', 'epochs': 1, 'timerange': None, 'spaces': space,