This commit is contained in:
orehunt 2020-03-03 07:57:13 +01:00
parent daba9d157b
commit d6c66a54fd
7 changed files with 726 additions and 1139 deletions

View File

@ -15,7 +15,8 @@ ARGS_STRATEGY = ["strategy", "strategy_path"]
ARGS_TRADE = ["db_url", "sd_notify", "dry_run"] ARGS_TRADE = ["db_url", "sd_notify", "dry_run"]
ARGS_COMMON_OPTIMIZE = ["ticker_interval", "timerange", "max_open_trades", "stake_amount", "fee"] ARGS_COMMON_OPTIMIZE = ["ticker_interval", "timerange",
"max_open_trades", "stake_amount", "fee"]
ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + [ ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + [
"position_stacking", "use_max_market_positions", "strategy_list", "export", "exportfilename" "position_stacking", "use_max_market_positions", "strategy_list", "export", "exportfilename"
@ -38,10 +39,8 @@ ARGS_LIST_EXCHANGES = ["print_one_column", "list_exchanges_all"]
ARGS_LIST_TIMEFRAMES = ["exchange", "print_one_column"] ARGS_LIST_TIMEFRAMES = ["exchange", "print_one_column"]
ARGS_LIST_PAIRS = [ ARGS_LIST_PAIRS = ["exchange", "print_list", "list_pairs_print_json", "print_one_column",
"exchange", "print_list", "list_pairs_print_json", "print_one_column", "print_csv", "print_csv", "base_currencies", "quote_currencies", "list_pairs_all"]
"base_currencies", "quote_currencies", "list_pairs_all"
]
ARGS_TEST_PAIRLIST = ["config", "quote_currencies", "print_one_column", "list_pairs_print_json"] ARGS_TEST_PAIRLIST = ["config", "quote_currencies", "print_one_column", "list_pairs_print_json"]
@ -56,38 +55,30 @@ ARGS_BUILD_HYPEROPT = ["user_data_dir", "hyperopt", "template"]
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"] ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"]
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"] ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"]
ARGS_DOWNLOAD_DATA = [ ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "download_trades", "exchange",
"pairs", "pairs_file", "days", "download_trades", "exchange", "timeframes", "erase", "timeframes", "erase", "dataformat_ohlcv", "dataformat_trades"]
"dataformat_ohlcv", "dataformat_trades"
]
ARGS_PLOT_DATAFRAME = [ ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit",
"pairs", "indicators1", "indicators2", "plot_limit", "db_url", "trade_source", "export", "db_url", "trade_source", "export", "exportfilename",
"exportfilename", "timerange", "ticker_interval" "timerange", "ticker_interval"]
]
ARGS_PLOT_PROFIT = [ ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url",
"pairs", "timerange", "export", "exportfilename", "db_url", "trade_source", "ticker_interval" "trade_source", "ticker_interval"]
]
ARGS_HYPEROPT_LIST = [ ARGS_HYPEROPT_LIST = ["hyperopt_list_best", "hyperopt_list_profitable",
"hyperopt_list_best", "hyperopt_list_profitable", "hyperopt_list_min_trades", "hyperopt_list_min_trades", "hyperopt_list_max_trades",
"hyperopt_list_max_trades", "hyperopt_list_min_avg_time", "hyperopt_list_max_avg_time", "hyperopt_list_min_avg_time", "hyperopt_list_max_avg_time",
"hyperopt_list_min_avg_profit", "hyperopt_list_max_avg_profit", "hyperopt_list_min_avg_profit", "hyperopt_list_max_avg_profit",
"hyperopt_list_min_total_profit", "hyperopt_list_max_total_profit", "print_colorized", "hyperopt_list_min_total_profit", "hyperopt_list_max_total_profit",
"print_json", "hyperopt_list_no_details" "print_colorized", "print_json", "hyperopt_list_no_details"]
]
ARGS_HYPEROPT_SHOW = [ ARGS_HYPEROPT_SHOW = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperopt_show_index",
"hyperopt_list_best", "hyperopt_list_profitable", "hyperopt_show_index", "print_json", "print_json", "hyperopt_show_no_header"]
"hyperopt_show_no_header"
]
NO_CONF_REQURIED = [ NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list-timeframes",
"convert-data", "convert-trade-data", "download-data", "list-timeframes", "list-markets", "list-markets", "list-pairs", "list-strategies",
"list-pairs", "list-strategies", "list-hyperopts", "hyperopt-list", "hyperopt-show", "list-hyperopts", "hyperopt-list", "hyperopt-show",
"plot-dataframe", "plot-profit" "plot-dataframe", "plot-profit"]
]
NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-hyperopt", "new-strategy"] NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-hyperopt", "new-strategy"]
@ -96,6 +87,7 @@ class Arguments:
""" """
Arguments Class. Manage the arguments received by the cli Arguments Class. Manage the arguments received by the cli
""" """
def __init__(self, args: Optional[List[str]]) -> None: def __init__(self, args: Optional[List[str]]) -> None:
self.args = args self.args = args
self._parsed_arg: Optional[argparse.Namespace] = None self._parsed_arg: Optional[argparse.Namespace] = None
@ -164,70 +156,70 @@ class Arguments:
self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot') self.parser = argparse.ArgumentParser(description='Free, open source crypto trading bot')
self._build_args(optionlist=['version'], parser=self.parser) self._build_args(optionlist=['version'], parser=self.parser)
from freqtrade.commands import ( from freqtrade.commands import (start_create_userdir, start_convert_data,
start_create_userdir, start_convert_data, start_download_data, start_hyperopt_list, start_download_data,
start_hyperopt_show, start_list_exchanges, start_list_hyperopts, start_list_markets, start_hyperopt_list, start_hyperopt_show,
start_list_strategies, start_list_timeframes, start_new_config, start_new_hyperopt, start_list_exchanges, start_list_hyperopts,
start_new_strategy, start_plot_dataframe, start_plot_profit, start_backtesting, start_list_markets, start_list_strategies,
start_hyperopt, start_edge, start_test_pairlist, start_trading) start_list_timeframes, start_new_config,
start_new_hyperopt, start_new_strategy,
start_plot_dataframe, start_plot_profit,
start_backtesting, start_hyperopt, start_edge,
start_test_pairlist, start_trading)
subparsers = self.parser.add_subparsers( subparsers = self.parser.add_subparsers(dest='command',
dest='command',
# Use custom message when no subhandler is added # Use custom message when no subhandler is added
# shown from `main.py` # shown from `main.py`
# required=True # required=True
) )
# Add trade subcommand # Add trade subcommand
trade_cmd = subparsers.add_parser('trade', trade_cmd = subparsers.add_parser('trade', help='Trade module.',
help='Trade module.',
parents=[_common_parser, _strategy_parser]) parents=[_common_parser, _strategy_parser])
trade_cmd.set_defaults(func=start_trading) trade_cmd.set_defaults(func=start_trading)
self._build_args(optionlist=ARGS_TRADE, parser=trade_cmd) self._build_args(optionlist=ARGS_TRADE, parser=trade_cmd)
# Add backtesting subcommand # Add backtesting subcommand
backtesting_cmd = subparsers.add_parser('backtesting', backtesting_cmd = subparsers.add_parser('backtesting', help='Backtesting module.',
help='Backtesting module.',
parents=[_common_parser, _strategy_parser]) parents=[_common_parser, _strategy_parser])
backtesting_cmd.set_defaults(func=start_backtesting) backtesting_cmd.set_defaults(func=start_backtesting)
self._build_args(optionlist=ARGS_BACKTEST, parser=backtesting_cmd) self._build_args(optionlist=ARGS_BACKTEST, parser=backtesting_cmd)
# Add edge subcommand # Add edge subcommand
edge_cmd = subparsers.add_parser('edge', edge_cmd = subparsers.add_parser('edge', help='Edge module.',
help='Edge module.',
parents=[_common_parser, _strategy_parser]) parents=[_common_parser, _strategy_parser])
edge_cmd.set_defaults(func=start_edge) edge_cmd.set_defaults(func=start_edge)
self._build_args(optionlist=ARGS_EDGE, parser=edge_cmd) self._build_args(optionlist=ARGS_EDGE, parser=edge_cmd)
# Add hyperopt subcommand # Add hyperopt subcommand
hyperopt_cmd = subparsers.add_parser( hyperopt_cmd = subparsers.add_parser('hyperopt', help='Hyperopt module.',
'hyperopt',
help='Hyperopt module.',
parents=[_common_parser, _strategy_parser], parents=[_common_parser, _strategy_parser],
) )
hyperopt_cmd.set_defaults(func=start_hyperopt) hyperopt_cmd.set_defaults(func=start_hyperopt)
self._build_args(optionlist=ARGS_HYPEROPT, parser=hyperopt_cmd) self._build_args(optionlist=ARGS_HYPEROPT, parser=hyperopt_cmd)
# add create-userdir subcommand # add create-userdir subcommand
create_userdir_cmd = subparsers.add_parser( create_userdir_cmd = subparsers.add_parser('create-userdir',
'create-userdir',
help="Create user-data directory.", help="Create user-data directory.",
) )
create_userdir_cmd.set_defaults(func=start_create_userdir) create_userdir_cmd.set_defaults(func=start_create_userdir)
self._build_args(optionlist=ARGS_CREATE_USERDIR, parser=create_userdir_cmd) self._build_args(optionlist=ARGS_CREATE_USERDIR, parser=create_userdir_cmd)
# add new-config subcommand # add new-config subcommand
build_config_cmd = subparsers.add_parser('new-config', help="Create new config") build_config_cmd = subparsers.add_parser('new-config',
help="Create new config")
build_config_cmd.set_defaults(func=start_new_config) build_config_cmd.set_defaults(func=start_new_config)
self._build_args(optionlist=ARGS_BUILD_CONFIG, parser=build_config_cmd) self._build_args(optionlist=ARGS_BUILD_CONFIG, parser=build_config_cmd)
# add new-strategy subcommand # add new-strategy subcommand
build_strategy_cmd = subparsers.add_parser('new-strategy', help="Create new strategy") build_strategy_cmd = subparsers.add_parser('new-strategy',
help="Create new strategy")
build_strategy_cmd.set_defaults(func=start_new_strategy) build_strategy_cmd.set_defaults(func=start_new_strategy)
self._build_args(optionlist=ARGS_BUILD_STRATEGY, parser=build_strategy_cmd) self._build_args(optionlist=ARGS_BUILD_STRATEGY, parser=build_strategy_cmd)
# add new-hyperopt subcommand # add new-hyperopt subcommand
build_hyperopt_cmd = subparsers.add_parser('new-hyperopt', help="Create new hyperopt") build_hyperopt_cmd = subparsers.add_parser('new-hyperopt',
help="Create new hyperopt")
build_hyperopt_cmd.set_defaults(func=start_new_hyperopt) build_hyperopt_cmd.set_defaults(func=start_new_hyperopt)
self._build_args(optionlist=ARGS_BUILD_HYPEROPT, parser=build_hyperopt_cmd) self._build_args(optionlist=ARGS_BUILD_HYPEROPT, parser=build_hyperopt_cmd)

View File

@ -13,7 +13,8 @@ def check_int_positive(value: str) -> int:
raise ValueError raise ValueError
except ValueError: except ValueError:
raise ArgumentTypeError( raise ArgumentTypeError(
f"{value} is invalid for this parameter, should be a positive integer value") f"{value} is invalid for this parameter, should be a positive integer value"
)
return uint return uint
@ -24,7 +25,8 @@ def check_int_nonzero(value: str) -> int:
raise ValueError raise ValueError
except ValueError: except ValueError:
raise ArgumentTypeError( raise ArgumentTypeError(
f"{value} is invalid for this parameter, should be a non-zero integer value") f"{value} is invalid for this parameter, should be a non-zero integer value"
)
return uint return uint
@ -38,32 +40,25 @@ class Arg:
# List of available command line options # List of available command line options
AVAILABLE_CLI_OPTIONS = { AVAILABLE_CLI_OPTIONS = {
# Common options # Common options
"verbosity": "verbosity": Arg(
Arg( '-v', '--verbose',
'-v',
'--verbose',
help='Verbose mode (-vv for more, -vvv to get all messages).', help='Verbose mode (-vv for more, -vvv to get all messages).',
action='count', action='count',
default=0, default=0,
), ),
"logfile": "logfile": Arg(
Arg(
'--logfile', '--logfile',
help="Log to the file specified. Special values are: 'syslog', 'journald'. " help="Log to the file specified. Special values are: 'syslog', 'journald'. "
"See the documentation for more details.", "See the documentation for more details.",
metavar='FILE', metavar='FILE',
), ),
"version": "version": Arg(
Arg( '-V', '--version',
'-V',
'--version',
action='version', action='version',
version=f'%(prog)s {__version__}', version=f'%(prog)s {__version__}',
), ),
"config": "config": Arg(
Arg( '-c', '--config',
'-c',
'--config',
help=f'Specify configuration file (default: `userdir/{constants.DEFAULT_CONFIG}` ' help=f'Specify configuration file (default: `userdir/{constants.DEFAULT_CONFIG}` '
f'or `config.json` whichever exists). ' f'or `config.json` whichever exists). '
f'Multiple --config options may be used. ' f'Multiple --config options may be used. '
@ -71,105 +66,84 @@ AVAILABLE_CLI_OPTIONS = {
action='append', action='append',
metavar='PATH', metavar='PATH',
), ),
"datadir": "datadir": Arg(
Arg( '-d', '--datadir',
'-d',
'--datadir',
help='Path to directory with historical backtesting data.', help='Path to directory with historical backtesting data.',
metavar='PATH', metavar='PATH',
), ),
"user_data_dir": "user_data_dir": Arg(
Arg( '--userdir', '--user-data-dir',
'--userdir',
'--user-data-dir',
help='Path to userdata directory.', help='Path to userdata directory.',
metavar='PATH', metavar='PATH',
), ),
"reset": "reset": Arg(
Arg(
'--reset', '--reset',
help='Reset sample files to their original state.', help='Reset sample files to their original state.',
action='store_true', action='store_true',
), ),
# Main options # Main options
"strategy": "strategy": Arg(
Arg( '-s', '--strategy',
'-s',
'--strategy',
help='Specify strategy class name which will be used by the bot.', help='Specify strategy class name which will be used by the bot.',
metavar='NAME', metavar='NAME',
), ),
"strategy_path": "strategy_path": Arg(
Arg(
'--strategy-path', '--strategy-path',
help='Specify additional strategy lookup path.', help='Specify additional strategy lookup path.',
metavar='PATH', metavar='PATH',
), ),
"db_url": "db_url": Arg(
Arg(
'--db-url', '--db-url',
help=f'Override trades database URL, this is useful in custom deployments ' help=f'Override trades database URL, this is useful in custom deployments '
f'(default: `{constants.DEFAULT_DB_PROD_URL}` for Live Run mode, ' f'(default: `{constants.DEFAULT_DB_PROD_URL}` for Live Run mode, '
f'`{constants.DEFAULT_DB_DRYRUN_URL}` for Dry Run).', f'`{constants.DEFAULT_DB_DRYRUN_URL}` for Dry Run).',
metavar='PATH', metavar='PATH',
), ),
"sd_notify": "sd_notify": Arg(
Arg(
'--sd-notify', '--sd-notify',
help='Notify systemd service manager.', help='Notify systemd service manager.',
action='store_true', action='store_true',
), ),
"dry_run": "dry_run": Arg(
Arg(
'--dry-run', '--dry-run',
help='Enforce dry-run for trading (removes Exchange secrets and simulates trades).', help='Enforce dry-run for trading (removes Exchange secrets and simulates trades).',
action='store_true', action='store_true',
), ),
# Optimize common # Optimize common
"ticker_interval": "ticker_interval": Arg(
Arg( '-i', '--ticker-interval',
'-i',
'--ticker-interval',
help='Specify ticker interval (`1m`, `5m`, `30m`, `1h`, `1d`).', help='Specify ticker interval (`1m`, `5m`, `30m`, `1h`, `1d`).',
), ),
"timerange": "timerange": Arg(
Arg(
'--timerange', '--timerange',
help='Specify what timerange of data to use.', help='Specify what timerange of data to use.',
), ),
"max_open_trades": "max_open_trades": Arg(
Arg(
'--max-open-trades', '--max-open-trades',
help='Override the value of the `max_open_trades` configuration setting.', help='Override the value of the `max_open_trades` configuration setting.',
type=int, type=int,
metavar='INT', metavar='INT',
), ),
"stake_amount": "stake_amount": Arg(
Arg(
'--stake-amount', '--stake-amount',
help='Override the value of the `stake_amount` configuration setting.', help='Override the value of the `stake_amount` configuration setting.',
type=float, type=float,
), ),
# Backtesting # Backtesting
"position_stacking": "position_stacking": Arg(
Arg( '--eps', '--enable-position-stacking',
'--eps',
'--enable-position-stacking',
help='Allow buying the same pair multiple times (position stacking).', help='Allow buying the same pair multiple times (position stacking).',
action='store_true', action='store_true',
default=False, default=False,
), ),
"use_max_market_positions": "use_max_market_positions": Arg(
Arg( '--dmmp', '--disable-max-market-positions',
'--dmmp',
'--disable-max-market-positions',
help='Disable applying `max_open_trades` during backtest ' help='Disable applying `max_open_trades` during backtest '
'(same as setting `max_open_trades` to a very high number).', '(same as setting `max_open_trades` to a very high number).',
action='store_false', action='store_false',
default=True, default=True,
), ),
"strategy_list": "strategy_list": Arg(
Arg(
'--strategy-list', '--strategy-list',
help='Provide a space-separated list of strategies to backtest. ' help='Provide a space-separated list of strategies to backtest. '
'Please note that ticker-interval needs to be set either in config ' 'Please note that ticker-interval needs to be set either in config '
@ -178,52 +152,44 @@ AVAILABLE_CLI_OPTIONS = {
'(so `backtest-data.json` becomes `backtest-data-DefaultStrategy.json`', '(so `backtest-data.json` becomes `backtest-data-DefaultStrategy.json`',
nargs='+', nargs='+',
), ),
"export": "export": Arg(
Arg(
'--export', '--export',
help='Export backtest results, argument are: trades. ' help='Export backtest results, argument are: trades. '
'Example: `--export=trades`', 'Example: `--export=trades`',
), ),
"exportfilename": "exportfilename": Arg(
Arg(
'--export-filename', '--export-filename',
help='Save backtest results to the file with this filename. ' help='Save backtest results to the file with this filename. '
'Requires `--export` to be set as well. ' 'Requires `--export` to be set as well. '
'Example: `--export-filename=user_data/backtest_results/backtest_today.json`', 'Example: `--export-filename=user_data/backtest_results/backtest_today.json`',
metavar='PATH', metavar='PATH',
), ),
"fee": "fee": Arg(
Arg(
'--fee', '--fee',
help='Specify fee ratio. Will be applied twice (on trade entry and exit).', help='Specify fee ratio. Will be applied twice (on trade entry and exit).',
type=float, type=float,
metavar='FLOAT', metavar='FLOAT',
), ),
# Edge # Edge
"stoploss_range": "stoploss_range": Arg(
Arg(
'--stoplosses', '--stoplosses',
help='Defines a range of stoploss values against which edge will assess the strategy. ' help='Defines a range of stoploss values against which edge will assess the strategy. '
'The format is "min,max,step" (without any space). ' 'The format is "min,max,step" (without any space). '
'Example: `--stoplosses=-0.01,-0.1,-0.001`', 'Example: `--stoplosses=-0.01,-0.1,-0.001`',
), ),
# Hyperopt # Hyperopt
"hyperopt": "hyperopt": Arg(
Arg(
'--hyperopt', '--hyperopt',
help='Specify hyperopt class name which will be used by the bot.', help='Specify hyperopt class name which will be used by the bot.',
metavar='NAME', metavar='NAME',
), ),
"hyperopt_path": "hyperopt_path": Arg(
Arg(
'--hyperopt-path', '--hyperopt-path',
help='Specify additional lookup path for Hyperopt and Hyperopt Loss functions.', help='Specify additional lookup path for Hyperopt and Hyperopt Loss functions.',
metavar='PATH', metavar='PATH',
), ),
"epochs": "epochs": Arg(
Arg( '-e', '--epochs',
'-e',
'--epochs',
help='Specify number of epochs (default: %(default)d).', help='Specify number of epochs (default: %(default)d).',
type=check_int_positive, type=check_int_positive,
metavar='INT', metavar='INT',
@ -260,32 +226,27 @@ AVAILABLE_CLI_OPTIONS = {
nargs='+', nargs='+',
default='default', default='default',
), ),
"print_all": "print_all": Arg(
Arg(
'--print-all', '--print-all',
help='Print all results, not only the best ones.', help='Print all results, not only the best ones.',
action='store_true', action='store_true',
default=False, default=False,
), ),
"print_colorized": "print_colorized": Arg(
Arg(
'--no-color', '--no-color',
help='Disable colorization of hyperopt results. May be useful if you are ' help='Disable colorization of hyperopt results. May be useful if you are '
'redirecting output to a file.', 'redirecting output to a file.',
action='store_false', action='store_false',
default=True, default=True,
), ),
"print_json": "print_json": Arg(
Arg(
'--print-json', '--print-json',
help='Print best result detailization in JSON format.', help='Print best result detailization in JSON format.',
action='store_true', action='store_true',
default=False, default=False,
), ),
"hyperopt_jobs": "hyperopt_jobs": Arg(
Arg( '-j', '--job-workers',
'-j',
'--job-workers',
help='The number of concurrently running jobs for hyperoptimization ' help='The number of concurrently running jobs for hyperoptimization '
'(hyperopt worker processes). ' '(hyperopt worker processes). '
'If -1 (default), all CPUs are used, for -2, all CPUs but one are used, etc. ' 'If -1 (default), all CPUs are used, for -2, all CPUs but one are used, etc. '
@ -294,15 +255,13 @@ AVAILABLE_CLI_OPTIONS = {
metavar='JOBS', metavar='JOBS',
default=-1, default=-1,
), ),
"hyperopt_random_state": "hyperopt_random_state": Arg(
Arg(
'--random-state', '--random-state',
help='Set random state to some positive integer for reproducible hyperopt results.', help='Set random state to some positive integer for reproducible hyperopt results.',
type=check_int_positive, type=check_int_positive,
metavar='INT', metavar='INT',
), ),
"hyperopt_min_trades": "hyperopt_min_trades": Arg(
Arg(
'--min-trades', '--min-trades',
help="Set minimal desired number of trades for evaluations in the hyperopt " help="Set minimal desired number of trades for evaluations in the hyperopt "
"optimization path (default: 1).", "optimization path (default: 1).",
@ -310,16 +269,14 @@ AVAILABLE_CLI_OPTIONS = {
metavar='INT', metavar='INT',
default=1, default=1,
), ),
"hyperopt_continue": "hyperopt_continue": Arg(
Arg(
"--continue", "--continue",
help="Continue hyperopt from previous runs. " help="Continue hyperopt from previous runs. "
"By default, temporary files will be removed and hyperopt will start from scratch.", "By default, temporary files will be removed and hyperopt will start from scratch.",
default=False, default=False,
action='store_true', action='store_true',
), ),
"hyperopt_loss": "hyperopt_loss": Arg(
Arg(
'--hyperopt-loss', '--hyperopt-loss',
help='Specify the class name of the hyperopt loss function class (IHyperOptLoss). ' help='Specify the class name of the hyperopt loss function class (IHyperOptLoss). '
'Different functions can generate completely different results, ' 'Different functions can generate completely different results, '
@ -331,143 +288,121 @@ AVAILABLE_CLI_OPTIONS = {
default=constants.DEFAULT_HYPEROPT_LOSS, default=constants.DEFAULT_HYPEROPT_LOSS,
), ),
# List exchanges # List exchanges
"print_one_column": "print_one_column": Arg(
Arg( '-1', '--one-column',
'-1',
'--one-column',
help='Print output in one column.', help='Print output in one column.',
action='store_true', action='store_true',
), ),
"list_exchanges_all": "list_exchanges_all": Arg(
Arg( '-a', '--all',
'-a',
'--all',
help='Print all exchanges known to the ccxt library.', help='Print all exchanges known to the ccxt library.',
action='store_true', action='store_true',
), ),
# List pairs / markets # List pairs / markets
"list_pairs_all": "list_pairs_all": Arg(
Arg( '-a', '--all',
'-a',
'--all',
help='Print all pairs or market symbols. By default only active ' help='Print all pairs or market symbols. By default only active '
'ones are shown.', 'ones are shown.',
action='store_true', action='store_true',
), ),
"print_list": "print_list": Arg(
Arg(
'--print-list', '--print-list',
help='Print list of pairs or market symbols. By default data is ' help='Print list of pairs or market symbols. By default data is '
'printed in the tabular format.', 'printed in the tabular format.',
action='store_true', action='store_true',
), ),
"list_pairs_print_json": "list_pairs_print_json": Arg(
Arg(
'--print-json', '--print-json',
help='Print list of pairs or market symbols in JSON format.', help='Print list of pairs or market symbols in JSON format.',
action='store_true', action='store_true',
default=False, default=False,
), ),
"print_csv": "print_csv": Arg(
Arg(
'--print-csv', '--print-csv',
help='Print exchange pair or market data in the csv format.', help='Print exchange pair or market data in the csv format.',
action='store_true', action='store_true',
), ),
"quote_currencies": "quote_currencies": Arg(
Arg(
'--quote', '--quote',
help='Specify quote currency(-ies). Space-separated list.', help='Specify quote currency(-ies). Space-separated list.',
nargs='+', nargs='+',
metavar='QUOTE_CURRENCY', metavar='QUOTE_CURRENCY',
), ),
"base_currencies": "base_currencies": Arg(
Arg(
'--base', '--base',
help='Specify base currency(-ies). Space-separated list.', help='Specify base currency(-ies). Space-separated list.',
nargs='+', nargs='+',
metavar='BASE_CURRENCY', metavar='BASE_CURRENCY',
), ),
# Script options # Script options
"pairs": "pairs": Arg(
Arg( '-p', '--pairs',
'-p',
'--pairs',
help='Show profits for only these pairs. Pairs are space-separated.', help='Show profits for only these pairs. Pairs are space-separated.',
nargs='+', nargs='+',
), ),
# Download data # Download data
"pairs_file": "pairs_file": Arg(
Arg(
'--pairs-file', '--pairs-file',
help='File containing a list of pairs to download.', help='File containing a list of pairs to download.',
metavar='FILE', metavar='FILE',
), ),
"days": "days": Arg(
Arg(
'--days', '--days',
help='Download data for given number of days.', help='Download data for given number of days.',
type=check_int_positive, type=check_int_positive,
metavar='INT', metavar='INT',
), ),
"download_trades": "download_trades": Arg(
Arg(
'--dl-trades', '--dl-trades',
help='Download trades instead of OHLCV data. The bot will resample trades to the ' help='Download trades instead of OHLCV data. The bot will resample trades to the '
'desired timeframe as specified as --timeframes/-t.', 'desired timeframe as specified as --timeframes/-t.',
action='store_true', action='store_true',
), ),
"format_from": "format_from": Arg(
Arg(
'--format-from', '--format-from',
help='Source format for data conversion.', help='Source format for data conversion.',
choices=constants.AVAILABLE_DATAHANDLERS, choices=constants.AVAILABLE_DATAHANDLERS,
required=True, required=True,
), ),
"format_to": "format_to": Arg(
Arg(
'--format-to', '--format-to',
help='Destination format for data conversion.', help='Destination format for data conversion.',
choices=constants.AVAILABLE_DATAHANDLERS, choices=constants.AVAILABLE_DATAHANDLERS,
required=True, required=True,
), ),
"dataformat_ohlcv": "dataformat_ohlcv": Arg(
Arg('--data-format-ohlcv', '--data-format-ohlcv',
help='Storage format for downloaded ohlcv data. (default: `%(default)s`).', help='Storage format for downloaded ohlcv data. (default: `%(default)s`).',
choices=constants.AVAILABLE_DATAHANDLERS, choices=constants.AVAILABLE_DATAHANDLERS,
default='json'), default='json'
"dataformat_trades": ),
Arg('--data-format-trades', "dataformat_trades": Arg(
'--data-format-trades',
help='Storage format for downloaded trades data. (default: `%(default)s`).', help='Storage format for downloaded trades data. (default: `%(default)s`).',
choices=constants.AVAILABLE_DATAHANDLERS, choices=constants.AVAILABLE_DATAHANDLERS,
default='jsongz'), default='jsongz'
"exchange": ),
Arg( "exchange": Arg(
'--exchange', '--exchange',
help=f'Exchange name (default: `{constants.DEFAULT_EXCHANGE}`). ' help=f'Exchange name (default: `{constants.DEFAULT_EXCHANGE}`). '
f'Only valid if no config is provided.', f'Only valid if no config is provided.',
), ),
"timeframes": "timeframes": Arg(
Arg( '-t', '--timeframes',
'-t',
'--timeframes',
help=f'Specify which tickers to download. Space-separated list. ' help=f'Specify which tickers to download. Space-separated list. '
f'Default: `1m 5m`.', f'Default: `1m 5m`.',
choices=[ choices=['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h',
'1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '1d', '3d', '1w' '6h', '8h', '12h', '1d', '3d', '1w'],
],
default=['1m', '5m'], default=['1m', '5m'],
nargs='+', nargs='+',
), ),
"erase": "erase": Arg(
Arg(
'--erase', '--erase',
help='Clean all existing data for the selected exchange/pairs/timeframes.', help='Clean all existing data for the selected exchange/pairs/timeframes.',
action='store_true', action='store_true',
), ),
# Templating options # Templating options
"template": "template": Arg(
Arg(
'--template', '--template',
help='Use a template which is either `minimal` or ' help='Use a template which is either `minimal` or '
'`full` (containing multiple sample indicators). Default: `%(default)s`.', '`full` (containing multiple sample indicators). Default: `%(default)s`.',
@ -475,22 +410,19 @@ AVAILABLE_CLI_OPTIONS = {
default='full', default='full',
), ),
# Plot dataframe # Plot dataframe
"indicators1": "indicators1": Arg(
Arg(
'--indicators1', '--indicators1',
help='Set indicators from your strategy you want in the first row of the graph. ' help='Set indicators from your strategy you want in the first row of the graph. '
"Space-separated list. Example: `ema3 ema5`. Default: `['sma', 'ema3', 'ema5']`.", "Space-separated list. Example: `ema3 ema5`. Default: `['sma', 'ema3', 'ema5']`.",
nargs='+', nargs='+',
), ),
"indicators2": "indicators2": Arg(
Arg(
'--indicators2', '--indicators2',
help='Set indicators from your strategy you want in the third row of the graph. ' help='Set indicators from your strategy you want in the third row of the graph. '
"Space-separated list. Example: `fastd fastk`. Default: `['macd', 'macdsignal']`.", "Space-separated list. Example: `fastd fastk`. Default: `['macd', 'macdsignal']`.",
nargs='+', nargs='+',
), ),
"plot_limit": "plot_limit": Arg(
Arg(
'--plot-limit', '--plot-limit',
help='Specify tick limit for plotting. Notice: too high values cause huge files. ' help='Specify tick limit for plotting. Notice: too high values cause huge files. '
'Default: %(default)s.', 'Default: %(default)s.',
@ -498,8 +430,7 @@ AVAILABLE_CLI_OPTIONS = {
metavar='INT', metavar='INT',
default=750, default=750,
), ),
"trade_source": "trade_source": Arg(
Arg(
'--trade-source', '--trade-source',
help='Specify the source for trades (Can be DB or file (backtest file)) ' help='Specify the source for trades (Can be DB or file (backtest file)) '
'Default: %(default)s', 'Default: %(default)s',
@ -507,90 +438,76 @@ AVAILABLE_CLI_OPTIONS = {
default="file", default="file",
), ),
# hyperopt-list, hyperopt-show # hyperopt-list, hyperopt-show
"hyperopt_list_profitable": "hyperopt_list_profitable": Arg(
Arg(
'--profitable', '--profitable',
help='Select only profitable epochs.', help='Select only profitable epochs.',
action='store_true', action='store_true',
), ),
"hyperopt_list_best": "hyperopt_list_best": Arg(
Arg(
'--best', '--best',
help='Select only best epochs.', help='Select only best epochs.',
action='store_true', action='store_true',
), ),
"hyperopt_list_min_trades": "hyperopt_list_min_trades": Arg(
Arg(
'--min-trades', '--min-trades',
help='Select epochs with more than INT trades.', help='Select epochs with more than INT trades.',
type=check_int_positive, type=check_int_positive,
metavar='INT', metavar='INT',
), ),
"hyperopt_list_max_trades": "hyperopt_list_max_trades": Arg(
Arg(
'--max-trades', '--max-trades',
help='Select epochs with less than INT trades.', help='Select epochs with less than INT trades.',
type=check_int_positive, type=check_int_positive,
metavar='INT', metavar='INT',
), ),
"hyperopt_list_min_avg_time": "hyperopt_list_min_avg_time": Arg(
Arg(
'--min-avg-time', '--min-avg-time',
help='Select epochs on above average time.', help='Select epochs on above average time.',
type=float, type=float,
metavar='FLOAT', metavar='FLOAT',
), ),
"hyperopt_list_max_avg_time": "hyperopt_list_max_avg_time": Arg(
Arg(
'--max-avg-time', '--max-avg-time',
help='Select epochs on under average time.', help='Select epochs on under average time.',
type=float, type=float,
metavar='FLOAT', metavar='FLOAT',
), ),
"hyperopt_list_min_avg_profit": "hyperopt_list_min_avg_profit": Arg(
Arg(
'--min-avg-profit', '--min-avg-profit',
help='Select epochs on above average profit.', help='Select epochs on above average profit.',
type=float, type=float,
metavar='FLOAT', metavar='FLOAT',
), ),
"hyperopt_list_max_avg_profit": "hyperopt_list_max_avg_profit": Arg(
Arg(
'--max-avg-profit', '--max-avg-profit',
help='Select epochs on below average profit.', help='Select epochs on below average profit.',
type=float, type=float,
metavar='FLOAT', metavar='FLOAT',
), ),
"hyperopt_list_min_total_profit": "hyperopt_list_min_total_profit": Arg(
Arg(
'--min-total-profit', '--min-total-profit',
help='Select epochs on above total profit.', help='Select epochs on above total profit.',
type=float, type=float,
metavar='FLOAT', metavar='FLOAT',
), ),
"hyperopt_list_max_total_profit": "hyperopt_list_max_total_profit": Arg(
Arg(
'--max-total-profit', '--max-total-profit',
help='Select epochs on below total profit.', help='Select epochs on below total profit.',
type=float, type=float,
metavar='FLOAT', metavar='FLOAT',
), ),
"hyperopt_list_no_details": "hyperopt_list_no_details": Arg(
Arg(
'--no-details', '--no-details',
help='Do not print best epoch details.', help='Do not print best epoch details.',
action='store_true', action='store_true',
), ),
"hyperopt_show_index": "hyperopt_show_index": Arg(
Arg( '-n', '--index',
'-n',
'--index',
help='Specify the index of the epoch to print details for.', help='Specify the index of the epoch to print details for.',
type=check_int_nonzero, type=check_int_nonzero,
metavar='INT', metavar='INT',
), ),
"hyperopt_show_no_header": "hyperopt_show_no_header": Arg(
Arg(
'--no-header', '--no-header',
help='Do not print epoch details header.', help='Do not print epoch details header.',
action='store_true', action='store_true',

View File

@ -10,7 +10,8 @@ from typing import Any, Callable, Dict, List, Optional
from freqtrade import constants from freqtrade import constants
from freqtrade.configuration.check_exchange import check_exchange from freqtrade.configuration.check_exchange import check_exchange
from freqtrade.configuration.deprecated_settings import process_temporary_deprecated_settings from freqtrade.configuration.deprecated_settings import process_temporary_deprecated_settings
from freqtrade.configuration.directory_operations import (create_datadir, create_userdata_dir) from freqtrade.configuration.directory_operations import (create_datadir,
create_userdata_dir)
from freqtrade.configuration.load_config import load_config_file from freqtrade.configuration.load_config import load_config_file
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.loggers import setup_logging from freqtrade.loggers import setup_logging
@ -25,6 +26,7 @@ class Configuration:
Class to read and init the bot configuration Class to read and init the bot configuration
Reuse this class for the bot, backtesting, hyperopt and every script that required configuration Reuse this class for the bot, backtesting, hyperopt and every script that required configuration
""" """
def __init__(self, args: Dict[str, Any], runmode: RunMode = None) -> None: def __init__(self, args: Dict[str, Any], runmode: RunMode = None) -> None:
self.args = args self.args = args
self.config: Optional[Dict[str, Any]] = None self.config: Optional[Dict[str, Any]] = None
@ -150,12 +152,11 @@ class Configuration:
if self.args.get("strategy") or not config.get('strategy'): if self.args.get("strategy") or not config.get('strategy'):
config.update({'strategy': self.args.get("strategy")}) config.update({'strategy': self.args.get("strategy")})
self._args_to_config(config, self._args_to_config(config, argname='strategy_path',
argname='strategy_path',
logstring='Using additional Strategy lookup path: {}') logstring='Using additional Strategy lookup path: {}')
if ('db_url' in self.args and self.args["db_url"] if ('db_url' in self.args and self.args["db_url"] and
and self.args["db_url"] != constants.DEFAULT_DB_PROD_URL): self.args["db_url"] != constants.DEFAULT_DB_PROD_URL):
config.update({'db_url': self.args["db_url"]}) config.update({'db_url': self.args["db_url"]})
logger.info('Parameter --db-url detected ...') logger.info('Parameter --db-url detected ...')
@ -193,23 +194,20 @@ class Configuration:
logger.info('Using data directory: %s ...', config.get('datadir')) logger.info('Using data directory: %s ...', config.get('datadir'))
if self.args.get('exportfilename'): if self.args.get('exportfilename'):
self._args_to_config(config, self._args_to_config(config, argname='exportfilename',
argname='exportfilename',
logstring='Storing backtest results to {} ...') logstring='Storing backtest results to {} ...')
else: else:
config['exportfilename'] = (config['user_data_dir'] / config['exportfilename'] = (config['user_data_dir']
'backtest_results/backtest-result.json') / 'backtest_results/backtest-result.json')
def _process_optimize_options(self, config: Dict[str, Any]) -> None: def _process_optimize_options(self, config: Dict[str, Any]) -> None:
# This will override the strategy configuration # This will override the strategy configuration
self._args_to_config(config, self._args_to_config(config, argname='ticker_interval',
argname='ticker_interval',
logstring='Parameter -i/--ticker-interval detected ... ' logstring='Parameter -i/--ticker-interval detected ... '
'Using ticker_interval: {} ...') 'Using ticker_interval: {} ...')
self._args_to_config(config, self._args_to_config(config, argname='position_stacking',
argname='position_stacking',
logstring='Parameter --enable-position-stacking detected ...') logstring='Parameter --enable-position-stacking detected ...')
# Setting max_open_trades to infinite if -1 # Setting max_open_trades to infinite if -1
@ -222,39 +220,31 @@ class Configuration:
logger.info('max_open_trades set to unlimited ...') logger.info('max_open_trades set to unlimited ...')
elif 'max_open_trades' in self.args and self.args["max_open_trades"]: elif 'max_open_trades' in self.args and self.args["max_open_trades"]:
config.update({'max_open_trades': self.args["max_open_trades"]}) config.update({'max_open_trades': self.args["max_open_trades"]})
logger.info( logger.info('Parameter --max-open-trades detected, '
'Parameter --max-open-trades detected, '
'overriding max_open_trades to: %s ...', config.get('max_open_trades')) 'overriding max_open_trades to: %s ...', config.get('max_open_trades'))
elif config['runmode'] in NON_UTIL_MODES: elif config['runmode'] in NON_UTIL_MODES:
logger.info('Using max_open_trades: %s ...', config.get('max_open_trades')) logger.info('Using max_open_trades: %s ...', config.get('max_open_trades'))
self._args_to_config(config, self._args_to_config(config, argname='stake_amount',
argname='stake_amount',
logstring='Parameter --stake-amount detected, ' logstring='Parameter --stake-amount detected, '
'overriding stake_amount to: {} ...') 'overriding stake_amount to: {} ...')
self._args_to_config(config, self._args_to_config(config, argname='fee',
argname='fee',
logstring='Parameter --fee detected, ' logstring='Parameter --fee detected, '
'setting fee to: {} ...') 'setting fee to: {} ...')
self._args_to_config(config, self._args_to_config(config, argname='timerange',
argname='timerange',
logstring='Parameter --timerange detected: {} ...') logstring='Parameter --timerange detected: {} ...')
self._process_datadir_options(config) self._process_datadir_options(config)
self._args_to_config(config, self._args_to_config(config, argname='strategy_list',
argname='strategy_list', logstring='Using strategy list of {} strategies', logfun=len)
logstring='Using strategy list of {} strategies',
logfun=len)
self._args_to_config(config, self._args_to_config(config, argname='ticker_interval',
argname='ticker_interval',
logstring='Overriding ticker interval with Command line argument') logstring='Overriding ticker interval with Command line argument')
self._args_to_config(config, self._args_to_config(config, argname='export',
argname='export',
logstring='Parameter --export detected: {} ...') logstring='Parameter --export detected: {} ...')
# Edge section: # Edge section:
@ -266,14 +256,13 @@ class Configuration:
logger.info('Parameter --stoplosses detected: %s ...', self.args["stoploss_range"]) logger.info('Parameter --stoplosses detected: %s ...', self.args["stoploss_range"])
# Hyperopt section # Hyperopt section
self._args_to_config(config, argname='hyperopt', logstring='Using Hyperopt class name: {}') self._args_to_config(config, argname='hyperopt',
logstring='Using Hyperopt class name: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_path',
argname='hyperopt_path',
logstring='Using additional Hyperopt lookup path: {}') logstring='Using additional Hyperopt lookup path: {}')
self._args_to_config(config, self._args_to_config(config, argname='epochs',
argname='epochs',
logstring='Parameter --epochs detected ... ' logstring='Parameter --epochs detected ... '
'Will run Hyperopt with for {} epochs ...') 'Will run Hyperopt with for {} epochs ...')
self._args_to_config(config, self._args_to_config(config,
@ -290,8 +279,7 @@ class Configuration:
argname='spaces', argname='spaces',
logstring='Parameter -s/--spaces detected: {}') logstring='Parameter -s/--spaces detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='print_all',
argname='print_all',
logstring='Parameter --print-all detected ...') logstring='Parameter --print-all detected ...')
if 'print_colorized' in self.args and not self.args["print_colorized"]: if 'print_colorized' in self.args and not self.args["print_colorized"]:
@ -300,115 +288,100 @@ class Configuration:
else: else:
config.update({'print_colorized': True}) config.update({'print_colorized': True})
self._args_to_config(config, self._args_to_config(config, argname='print_json',
argname='print_json',
logstring='Parameter --print-json detected ...') logstring='Parameter --print-json detected ...')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_jobs',
argname='hyperopt_jobs',
logstring='Parameter -j/--job-workers detected: {}') logstring='Parameter -j/--job-workers detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_random_state',
argname='hyperopt_random_state',
logstring='Parameter --random-state detected: {}') logstring='Parameter --random-state detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_min_trades',
argname='hyperopt_min_trades',
logstring='Parameter --min-trades detected: {}') logstring='Parameter --min-trades detected: {}')
self._args_to_config(config, argname='hyperopt_continue', logstring='Hyperopt continue: {}') self._args_to_config(config, argname='hyperopt_continue',
logstring='Hyperopt continue: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_loss',
argname='hyperopt_loss',
logstring='Using Hyperopt loss class name: {}') logstring='Using Hyperopt loss class name: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_show_index',
argname='hyperopt_show_index',
logstring='Parameter -n/--index detected: {}') logstring='Parameter -n/--index detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_list_best',
argname='hyperopt_list_best',
logstring='Parameter --best detected: {}') logstring='Parameter --best detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_list_profitable',
argname='hyperopt_list_profitable',
logstring='Parameter --profitable detected: {}') logstring='Parameter --profitable detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_list_min_trades',
argname='hyperopt_list_min_trades',
logstring='Parameter --min-trades detected: {}') logstring='Parameter --min-trades detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_list_max_trades',
argname='hyperopt_list_max_trades',
logstring='Parameter --max-trades detected: {}') logstring='Parameter --max-trades detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_list_min_avg_time',
argname='hyperopt_list_min_avg_time',
logstring='Parameter --min-avg-time detected: {}') logstring='Parameter --min-avg-time detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_list_max_avg_time',
argname='hyperopt_list_max_avg_time',
logstring='Parameter --max-avg-time detected: {}') logstring='Parameter --max-avg-time detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_list_min_avg_profit',
argname='hyperopt_list_min_avg_profit',
logstring='Parameter --min-avg-profit detected: {}') logstring='Parameter --min-avg-profit detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_list_max_avg_profit',
argname='hyperopt_list_max_avg_profit',
logstring='Parameter --max-avg-profit detected: {}') logstring='Parameter --max-avg-profit detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_list_min_total_profit',
argname='hyperopt_list_min_total_profit',
logstring='Parameter --min-total-profit detected: {}') logstring='Parameter --min-total-profit detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_list_max_total_profit',
argname='hyperopt_list_max_total_profit',
logstring='Parameter --max-total-profit detected: {}') logstring='Parameter --max-total-profit detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_list_no_details',
argname='hyperopt_list_no_details',
logstring='Parameter --no-details detected: {}') logstring='Parameter --no-details detected: {}')
self._args_to_config(config, self._args_to_config(config, argname='hyperopt_show_no_header',
argname='hyperopt_show_no_header',
logstring='Parameter --no-header detected: {}') logstring='Parameter --no-header detected: {}')
def _process_plot_options(self, config: Dict[str, Any]) -> None: def _process_plot_options(self, config: Dict[str, Any]) -> None:
self._args_to_config(config, argname='pairs', logstring='Using pairs {}') self._args_to_config(config, argname='pairs',
logstring='Using pairs {}')
self._args_to_config(config, argname='indicators1', logstring='Using indicators1: {}') self._args_to_config(config, argname='indicators1',
logstring='Using indicators1: {}')
self._args_to_config(config, argname='indicators2', logstring='Using indicators2: {}') self._args_to_config(config, argname='indicators2',
logstring='Using indicators2: {}')
self._args_to_config(config, argname='plot_limit', logstring='Limiting plot to: {}') self._args_to_config(config, argname='plot_limit',
self._args_to_config(config, argname='trade_source', logstring='Using trades from: {}') logstring='Limiting plot to: {}')
self._args_to_config(config, argname='trade_source',
logstring='Using trades from: {}')
self._args_to_config(config, self._args_to_config(config, argname='erase',
argname='erase',
logstring='Erase detected. Deleting existing data.') logstring='Erase detected. Deleting existing data.')
self._args_to_config(config, argname='timeframes', logstring='timeframes --timeframes: {}') self._args_to_config(config, argname='timeframes',
logstring='timeframes --timeframes: {}')
self._args_to_config(config, argname='days', logstring='Detected --days: {}') self._args_to_config(config, argname='days',
logstring='Detected --days: {}')
self._args_to_config(config, self._args_to_config(config, argname='download_trades',
argname='download_trades',
logstring='Detected --dl-trades: {}') logstring='Detected --dl-trades: {}')
self._args_to_config(config, self._args_to_config(config, argname='dataformat_ohlcv',
argname='dataformat_ohlcv',
logstring='Using "{}" to store OHLCV data.') logstring='Using "{}" to store OHLCV data.')
self._args_to_config(config, self._args_to_config(config, argname='dataformat_trades',
argname='dataformat_trades',
logstring='Using "{}" to store trades data.') logstring='Using "{}" to store trades data.')
def _process_runmode(self, config: Dict[str, Any]) -> None: def _process_runmode(self, config: Dict[str, Any]) -> None:
self._args_to_config(config, self._args_to_config(config, argname='dry_run',
argname='dry_run',
logstring='Parameter --dry-run detected, ' logstring='Parameter --dry-run detected, '
'overriding dry_run to: {} ...') 'overriding dry_run to: {} ...')
@ -419,11 +392,8 @@ class Configuration:
config.update({'runmode': self.runmode}) config.update({'runmode': self.runmode})
def _args_to_config(self, def _args_to_config(self, config: Dict[str, Any], argname: str,
config: Dict[str, Any], logstring: str, logfun: Optional[Callable] = None,
argname: str,
logstring: str,
logfun: Optional[Callable] = None,
deprecated_msg: Optional[str] = None) -> None: deprecated_msg: Optional[str] = None) -> None:
""" """
:param config: Configuration dictionary :param config: Configuration dictionary

View File

@ -1,4 +1,5 @@
# pragma pylint: disable=too-few-public-methods # pragma pylint: disable=too-few-public-methods
""" """
bot constants bot constants
""" """
@ -18,9 +19,8 @@ REQUIRED_ORDERTIF = ['buy', 'sell']
REQUIRED_ORDERTYPES = ['buy', 'sell', 'stoploss', 'stoploss_on_exchange'] REQUIRED_ORDERTYPES = ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']
ORDERTYPE_POSSIBILITIES = ['limit', 'market'] ORDERTYPE_POSSIBILITIES = ['limit', 'market']
ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc'] ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc']
AVAILABLE_PAIRLISTS = [ AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList',
'StaticPairList', 'VolumePairList', 'PrecisionFilter', 'PriceFilter', 'SpreadFilter' 'PrecisionFilter', 'PriceFilter', 'SpreadFilter']
]
AVAILABLE_DATAHANDLERS = ['json', 'jsongz'] AVAILABLE_DATAHANDLERS = ['json', 'jsongz']
DRY_RUN_WALLET = 1000 DRY_RUN_WALLET = 1000
MATH_CLOSE_PREC = 1e-14 # Precision used for float comparisons MATH_CLOSE_PREC = 1e-14 # Precision used for float comparisons
@ -40,9 +40,11 @@ USER_DATA_FILES = {
} }
SUPPORTED_FIAT = [ SUPPORTED_FIAT = [
"AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK", "EUR", "GBP", "HKD", "HUF", "IDR", "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK",
"ILS", "INR", "JPY", "KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN", "RUB", "SEK", "EUR", "GBP", "HKD", "HUF", "IDR", "ILS", "INR", "JPY",
"SGD", "THB", "TRY", "TWD", "ZAR", "USD", "BTC", "XBT", "ETH", "XRP", "LTC", "BCH", "USDT" "KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN",
"RUB", "SEK", "SGD", "THB", "TRY", "TWD", "ZAR", "USD",
"BTC", "XBT", "ETH", "XRP", "LTC", "BCH", "USDT"
] ]
MINIMAL_CONFIG = { MINIMAL_CONFIG = {
@ -63,16 +65,9 @@ MINIMAL_CONFIG = {
CONF_SCHEMA = { CONF_SCHEMA = {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'max_open_trades': { 'max_open_trades': {'type': ['integer', 'number'], 'minimum': -1},
'type': ['integer', 'number'], 'ticker_interval': {'type': 'string'},
'minimum': -1 'stake_currency': {'type': 'string'},
},
'ticker_interval': {
'type': 'string'
},
'stake_currency': {
'type': 'string'
},
'stake_amount': { 'stake_amount': {
'type': ['number', 'string'], 'type': ['number', 'string'],
'minimum': 0.0001, 'minimum': 0.0001,
@ -84,76 +79,32 @@ CONF_SCHEMA = {
'maximum': 1, 'maximum': 1,
'default': 0.99 'default': 0.99
}, },
'amend_last_stake_amount': { 'amend_last_stake_amount': {'type': 'boolean', 'default': False},
'type': 'boolean',
'default': False
},
'last_stake_amount_min_ratio': { 'last_stake_amount_min_ratio': {
'type': 'number', 'type': 'number', 'minimum': 0.0, 'maximum': 1.0, 'default': 0.5
'minimum': 0.0,
'maximum': 1.0,
'default': 0.5
},
'fiat_display_currency': {
'type': 'string',
'enum': SUPPORTED_FIAT
},
'dry_run': {
'type': 'boolean'
},
'dry_run_wallet': {
'type': 'number',
'default': DRY_RUN_WALLET
},
'process_only_new_candles': {
'type': 'boolean'
}, },
'fiat_display_currency': {'type': 'string', 'enum': SUPPORTED_FIAT},
'dry_run': {'type': 'boolean'},
'dry_run_wallet': {'type': 'number', 'default': DRY_RUN_WALLET},
'process_only_new_candles': {'type': 'boolean'},
'minimal_roi': { 'minimal_roi': {
'type': 'object', 'type': 'object',
'patternProperties': { 'patternProperties': {
'^[0-9.]+$': { '^[0-9.]+$': {'type': 'number'}
'type': 'number'
}
}, },
'minProperties': 1 'minProperties': 1
}, },
'amount_reserve_percent': { 'amount_reserve_percent': {'type': 'number', 'minimum': 0.0, 'maximum': 0.5},
'type': 'number', 'stoploss': {'type': 'number', 'maximum': 0, 'exclusiveMaximum': True},
'minimum': 0.0, 'trailing_stop': {'type': 'boolean'},
'maximum': 0.5 'trailing_stop_positive': {'type': 'number', 'minimum': 0, 'maximum': 1},
}, 'trailing_stop_positive_offset': {'type': 'number', 'minimum': 0, 'maximum': 1},
'stoploss': { 'trailing_only_offset_is_reached': {'type': 'boolean'},
'type': 'number',
'maximum': 0,
'exclusiveMaximum': True
},
'trailing_stop': {
'type': 'boolean'
},
'trailing_stop_positive': {
'type': 'number',
'minimum': 0,
'maximum': 1
},
'trailing_stop_positive_offset': {
'type': 'number',
'minimum': 0,
'maximum': 1
},
'trailing_only_offset_is_reached': {
'type': 'boolean'
},
'unfilledtimeout': { 'unfilledtimeout': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'buy': { 'buy': {'type': 'number', 'minimum': 1},
'type': 'number', 'sell': {'type': 'number', 'minimum': 1}
'minimum': 1
},
'sell': {
'type': 'number',
'minimum': 1
}
} }
}, },
'bid_strategy': { 'bid_strategy': {
@ -164,24 +115,13 @@ CONF_SCHEMA = {
'minimum': 0, 'minimum': 0,
'maximum': 1, 'maximum': 1,
'exclusiveMaximum': False, 'exclusiveMaximum': False,
'use_order_book': { 'use_order_book': {'type': 'boolean'},
'type': 'boolean' 'order_book_top': {'type': 'integer', 'maximum': 20, 'minimum': 1},
},
'order_book_top': {
'type': 'integer',
'maximum': 20,
'minimum': 1
},
'check_depth_of_market': { 'check_depth_of_market': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'enabled': { 'enabled': {'type': 'boolean'},
'type': 'boolean' 'bids_to_ask_delta': {'type': 'number', 'minimum': 0},
},
'bids_to_ask_delta': {
'type': 'number',
'minimum': 0
},
} }
}, },
}, },
@ -191,92 +131,43 @@ CONF_SCHEMA = {
'ask_strategy': { 'ask_strategy': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'use_order_book': { 'use_order_book': {'type': 'boolean'},
'type': 'boolean' 'order_book_min': {'type': 'integer', 'minimum': 1},
}, 'order_book_max': {'type': 'integer', 'minimum': 1, 'maximum': 50},
'order_book_min': { 'use_sell_signal': {'type': 'boolean'},
'type': 'integer', 'sell_profit_only': {'type': 'boolean'},
'minimum': 1 'ignore_roi_if_buy_signal': {'type': 'boolean'}
},
'order_book_max': {
'type': 'integer',
'minimum': 1,
'maximum': 50
},
'use_sell_signal': {
'type': 'boolean'
},
'sell_profit_only': {
'type': 'boolean'
},
'ignore_roi_if_buy_signal': {
'type': 'boolean'
}
} }
}, },
'order_types': { 'order_types': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'buy': { 'buy': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
'type': 'string', 'sell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
'enum': ORDERTYPE_POSSIBILITIES 'emergencysell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
}, 'stoploss': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
'sell': { 'stoploss_on_exchange': {'type': 'boolean'},
'type': 'string', 'stoploss_on_exchange_interval': {'type': 'number'}
'enum': ORDERTYPE_POSSIBILITIES
},
'emergencysell': {
'type': 'string',
'enum': ORDERTYPE_POSSIBILITIES
},
'stoploss': {
'type': 'string',
'enum': ORDERTYPE_POSSIBILITIES
},
'stoploss_on_exchange': {
'type': 'boolean'
},
'stoploss_on_exchange_interval': {
'type': 'number'
}
}, },
'required': ['buy', 'sell', 'stoploss', 'stoploss_on_exchange'] 'required': ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']
}, },
'order_time_in_force': { 'order_time_in_force': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'buy': { 'buy': {'type': 'string', 'enum': ORDERTIF_POSSIBILITIES},
'type': 'string', 'sell': {'type': 'string', 'enum': ORDERTIF_POSSIBILITIES}
'enum': ORDERTIF_POSSIBILITIES
},
'sell': {
'type': 'string',
'enum': ORDERTIF_POSSIBILITIES
}
}, },
'required': ['buy', 'sell'] 'required': ['buy', 'sell']
}, },
'exchange': { 'exchange': {'$ref': '#/definitions/exchange'},
'$ref': '#/definitions/exchange' 'edge': {'$ref': '#/definitions/edge'},
},
'edge': {
'$ref': '#/definitions/edge'
},
'experimental': { 'experimental': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'use_sell_signal': { 'use_sell_signal': {'type': 'boolean'},
'type': 'boolean' 'sell_profit_only': {'type': 'boolean'},
}, 'ignore_roi_if_buy_signal': {'type': 'boolean'},
'sell_profit_only': { 'block_bad_exchanges': {'type': 'boolean'}
'type': 'boolean'
},
'ignore_roi_if_buy_signal': {
'type': 'boolean'
},
'block_bad_exchanges': {
'type': 'boolean'
}
} }
}, },
'pairlists': { 'pairlists': {
@ -284,13 +175,8 @@ CONF_SCHEMA = {
'items': { 'items': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'method': { 'method': {'type': 'string', 'enum': AVAILABLE_PAIRLISTS},
'type': 'string', 'config': {'type': 'object'}
'enum': AVAILABLE_PAIRLISTS
},
'config': {
'type': 'object'
}
}, },
'required': ['method'], 'required': ['method'],
} }
@ -298,87 +184,48 @@ CONF_SCHEMA = {
'telegram': { 'telegram': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'enabled': { 'enabled': {'type': 'boolean'},
'type': 'boolean' 'token': {'type': 'string'},
}, 'chat_id': {'type': 'string'},
'token': {
'type': 'string'
},
'chat_id': {
'type': 'string'
},
}, },
'required': ['enabled', 'token', 'chat_id'] 'required': ['enabled', 'token', 'chat_id']
}, },
'webhook': { 'webhook': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'enabled': { 'enabled': {'type': 'boolean'},
'type': 'boolean' 'webhookbuy': {'type': 'object'},
}, 'webhookbuycancel': {'type': 'object'},
'webhookbuy': { 'webhooksell': {'type': 'object'},
'type': 'object' 'webhooksellcancel': {'type': 'object'},
}, 'webhookstatus': {'type': 'object'},
'webhookbuycancel': {
'type': 'object'
},
'webhooksell': {
'type': 'object'
},
'webhooksellcancel': {
'type': 'object'
},
'webhookstatus': {
'type': 'object'
},
}, },
}, },
'api_server': { 'api_server': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'enabled': { 'enabled': {'type': 'boolean'},
'type': 'boolean' 'listen_ip_address': {'format': 'ipv4'},
},
'listen_ip_address': {
'format': 'ipv4'
},
'listen_port': { 'listen_port': {
'type': 'integer', 'type': 'integer',
'minimum': 1024, 'minimum': 1024,
'maximum': 65535 'maximum': 65535
}, },
'username': { 'username': {'type': 'string'},
'type': 'string' 'password': {'type': 'string'},
},
'password': {
'type': 'string'
},
}, },
'required': ['enabled', 'listen_ip_address', 'listen_port', 'username', 'password'] 'required': ['enabled', 'listen_ip_address', 'listen_port', 'username', 'password']
}, },
'db_url': { 'db_url': {'type': 'string'},
'type': 'string' 'initial_state': {'type': 'string', 'enum': ['running', 'stopped']},
}, 'forcebuy_enable': {'type': 'boolean'},
'initial_state': {
'type': 'string',
'enum': ['running', 'stopped']
},
'forcebuy_enable': {
'type': 'boolean'
},
'internals': { 'internals': {
'type': 'object', 'type': 'object',
'default': {}, 'default': {},
'properties': { 'properties': {
'process_throttle_secs': { 'process_throttle_secs': {'type': 'integer'},
'type': 'integer' 'interval': {'type': 'integer'},
}, 'sd_notify': {'type': 'boolean'},
'interval': {
'type': 'integer'
},
'sd_notify': {
'type': 'boolean'
},
} }
}, },
'dataformat_ohlcv': { 'dataformat_ohlcv': {
@ -396,28 +243,12 @@ CONF_SCHEMA = {
'exchange': { 'exchange': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'name': { 'name': {'type': 'string'},
'type': 'string' 'sandbox': {'type': 'boolean', 'default': False},
}, 'key': {'type': 'string', 'default': ''},
'sandbox': { 'secret': {'type': 'string', 'default': ''},
'type': 'boolean', 'password': {'type': 'string', 'default': ''},
'default': False 'uid': {'type': 'string'},
},
'key': {
'type': 'string',
'default': ''
},
'secret': {
'type': 'string',
'default': ''
},
'password': {
'type': 'string',
'default': ''
},
'uid': {
'type': 'string'
},
'pair_whitelist': { 'pair_whitelist': {
'type': 'array', 'type': 'array',
'items': { 'items': {
@ -432,65 +263,29 @@ CONF_SCHEMA = {
}, },
'uniqueItems': True 'uniqueItems': True
}, },
'outdated_offset': { 'outdated_offset': {'type': 'integer', 'minimum': 1},
'type': 'integer', 'markets_refresh_interval': {'type': 'integer'},
'minimum': 1 'ccxt_config': {'type': 'object'},
}, 'ccxt_async_config': {'type': 'object'}
'markets_refresh_interval': {
'type': 'integer'
},
'ccxt_config': {
'type': 'object'
},
'ccxt_async_config': {
'type': 'object'
}
}, },
'required': ['name'] 'required': ['name']
}, },
'edge': { 'edge': {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'enabled': { 'enabled': {'type': 'boolean'},
'type': 'boolean' 'process_throttle_secs': {'type': 'integer', 'minimum': 600},
}, 'calculate_since_number_of_days': {'type': 'integer'},
'process_throttle_secs': { 'allowed_risk': {'type': 'number'},
'type': 'integer', 'capital_available_percentage': {'type': 'number'},
'minimum': 600 'stoploss_range_min': {'type': 'number'},
}, 'stoploss_range_max': {'type': 'number'},
'calculate_since_number_of_days': { 'stoploss_range_step': {'type': 'number'},
'type': 'integer' 'minimum_winrate': {'type': 'number'},
}, 'minimum_expectancy': {'type': 'number'},
'allowed_risk': { 'min_trade_number': {'type': 'number'},
'type': 'number' 'max_trade_duration_minute': {'type': 'integer'},
}, 'remove_pumps': {'type': 'boolean'}
'capital_available_percentage': {
'type': 'number'
},
'stoploss_range_min': {
'type': 'number'
},
'stoploss_range_max': {
'type': 'number'
},
'stoploss_range_step': {
'type': 'number'
},
'minimum_winrate': {
'type': 'number'
},
'minimum_expectancy': {
'type': 'number'
},
'min_trade_number': {
'type': 'number'
},
'max_trade_duration_minute': {
'type': 'integer'
},
'remove_pumps': {
'type': 'boolean'
}
}, },
'required': ['process_throttle_secs', 'allowed_risk'] 'required': ['process_throttle_secs', 'allowed_risk']
} }

View File

@ -248,27 +248,24 @@ class Hyperopt:
result: Dict = {} result: Dict = {}
if self.has_space('buy'): if self.has_space('buy'):
result['buy'] = {p.name: params.get(p.name) for p in self.hyperopt_space('buy')} result['buy'] = {p.name: params.get(p.name)
for p in self.hyperopt_space('buy')}
if self.has_space('sell'): if self.has_space('sell'):
result['sell'] = {p.name: params.get(p.name) for p in self.hyperopt_space('sell')} result['sell'] = {p.name: params.get(p.name)
for p in self.hyperopt_space('sell')}
if self.has_space('roi'): if self.has_space('roi'):
result['roi'] = self.custom_hyperopt.generate_roi_table(params) result['roi'] = self.custom_hyperopt.generate_roi_table(params)
if self.has_space('stoploss'): if self.has_space('stoploss'):
result['stoploss'] = { result['stoploss'] = {p.name: params.get(p.name)
p.name: params.get(p.name) for p in self.hyperopt_space('stoploss')}
for p in self.hyperopt_space('stoploss')
}
if self.has_space('trailing'): if self.has_space('trailing'):
result['trailing'] = self.custom_hyperopt.generate_trailing_params(params) result['trailing'] = self.custom_hyperopt.generate_trailing_params(params)
return result return result
@staticmethod @staticmethod
def print_epoch_details(results, def print_epoch_details(results, total_epochs: int, print_json: bool,
total_epochs: int, no_header: bool = False, header_str: str = None) -> None:
print_json: bool,
no_header: bool = False,
header_str: str = None) -> None:
""" """
Display details of the hyperopt result Display details of the hyperopt result
""" """
@ -307,7 +304,8 @@ class Hyperopt:
# OrderedDict is used to keep the numeric order of the items # OrderedDict is used to keep the numeric order of the items
# in the dict. # in the dict.
result_dict['minimal_roi'] = OrderedDict( result_dict['minimal_roi'] = OrderedDict(
(str(k), v) for k, v in space_params.items()) (str(k), v) for k, v in space_params.items()
)
else: # 'stoploss', 'trailing' else: # 'stoploss', 'trailing'
result_dict.update(space_params) result_dict.update(space_params)
@ -359,7 +357,8 @@ class Hyperopt:
def _format_explanation_string(results, total_epochs) -> str: def _format_explanation_string(results, total_epochs) -> str:
return (("*" if 'is_initial_point' in results and results['is_initial_point'] else " ") + return (("*" if 'is_initial_point' in results and results['is_initial_point'] else " ") +
f"{results['current_epoch']:5d}/{total_epochs}: " + f"{results['current_epoch']:5d}/{total_epochs}: " +
f"{results['results_explanation']} " + f"Objective: {results['loss']:.5f}") f"{results['results_explanation']} " +
f"Objective: {results['loss']:.5f}")
@staticmethod @staticmethod
def print_result_table(config: dict, results: list, total_epochs: int, highlight_best: bool, def print_result_table(config: dict, results: list, total_epochs: int, highlight_best: bool,
@ -372,15 +371,12 @@ class Hyperopt:
trials = json_normalize(results, max_level=1) trials = json_normalize(results, max_level=1)
trials['Best'] = '' trials['Best'] = ''
trials = trials[[ trials = trials[['Best', 'current_epoch', 'results_metrics.trade_count',
'Best', 'current_epoch', 'results_metrics.trade_count', 'results_metrics.avg_profit', 'results_metrics.avg_profit', 'results_metrics.total_profit',
'results_metrics.total_profit', 'results_metrics.profit', 'results_metrics.duration', 'results_metrics.profit', 'results_metrics.duration',
'loss', 'is_initial_point', 'is_best' 'loss', 'is_initial_point', 'is_best']]
]] trials.columns = ['Best', 'Epoch', 'Trades', 'Avg profit', 'Total profit',
trials.columns = [ 'Profit', 'Avg duration', 'Objective', 'is_initial_point', 'is_best']
'Best', 'Epoch', 'Trades', 'Avg profit', 'Total profit', 'Profit', 'Avg duration',
'Objective', 'is_initial_point', 'is_best'
]
trials['is_profit'] = False trials['is_profit'] = False
trials.loc[trials['is_initial_point'], 'Best'] = '*' trials.loc[trials['is_initial_point'], 'Best'] = '*'
trials.loc[trials['is_best'], 'Best'] = 'Best' trials.loc[trials['is_best'], 'Best'] = 'Best'
@ -388,32 +384,30 @@ class Hyperopt:
trials.loc[trials['Total profit'] > 0, 'is_profit'] = True trials.loc[trials['Total profit'] > 0, 'is_profit'] = True
trials['Trades'] = trials['Trades'].astype(str) trials['Trades'] = trials['Trades'].astype(str)
trials['Epoch'] = trials['Epoch'].apply(lambda x: "{}/{}".format(x, total_epochs)) trials['Epoch'] = trials['Epoch'].apply(
trials['Avg profit'] = trials['Avg profit'].apply(lambda x: '{:,.2f}%'.format(x) lambda x: "{}/{}".format(x, total_epochs))
if not isna(x) else x) trials['Avg profit'] = trials['Avg profit'].apply(
trials['Profit'] = trials['Profit'].apply(lambda x: '{:,.2f}%'.format(x) lambda x: '{:,.2f}%'.format(x) if not isna(x) else x)
if not isna(x) else x) trials['Profit'] = trials['Profit'].apply(
lambda x: '{:,.2f}%'.format(x) if not isna(x) else x)
trials['Total profit'] = trials['Total profit'].apply( trials['Total profit'] = trials['Total profit'].apply(
lambda x: '{: 11.8f} '.format(x) + config['stake_currency'] if not isna(x) else x) lambda x: '{: 11.8f} '.format(x) + config['stake_currency'] if not isna(x) else x)
trials['Avg duration'] = trials['Avg duration'].apply(lambda x: '{:,.1f}m'.format(x) trials['Avg duration'] = trials['Avg duration'].apply(
if not isna(x) else x) lambda x: '{:,.1f}m'.format(x) if not isna(x) else x)
if print_colorized: if print_colorized:
for i in range(len(trials)): for i in range(len(trials)):
if trials.loc[i]['is_profit']: if trials.loc[i]['is_profit']:
for z in range(len(trials.loc[i]) - 3): for z in range(len(trials.loc[i])-3):
trials.iat[i, z] = "{}{}{}".format(Fore.GREEN, str(trials.loc[i][z]), trials.iat[i, z] = "{}{}{}".format(Fore.GREEN,
Fore.RESET) str(trials.loc[i][z]), Fore.RESET)
if trials.loc[i]['is_best'] and highlight_best: if trials.loc[i]['is_best'] and highlight_best:
for z in range(len(trials.loc[i]) - 3): for z in range(len(trials.loc[i])-3):
trials.iat[i, z] = "{}{}{}".format(Style.BRIGHT, str(trials.loc[i][z]), trials.iat[i, z] = "{}{}{}".format(Style.BRIGHT,
Style.RESET_ALL) str(trials.loc[i][z]), Style.RESET_ALL)
trials = trials.drop(columns=['is_initial_point', 'is_best', 'is_profit']) trials = trials.drop(columns=['is_initial_point', 'is_best', 'is_profit'])
print( print(tabulate(trials.to_dict(orient='list'), headers='keys', tablefmt='psql',
tabulate(trials.to_dict(orient='list'),
headers='keys',
tablefmt='psql',
stralign="right")) stralign="right"))
def has_space(self, space: str) -> bool: def has_space(self, space: str) -> bool:
@ -518,10 +512,8 @@ class Hyperopt:
# path. We do not want to optimize 'hodl' strategies. # path. We do not want to optimize 'hodl' strategies.
loss: float = MAX_LOSS loss: float = MAX_LOSS
if trade_count >= self.config['hyperopt_min_trades']: if trade_count >= self.config['hyperopt_min_trades']:
loss = self.calculate_loss(results=backtesting_results, loss = self.calculate_loss(results=backtesting_results, trade_count=trade_count,
trade_count=trade_count, min_date=min_date.datetime, max_date=max_date.datetime)
min_date=min_date.datetime,
max_date=max_date.datetime)
return { return {
'loss': loss, 'loss': loss,
'params_dict': params_dict, 'params_dict': params_dict,
@ -549,8 +541,8 @@ class Hyperopt:
f"Avg profit {results_metrics['avg_profit']: 6.2f}%. " f"Avg profit {results_metrics['avg_profit']: 6.2f}%. "
f"Total profit {results_metrics['total_profit']: 11.8f} {stake_cur} " f"Total profit {results_metrics['total_profit']: 11.8f} {stake_cur} "
f"({results_metrics['profit']: 7.2f}\N{GREEK CAPITAL LETTER SIGMA}%). " f"({results_metrics['profit']: 7.2f}\N{GREEK CAPITAL LETTER SIGMA}%). "
f"Avg duration {results_metrics['duration']:5.1f} min.").encode( f"Avg duration {results_metrics['duration']:5.1f} min."
locale.getpreferredencoding(), 'replace').decode('utf-8') ).encode(locale.getpreferredencoding(), 'replace').decode('utf-8')
def get_next_point_strategy(self): def get_next_point_strategy(self):
""" Choose a strategy randomly among the supported ones, used in multi opt mode """ Choose a strategy randomly among the supported ones, used in multi opt mode
@ -571,10 +563,6 @@ class Hyperopt:
acq_optimizer=self.opt_acq_optimizer, acq_optimizer=self.opt_acq_optimizer,
n_initial_points=n_initial_points, n_initial_points=n_initial_points,
acq_optimizer_kwargs={'n_jobs': n_jobs}, acq_optimizer_kwargs={'n_jobs': n_jobs},
acq_func_kwargs={
'xi': 0.00001,
'kappa': 0.00001
},
model_queue_size=self.n_models, model_queue_size=self.n_models,
random_state=self.random_state, random_state=self.random_state,
) )
@ -755,8 +743,9 @@ class Hyperopt:
n_parameters += len(d.bounds) n_parameters += len(d.bounds)
# guess the size of the search space as the count of the # guess the size of the search space as the count of the
# unordered combination of the dimensions entries # unordered combination of the dimensions entries
search_space_size = (factorial(n_parameters) / search_space_size = int(
(factorial(n_parameters - n_dimensions) * factorial(n_dimensions))) (factorial(n_parameters) /
(factorial(n_parameters - n_dimensions) * factorial(n_dimensions))))
# logger.info(f'Search space size: {search_space_size}') # logger.info(f'Search space size: {search_space_size}')
if search_space_size < n_jobs: if search_space_size < n_jobs:
# don't waste if the space is small # don't waste if the space is small
@ -789,7 +778,7 @@ class Hyperopt:
if self.max_epoch > self.search_space_size: if self.max_epoch > self.search_space_size:
self.max_epoch = self.search_space_size self.max_epoch = self.search_space_size
print() print()
logger.info(f'Max epochs set to: {self.epochs_limit()}') logger.info(f'Max epoch set to: {self.epochs_limit()}')
def setup_optimizers(self): def setup_optimizers(self):
""" Setup the optimizers objects, try to load from disk, or create new ones """ """ Setup the optimizers objects, try to load from disk, or create new ones """
@ -834,8 +823,10 @@ class Hyperopt:
self.n_samples += len(preprocessed[pair]) self.n_samples += len(preprocessed[pair])
min_date, max_date = get_timerange(data) min_date, max_date = get_timerange(data)
logger.info('Hyperopting with data from %s up to %s (%s days)..', min_date.isoformat(), logger.info(
max_date.isoformat(), (max_date - min_date).days) 'Hyperopting with data from %s up to %s (%s days)..',
min_date.isoformat(), max_date.isoformat(), (max_date - min_date).days
)
dump(preprocessed, self.tickerdata_pickle) dump(preprocessed, self.tickerdata_pickle)
# We don't need exchange instance anymore while running hyperopt # We don't need exchange instance anymore while running hyperopt
@ -898,7 +889,7 @@ class Hyperopt:
break break
except KeyboardInterrupt: except KeyboardInterrupt:
print("User interrupted..") print('User interrupted..')
self.save_trials(final=True) self.save_trials(final=True)

View File

@ -13,7 +13,3 @@ ignore_missing_imports = True
[mypy-tests.*] [mypy-tests.*]
ignore_errors = True ignore_errors = True
[yapf]
based_on_style = pep8
column_limit = 100

View File

@ -10,13 +10,15 @@ import pytest
from arrow import Arrow from arrow import Arrow
from filelock import Timeout from filelock import Timeout
from freqtrade.commands.optimize_commands import (setup_optimize_configuration, start_hyperopt) from freqtrade.commands.optimize_commands import (setup_optimize_configuration,
start_hyperopt)
from freqtrade.data.history import load_data from freqtrade.data.history import load_data
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.optimize.default_hyperopt import DefaultHyperOpt from freqtrade.optimize.default_hyperopt import DefaultHyperOpt
from freqtrade.optimize.default_hyperopt_loss import DefaultHyperOptLoss from freqtrade.optimize.default_hyperopt_loss import DefaultHyperOptLoss
from freqtrade.optimize.hyperopt import Hyperopt from freqtrade.optimize.hyperopt import Hyperopt
from freqtrade.resolvers.hyperopt_resolver import (HyperOptLossResolver, HyperOptResolver) from freqtrade.resolvers.hyperopt_resolver import (HyperOptLossResolver,
HyperOptResolver)
from freqtrade.state import RunMode from freqtrade.state import RunMode
from freqtrade.strategy.interface import SellType from freqtrade.strategy.interface import SellType
from tests.conftest import (get_args, log_has, log_has_re, patch_exchange, from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
@ -35,18 +37,21 @@ def hyperopt(default_conf, mocker):
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
def hyperopt_results(): def hyperopt_results():
return pd.DataFrame({ return pd.DataFrame(
{
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'], 'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
'profit_percent': [-0.1, 0.2, 0.3], 'profit_percent': [-0.1, 0.2, 0.3],
'profit_abs': [-0.2, 0.4, 0.6], 'profit_abs': [-0.2, 0.4, 0.6],
'trade_duration': [10, 30, 10], 'trade_duration': [10, 30, 10],
'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.ROI], 'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.ROI],
'close_time': [ 'close_time':
[
datetime(2019, 1, 1, 9, 26, 3, 478039), datetime(2019, 1, 1, 9, 26, 3, 478039),
datetime(2019, 2, 1, 9, 26, 3, 478039), datetime(2019, 2, 1, 9, 26, 3, 478039),
datetime(2019, 3, 1, 9, 26, 3, 478039) datetime(2019, 3, 1, 9, 26, 3, 478039)
] ]
}) }
)
# Functions for recurrent object patching # Functions for recurrent object patching
@ -75,10 +80,8 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca
args = [ args = [
'hyperopt', 'hyperopt',
'--config', '--config', 'config.json',
'config.json', '--hyperopt', 'DefaultHyperOpt',
'--hyperopt',
'DefaultHyperOpt',
] ]
config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT) config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT)
@ -102,12 +105,23 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca
def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplog) -> None: def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplog) -> None:
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.configuration.configuration.create_datadir', lambda c, x: x) mocker.patch(
'freqtrade.configuration.configuration.create_datadir',
lambda c, x: x
)
args = [ args = [
'hyperopt', '--config', 'config.json', '--hyperopt', 'DefaultHyperOpt', '--datadir', 'hyperopt',
'/foo/bar', '--ticker-interval', '1m', '--timerange', ':100', '--enable-position-stacking', '--config', 'config.json',
'--disable-max-market-positions', '--epochs', '1000', '--spaces', 'default', '--print-all' '--hyperopt', 'DefaultHyperOpt',
'--datadir', '/foo/bar',
'--ticker-interval', '1m',
'--timerange', ':100',
'--enable-position-stacking',
'--disable-max-market-positions',
'--epochs', '1000',
'--spaces', 'default',
'--print-all'
] ]
config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT) config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT)
@ -151,21 +165,20 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
delattr(hyperopt, 'populate_indicators') delattr(hyperopt, 'populate_indicators')
delattr(hyperopt, 'populate_buy_trend') delattr(hyperopt, 'populate_buy_trend')
delattr(hyperopt, 'populate_sell_trend') delattr(hyperopt, 'populate_sell_trend')
mocker.patch('freqtrade.resolvers.hyperopt_resolver.HyperOptResolver.load_object', mocker.patch(
MagicMock(return_value=hyperopt(default_conf))) 'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver.load_object',
MagicMock(return_value=hyperopt(default_conf))
)
default_conf.update({'hyperopt': 'DefaultHyperOpt'}) default_conf.update({'hyperopt': 'DefaultHyperOpt'})
x = HyperOptResolver.load_hyperopt(default_conf) x = HyperOptResolver.load_hyperopt(default_conf)
assert not hasattr(x, 'populate_indicators') assert not hasattr(x, 'populate_indicators')
assert not hasattr(x, 'populate_buy_trend') assert not hasattr(x, 'populate_buy_trend')
assert not hasattr(x, 'populate_sell_trend') assert not hasattr(x, 'populate_sell_trend')
assert log_has( assert log_has("Hyperopt class does not provide populate_indicators() method. "
"Hyperopt class does not provide populate_indicators() method. "
"Using populate_indicators from the strategy.", caplog) "Using populate_indicators from the strategy.", caplog)
assert log_has( assert log_has("Hyperopt class does not provide populate_sell_trend() method. "
"Hyperopt class does not provide populate_sell_trend() method. "
"Using populate_sell_trend from the strategy.", caplog) "Using populate_sell_trend from the strategy.", caplog)
assert log_has( assert log_has("Hyperopt class does not provide populate_buy_trend() method. "
"Hyperopt class does not provide populate_buy_trend() method. "
"Using populate_buy_trend from the strategy.", caplog) "Using populate_buy_trend from the strategy.", caplog)
assert hasattr(x, "ticker_interval") assert hasattr(x, "ticker_interval")
@ -188,8 +201,10 @@ def test_hyperoptresolver_noname(default_conf):
def test_hyperoptlossresolver(mocker, default_conf, caplog) -> None: def test_hyperoptlossresolver(mocker, default_conf, caplog) -> None:
hl = DefaultHyperOptLoss hl = DefaultHyperOptLoss
mocker.patch('freqtrade.resolvers.hyperopt_resolver.HyperOptLossResolver.load_object', mocker.patch(
MagicMock(return_value=hl)) 'freqtrade.resolvers.hyperopt_resolver.HyperOptLossResolver.load_object',
MagicMock(return_value=hl)
)
x = HyperOptLossResolver.load_hyperoptloss(default_conf) x = HyperOptLossResolver.load_hyperoptloss(default_conf)
assert hasattr(x, "hyperopt_loss_function") assert hasattr(x, "hyperopt_loss_function")
@ -208,7 +223,12 @@ def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
patch_exchange(mocker) patch_exchange(mocker)
args = ['hyperopt', '--config', 'config.json', '--hyperopt', 'DefaultHyperOpt', '--epochs', '5'] args = [
'hyperopt',
'--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt',
'--epochs', '5'
]
pargs = get_args(args) pargs = get_args(args)
with pytest.raises(OperationalException, match=r"Please ensure that the hyperopt dependencies"): with pytest.raises(OperationalException, match=r"Please ensure that the hyperopt dependencies"):
@ -221,7 +241,12 @@ def test_start(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
patch_exchange(mocker) patch_exchange(mocker)
args = ['hyperopt', '--config', 'config.json', '--hyperopt', 'DefaultHyperOpt', '--epochs', '5'] args = [
'hyperopt',
'--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt',
'--epochs', '5'
]
pargs = get_args(args) pargs = get_args(args)
start_hyperopt(pargs) start_hyperopt(pargs)
@ -232,12 +257,19 @@ def test_start(mocker, default_conf, caplog) -> None:
def test_start_no_data(mocker, default_conf, caplog) -> None: def test_start_no_data(mocker, default_conf, caplog) -> None:
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.data.history.load_pair_history', MagicMock(return_value=pd.DataFrame)) mocker.patch('freqtrade.data.history.load_pair_history', MagicMock(return_value=pd.DataFrame))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange', mocker.patch(
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))) 'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
patch_exchange(mocker) patch_exchange(mocker)
args = ['hyperopt', '--config', 'config.json', '--hyperopt', 'DefaultHyperOpt', '--epochs', '5'] args = [
'hyperopt',
'--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt',
'--epochs', '5'
]
pargs = get_args(args) pargs = get_args(args)
with pytest.raises(OperationalException, match='No data found. Terminating.'): with pytest.raises(OperationalException, match='No data found. Terminating.'):
start_hyperopt(pargs) start_hyperopt(pargs)
@ -249,7 +281,12 @@ def test_start_filelock(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
patch_exchange(mocker) patch_exchange(mocker)
args = ['hyperopt', '--config', 'config.json', '--hyperopt', 'DefaultHyperOpt', '--epochs', '5'] args = [
'hyperopt',
'--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt',
'--epochs', '5'
]
pargs = get_args(args) pargs = get_args(args)
start_hyperopt(pargs) start_hyperopt(pargs)
assert log_has("Another running instance of freqtrade Hyperopt detected.", caplog) assert log_has("Another running instance of freqtrade Hyperopt detected.", caplog)
@ -257,12 +294,12 @@ def test_start_filelock(mocker, default_conf, caplog) -> None:
def test_loss_calculation_prefer_correct_trade_count(default_conf, hyperopt_results) -> None: def test_loss_calculation_prefer_correct_trade_count(default_conf, hyperopt_results) -> None:
hl = HyperOptLossResolver.load_hyperoptloss(default_conf) hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, 600, datetime(2019, 1, 1), correct = hl.hyperopt_loss_function(hyperopt_results, 600,
datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(hyperopt_results, 600 + 100, datetime(2019, 1, 1), over = hl.hyperopt_loss_function(hyperopt_results, 600 + 100,
datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(hyperopt_results, 600 - 100, datetime(2019, 1, 1), under = hl.hyperopt_loss_function(hyperopt_results, 600 - 100,
datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over > correct assert over > correct
assert under > correct assert under > correct
@ -272,9 +309,10 @@ def test_loss_calculation_prefer_shorter_trades(default_conf, hyperopt_results)
resultsb.loc[1, 'trade_duration'] = 20 resultsb.loc[1, 'trade_duration'] = 20
hl = HyperOptLossResolver.load_hyperoptloss(default_conf) hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
longer = hl.hyperopt_loss_function(hyperopt_results, 100, datetime(2019, 1, 1), longer = hl.hyperopt_loss_function(hyperopt_results, 100,
datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
shorter = hl.hyperopt_loss_function(resultsb, 100, datetime(2019, 1, 1), datetime(2019, 5, 1)) shorter = hl.hyperopt_loss_function(resultsb, 100,
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert shorter < longer assert shorter < longer
@ -285,11 +323,12 @@ def test_loss_calculation_has_limited_profit(default_conf, hyperopt_results) ->
results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2 results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2
hl = HyperOptLossResolver.load_hyperoptloss(default_conf) hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, 600, datetime(2019, 1, 1), correct = hl.hyperopt_loss_function(hyperopt_results, 600,
datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, 600, datetime(2019, 1, 1), datetime(2019, 5, 1)) over = hl.hyperopt_loss_function(results_over, 600,
under = hl.hyperopt_loss_function(results_under, 600, datetime(2019, 1, 1), datetime(2019, 1, 1), datetime(2019, 5, 1))
datetime(2019, 5, 1)) under = hl.hyperopt_loss_function(results_under, 600,
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < correct assert over < correct
assert under > correct assert under > correct
@ -304,10 +343,10 @@ def test_sharpe_loss_prefers_higher_profits(default_conf, hyperopt_results) -> N
hl = HyperOptLossResolver.load_hyperoptloss(default_conf) hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results), correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), datetime(2019, 1, 1), over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), datetime(2019, 1, 1), under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < correct assert over < correct
assert under > correct assert under > correct
@ -322,10 +361,10 @@ def test_sharpe_loss_daily_prefers_higher_profits(default_conf, hyperopt_results
hl = HyperOptLossResolver.load_hyperoptloss(default_conf) hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results), correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), datetime(2019, 1, 1), over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), datetime(2019, 1, 1), under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < correct assert over < correct
assert under > correct assert under > correct
@ -376,10 +415,10 @@ def test_onlyprofit_loss_prefers_higher_profits(default_conf, hyperopt_results)
hl = HyperOptLossResolver.load_hyperoptloss(default_conf) hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results), correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), datetime(2019, 1, 1), over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), datetime(2019, 1, 1), under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
datetime(2019, 5, 1)) datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < correct assert over < correct
assert under > correct assert under > correct
@ -387,24 +426,28 @@ def test_onlyprofit_loss_prefers_higher_profits(default_conf, hyperopt_results)
def test_log_results_if_loss_improves(hyperopt, capsys) -> None: def test_log_results_if_loss_improves(hyperopt, capsys) -> None:
hyperopt.current_best_loss = 2 hyperopt.current_best_loss = 2
hyperopt.total_epochs = 2 hyperopt.total_epochs = 2
hyperopt.print_results({ hyperopt.print_results(
{
'is_best': True, 'is_best': True,
'loss': 1, 'loss': 1,
'current_epoch': 2, # This starts from 1 (in a human-friendly manner) 'current_epoch': 2, # This starts from 1 (in a human-friendly manner)
'results_explanation': 'foo.', 'results_explanation': 'foo.',
'is_initial_point': False 'is_initial_point': False
}) }
)
out, err = capsys.readouterr() out, err = capsys.readouterr()
assert ' 2/2: foo. Objective: 1.00000' in out assert ' 2/2: foo. Objective: 1.00000' in out
def test_no_log_if_loss_does_not_improve(hyperopt, caplog) -> None: def test_no_log_if_loss_does_not_improve(hyperopt, caplog) -> None:
hyperopt.current_best_loss = 2 hyperopt.current_best_loss = 2
hyperopt.print_results({ hyperopt.print_results(
{
'is_best': False, 'is_best': False,
'loss': 3, 'loss': 3,
'current_epoch': 1, 'current_epoch': 1,
}) }
)
assert caplog.record_tuples == [] assert caplog.record_tuples == []
@ -452,32 +495,25 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None:
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None))) MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange', mocker.patch(
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))) 'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch( parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel', 'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{ MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result',
'loss': 1, 'params': {'buy': {}, 'sell': {}, 'roi': {}, 'stoploss': 0.0}}])
'results_explanation': 'foo result', )
'params': {
'buy': {},
'sell': {},
'roi': {},
'stoploss': 0.0
}
}]))
patch_exchange(mocker) patch_exchange(mocker)
# Co-test loading ticker-interval from strategy # Co-test loading ticker-interval from strategy
del default_conf['ticker_interval'] del default_conf['ticker_interval']
default_conf.update({ default_conf.update({'config': 'config.json.example',
'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt', 'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'default', 'spaces': 'default',
'hyperopt_jobs': 1, 'hyperopt_jobs': 1, })
})
hyperopt = Hyperopt(default_conf) hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock() hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
@ -504,7 +540,11 @@ def test_start_calls_optimizer(mocker, default_conf, caplog, capsys) -> None:
def test_format_results(hyperopt): def test_format_results(hyperopt):
# Test with BTC as stake_currency # Test with BTC as stake_currency
trades = [('ETH/BTC', 2, 2, 123), ('LTC/BTC', 1, 1, 123), ('XPR/BTC', -1, -2, -246)] trades = [
('ETH/BTC', 2, 2, 123),
('LTC/BTC', 1, 1, 123),
('XPR/BTC', -1, -2, -246)
]
labels = ['currency', 'profit_percent', 'profit_abs', 'trade_duration'] labels = ['currency', 'profit_percent', 'profit_abs', 'trade_duration']
df = pd.DataFrame.from_records(trades, columns=labels) df = pd.DataFrame.from_records(trades, columns=labels)
results_metrics = hyperopt._calculate_results_metrics(df) results_metrics = hyperopt._calculate_results_metrics(df)
@ -528,7 +568,11 @@ def test_format_results(hyperopt):
assert result.find('2.0000Σ %') assert result.find('2.0000Σ %')
# Test with EUR as stake_currency # Test with EUR as stake_currency
trades = [('ETH/EUR', 2, 2, 123), ('LTC/EUR', 1, 1, 123), ('XPR/EUR', -1, -2, -246)] trades = [
('ETH/EUR', 2, 2, 123),
('LTC/EUR', 1, 1, 123),
('XPR/EUR', -1, -2, -246)
]
df = pd.DataFrame.from_records(trades, columns=labels) df = pd.DataFrame.from_records(trades, columns=labels)
results_metrics = hyperopt._calculate_results_metrics(df) results_metrics = hyperopt._calculate_results_metrics(df)
results['total_profit'] = results_metrics['total_profit'] results['total_profit'] = results_metrics['total_profit']
@ -537,97 +581,32 @@ def test_format_results(hyperopt):
@pytest.mark.parametrize("spaces, expected_results", [ @pytest.mark.parametrize("spaces, expected_results", [
(['buy'], { (['buy'],
'buy': True, {'buy': True, 'sell': False, 'roi': False, 'stoploss': False, 'trailing': False}),
'sell': False, (['sell'],
'roi': False, {'buy': False, 'sell': True, 'roi': False, 'stoploss': False, 'trailing': False}),
'stoploss': False, (['roi'],
'trailing': False {'buy': False, 'sell': False, 'roi': True, 'stoploss': False, 'trailing': False}),
}), (['stoploss'],
(['sell'], { {'buy': False, 'sell': False, 'roi': False, 'stoploss': True, 'trailing': False}),
'buy': False, (['trailing'],
'sell': True, {'buy': False, 'sell': False, 'roi': False, 'stoploss': False, 'trailing': True}),
'roi': False, (['buy', 'sell', 'roi', 'stoploss'],
'stoploss': False, {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False}),
'trailing': False (['buy', 'sell', 'roi', 'stoploss', 'trailing'],
}), {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True}),
(['roi'], { (['buy', 'roi'],
'buy': False, {'buy': True, 'sell': False, 'roi': True, 'stoploss': False, 'trailing': False}),
'sell': False, (['all'],
'roi': True, {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True}),
'stoploss': False, (['default'],
'trailing': False {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False}),
}), (['default', 'trailing'],
(['stoploss'], { {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True}),
'buy': False, (['all', 'buy'],
'sell': False, {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': True}),
'roi': False, (['default', 'buy'],
'stoploss': True, {'buy': True, 'sell': True, 'roi': True, 'stoploss': True, 'trailing': False}),
'trailing': False
}),
(['trailing'], {
'buy': False,
'sell': False,
'roi': False,
'stoploss': False,
'trailing': True
}),
(['buy', 'sell', 'roi', 'stoploss'], {
'buy': True,
'sell': True,
'roi': True,
'stoploss': True,
'trailing': False
}),
(['buy', 'sell', 'roi', 'stoploss', 'trailing'], {
'buy': True,
'sell': True,
'roi': True,
'stoploss': True,
'trailing': True
}),
(['buy', 'roi'], {
'buy': True,
'sell': False,
'roi': True,
'stoploss': False,
'trailing': False
}),
(['all'], {
'buy': True,
'sell': True,
'roi': True,
'stoploss': True,
'trailing': True
}),
(['default'], {
'buy': True,
'sell': True,
'roi': True,
'stoploss': True,
'trailing': False
}),
(['default', 'trailing'], {
'buy': True,
'sell': True,
'roi': True,
'stoploss': True,
'trailing': True
}),
(['all', 'buy'], {
'buy': True,
'sell': True,
'roi': True,
'stoploss': True,
'trailing': True
}),
(['default', 'buy'], {
'buy': True,
'sell': True,
'roi': True,
'stoploss': True,
'trailing': False
}),
]) ])
def test_has_space(hyperopt, spaces, expected_results): def test_has_space(hyperopt, spaces, expected_results):
for s in ['buy', 'sell', 'roi', 'stoploss', 'trailing']: for s in ['buy', 'sell', 'roi', 'stoploss', 'trailing']:
@ -653,7 +632,8 @@ def test_buy_strategy_generator(hyperopt, testdatadir) -> None:
dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'], dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'],
{'pair': 'UNITTEST/BTC'}) {'pair': 'UNITTEST/BTC'})
populate_buy_trend = hyperopt.custom_hyperopt.buy_strategy_generator({ populate_buy_trend = hyperopt.custom_hyperopt.buy_strategy_generator(
{
'adx-value': 20, 'adx-value': 20,
'fastd-value': 20, 'fastd-value': 20,
'mfi-value': 20, 'mfi-value': 20,
@ -663,7 +643,8 @@ def test_buy_strategy_generator(hyperopt, testdatadir) -> None:
'mfi-enabled': True, 'mfi-enabled': True,
'rsi-enabled': True, 'rsi-enabled': True,
'trigger': 'bb_lower' 'trigger': 'bb_lower'
}) }
)
result = populate_buy_trend(dataframe, {'pair': 'UNITTEST/BTC'}) result = populate_buy_trend(dataframe, {'pair': 'UNITTEST/BTC'})
# Check if some indicators are generated. We will not test all of them # Check if some indicators are generated. We will not test all of them
assert 'buy' in result assert 'buy' in result
@ -679,14 +660,20 @@ def test_backtest_params(mocker, default_conf) -> None:
'hyperopt_min_trades': 1, 'hyperopt_min_trades': 1,
}) })
trades = [('TRX/BTC', 0.023117, 0.000233, 100)] trades = [
('TRX/BTC', 0.023117, 0.000233, 100)
]
labels = ['currency', 'profit_percent', 'profit_abs', 'trade_duration'] labels = ['currency', 'profit_percent', 'profit_abs', 'trade_duration']
backtest_result = pd.DataFrame.from_records(trades, columns=labels) backtest_result = pd.DataFrame.from_records(trades, columns=labels)
mocker.patch('freqtrade.optimize.hyperopt.Backtesting.backtest', mocker.patch(
MagicMock(return_value=backtest_result)) 'freqtrade.optimize.hyperopt.Backtesting.backtest',
mocker.patch('freqtrade.optimize.hyperopt.get_timerange', MagicMock(return_value=backtest_result)
MagicMock(return_value=(Arrow(2017, 12, 10), Arrow(2017, 12, 13)))) )
mocker.patch(
'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(Arrow(2017, 12, 10), Arrow(2017, 12, 13)))
)
patch_exchange(mocker) patch_exchange(mocker)
mocker.patch('freqtrade.optimize.hyperopt.load', MagicMock()) mocker.patch('freqtrade.optimize.hyperopt.load', MagicMock())
@ -722,15 +709,11 @@ def test_backtest_params(mocker, default_conf) -> None:
'trailing_only_offset_is_reached': False, 'trailing_only_offset_is_reached': False,
} }
response_expected = { response_expected = {
'loss': 'loss': 1.9840569076926293,
1.9840569076926293, 'results_explanation': (' 1 trades. Avg profit 2.31%. Total profit 0.00023300 BTC '
'results_explanation': '( 2.31\N{GREEK CAPITAL LETTER SIGMA}%). Avg duration 100.0 min.'
(' 1 trades. Avg profit 2.31%. Total profit 0.00023300 BTC ' ).encode(locale.getpreferredencoding(), 'replace').decode('utf-8'),
'( 2.31\N{GREEK CAPITAL LETTER SIGMA}%). Avg duration 100.0 min.').encode( 'params_details': {'buy': {'adx-enabled': False,
locale.getpreferredencoding(), 'replace').decode('utf-8'),
'params_details': {
'buy': {
'adx-enabled': False,
'adx-value': 0, 'adx-value': 0,
'fastd-enabled': True, 'fastd-enabled': True,
'fastd-value': 35, 'fastd-value': 35,
@ -738,16 +721,12 @@ def test_backtest_params(mocker, default_conf) -> None:
'mfi-value': 0, 'mfi-value': 0,
'rsi-enabled': False, 'rsi-enabled': False,
'rsi-value': 0, 'rsi-value': 0,
'trigger': 'macd_cross_signal' 'trigger': 'macd_cross_signal'},
}, 'roi': {0: 0.12000000000000001,
'roi': {
0: 0.12000000000000001,
20.0: 0.02, 20.0: 0.02,
50.0: 0.01, 50.0: 0.01,
110.0: 0 110.0: 0},
}, 'sell': {'sell-adx-enabled': False,
'sell': {
'sell-adx-enabled': False,
'sell-adx-value': 0, 'sell-adx-value': 0,
'sell-fastd-enabled': True, 'sell-fastd-enabled': True,
'sell-fastd-value': 75, 'sell-fastd-value': 75,
@ -755,29 +734,19 @@ def test_backtest_params(mocker, default_conf) -> None:
'sell-mfi-value': 0, 'sell-mfi-value': 0,
'sell-rsi-enabled': False, 'sell-rsi-enabled': False,
'sell-rsi-value': 0, 'sell-rsi-value': 0,
'sell-trigger': 'macd_cross_signal' 'sell-trigger': 'macd_cross_signal'},
}, 'stoploss': {'stoploss': -0.4},
'stoploss': { 'trailing': {'trailing_only_offset_is_reached': False,
'stoploss': -0.4
},
'trailing': {
'trailing_only_offset_is_reached': False,
'trailing_stop': True, 'trailing_stop': True,
'trailing_stop_positive': 0.02, 'trailing_stop_positive': 0.02,
'trailing_stop_positive_offset': 0.07 'trailing_stop_positive_offset': 0.07}},
} 'params_dict': optimizer_param,
}, 'results_metrics': {'avg_profit': 2.3117,
'params_dict':
optimizer_param,
'results_metrics': {
'avg_profit': 2.3117,
'duration': 100.0, 'duration': 100.0,
'profit': 2.3117, 'profit': 2.3117,
'total_profit': 0.000233, 'total_profit': 0.000233,
'trade_count': 1 'trade_count': 1},
}, 'total_profit': 0.00023300
'total_profit':
0.00023300
} }
hyperopt = Hyperopt(default_conf) hyperopt = Hyperopt(default_conf)
@ -788,8 +757,7 @@ def test_backtest_params(mocker, default_conf) -> None:
def test_clean_hyperopt(mocker, default_conf, caplog): def test_clean_hyperopt(mocker, default_conf, caplog):
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({ default_conf.update({'config': 'config.json.example',
'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt', 'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
@ -807,8 +775,7 @@ def test_clean_hyperopt(mocker, default_conf, caplog):
def test_continue_hyperopt(mocker, default_conf, caplog): def test_continue_hyperopt(mocker, default_conf, caplog):
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({ default_conf.update({'config': 'config.json.example',
'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt', 'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
@ -828,35 +795,22 @@ def test_print_json_spaces_all(mocker, default_conf, caplog, capsys) -> None:
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None))) MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange', mocker.patch(
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))) 'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch( parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel', 'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{ MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {},
'loss': 1, 'params_details': {'buy': {'mfi-value': None},
'results_explanation': 'foo result', 'sell': {'sell-mfi-value': None},
'params': {}, 'roi': {}, 'stoploss': {'stoploss': None},
'params_details': { 'trailing': {'trailing_stop': None}}}])
'buy': { )
'mfi-value': None
},
'sell': {
'sell-mfi-value': None
},
'roi': {},
'stoploss': {
'stoploss': None
},
'trailing': {
'trailing_stop': None
}
}
}]))
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({ default_conf.update({'config': 'config.json.example',
'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt', 'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
@ -887,32 +841,21 @@ def test_print_json_spaces_default(mocker, default_conf, caplog, capsys) -> None
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None))) MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange', mocker.patch(
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))) 'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch( parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel', 'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{ MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {},
'loss': 1, 'params_details': {'buy': {'mfi-value': None},
'results_explanation': 'foo result', 'sell': {'sell-mfi-value': None},
'params': {}, 'roi': {}, 'stoploss': {'stoploss': None}}}])
'params_details': { )
'buy': {
'mfi-value': None
},
'sell': {
'sell-mfi-value': None
},
'roi': {},
'stoploss': {
'stoploss': None
}
}
}]))
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({ default_conf.update({'config': 'config.json.example',
'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt', 'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
@ -943,26 +886,19 @@ def test_print_json_spaces_roi_stoploss(mocker, default_conf, caplog, capsys) ->
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None))) MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange', mocker.patch(
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))) 'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch( parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel', 'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{ MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {},
'loss': 1, 'params_details': {'roi': {}, 'stoploss': {'stoploss': None}}}])
'results_explanation': 'foo result', )
'params': {},
'params_details': {
'roi': {},
'stoploss': {
'stoploss': None
}
}
}]))
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({ default_conf.update({'config': 'config.json.example',
'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt', 'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
@ -993,28 +929,24 @@ def test_simplified_interface_roi_stoploss(mocker, default_conf, caplog, capsys)
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None))) MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange', mocker.patch(
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))) 'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch( parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel', 'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{ MagicMock(return_value=[{
'loss': 1, 'loss': 1, 'results_explanation': 'foo result', 'params': {'stoploss': 0.0}}])
'results_explanation': 'foo result', )
'params': {
'stoploss': 0.0
}
}]))
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({ default_conf.update({'config': 'config.json.example',
'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt', 'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'roi stoploss', 'spaces': 'roi stoploss',
'hyperopt_jobs': 1, 'hyperopt_jobs': 1, })
})
hyperopt = Hyperopt(default_conf) hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock() hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
@ -1048,19 +980,19 @@ def test_simplified_interface_all_failed(mocker, default_conf, caplog, capsys) -
mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None))) MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange', mocker.patch(
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))) 'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({ default_conf.update({'config': 'config.json.example',
'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt', 'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'all', 'spaces': 'all',
'hyperopt_jobs': 1, 'hyperopt_jobs': 1, })
})
hyperopt = Hyperopt(default_conf) hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock() hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
@ -1079,26 +1011,23 @@ def test_simplified_interface_buy(mocker, default_conf, caplog, capsys) -> None:
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None))) MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange', mocker.patch(
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))) 'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch( parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel', 'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{ MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}])
'loss': 1, )
'results_explanation': 'foo result',
'params': {}
}]))
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({ default_conf.update({'config': 'config.json.example',
'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt', 'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'buy', 'spaces': 'buy',
'hyperopt_jobs': 1, 'hyperopt_jobs': 1, })
})
hyperopt = Hyperopt(default_conf) hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock() hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
@ -1132,26 +1061,23 @@ def test_simplified_interface_sell(mocker, default_conf, caplog, capsys) -> None
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None))) MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange', mocker.patch(
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))) 'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch( parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel', 'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{ MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}])
'loss': 1, )
'results_explanation': 'foo result',
'params': {}
}]))
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({ default_conf.update({'config': 'config.json.example',
'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt', 'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': 'sell', 'spaces': 'sell',
'hyperopt_jobs': 1, 'hyperopt_jobs': 1, })
})
hyperopt = Hyperopt(default_conf) hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock() hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()
@ -1191,19 +1117,19 @@ def test_simplified_interface_failed(mocker, default_conf, caplog, capsys, metho
mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None))) MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange', mocker.patch(
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))) 'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({ default_conf.update({'config': 'config.json.example',
'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt', 'hyperopt': 'DefaultHyperOpt',
'epochs': 1, 'epochs': 1,
'timerange': None, 'timerange': None,
'spaces': space, 'spaces': space,
'hyperopt_jobs': 1, 'hyperopt_jobs': 1, })
})
hyperopt = Hyperopt(default_conf) hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock() hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()