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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,13 +10,15 @@ import pytest
from arrow import Arrow
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.exceptions import OperationalException
from freqtrade.optimize.default_hyperopt import DefaultHyperOpt
from freqtrade.optimize.default_hyperopt_loss import DefaultHyperOptLoss
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.strategy.interface import SellType
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')
def hyperopt_results():
return pd.DataFrame({
return pd.DataFrame(
{
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
'profit_percent': [-0.1, 0.2, 0.3],
'profit_abs': [-0.2, 0.4, 0.6],
'trade_duration': [10, 30, 10],
'sell_reason': [SellType.STOP_LOSS, SellType.ROI, SellType.ROI],
'close_time': [
'close_time':
[
datetime(2019, 1, 1, 9, 26, 3, 478039),
datetime(2019, 2, 1, 9, 26, 3, 478039),
datetime(2019, 3, 1, 9, 26, 3, 478039)
]
})
}
)
# Functions for recurrent object patching
@ -75,10 +80,8 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca
args = [
'hyperopt',
'--config',
'config.json',
'--hyperopt',
'DefaultHyperOpt',
'--config', 'config.json',
'--hyperopt', 'DefaultHyperOpt',
]
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:
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 = [
'hyperopt', '--config', 'config.json', '--hyperopt', 'DefaultHyperOpt', '--datadir',
'/foo/bar', '--ticker-interval', '1m', '--timerange', ':100', '--enable-position-stacking',
'--disable-max-market-positions', '--epochs', '1000', '--spaces', 'default', '--print-all'
'hyperopt',
'--config', 'config.json',
'--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)
@ -151,21 +165,20 @@ def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
delattr(hyperopt, 'populate_indicators')
delattr(hyperopt, 'populate_buy_trend')
delattr(hyperopt, 'populate_sell_trend')
mocker.patch('freqtrade.resolvers.hyperopt_resolver.HyperOptResolver.load_object',
MagicMock(return_value=hyperopt(default_conf)))
mocker.patch(
'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver.load_object',
MagicMock(return_value=hyperopt(default_conf))
)
default_conf.update({'hyperopt': 'DefaultHyperOpt'})
x = HyperOptResolver.load_hyperopt(default_conf)
assert not hasattr(x, 'populate_indicators')
assert not hasattr(x, 'populate_buy_trend')
assert not hasattr(x, 'populate_sell_trend')
assert log_has(
"Hyperopt class does not provide populate_indicators() method. "
assert log_has("Hyperopt class does not provide populate_indicators() method. "
"Using populate_indicators from the strategy.", caplog)
assert log_has(
"Hyperopt class does not provide populate_sell_trend() method. "
assert log_has("Hyperopt class does not provide populate_sell_trend() method. "
"Using populate_sell_trend from the strategy.", caplog)
assert log_has(
"Hyperopt class does not provide populate_buy_trend() method. "
assert log_has("Hyperopt class does not provide populate_buy_trend() method. "
"Using populate_buy_trend from the strategy.", caplog)
assert hasattr(x, "ticker_interval")
@ -188,8 +201,10 @@ def test_hyperoptresolver_noname(default_conf):
def test_hyperoptlossresolver(mocker, default_conf, caplog) -> None:
hl = DefaultHyperOptLoss
mocker.patch('freqtrade.resolvers.hyperopt_resolver.HyperOptLossResolver.load_object',
MagicMock(return_value=hl))
mocker.patch(
'freqtrade.resolvers.hyperopt_resolver.HyperOptLossResolver.load_object',
MagicMock(return_value=hl)
)
x = HyperOptLossResolver.load_hyperoptloss(default_conf)
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)
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)
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)
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)
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:
patched_configuration_load_config_file(mocker, default_conf)
mocker.patch('freqtrade.data.history.load_pair_history', MagicMock(return_value=pd.DataFrame))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))))
mocker.patch(
'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
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)
with pytest.raises(OperationalException, match='No data found. Terminating.'):
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)
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)
start_hyperopt(pargs)
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:
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, 600, datetime(2019, 1, 1),
datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(hyperopt_results, 600 + 100, datetime(2019, 1, 1),
datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(hyperopt_results, 600 - 100, datetime(2019, 1, 1),
datetime(2019, 5, 1))
correct = hl.hyperopt_loss_function(hyperopt_results, 600,
datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(hyperopt_results, 600 + 100,
datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(hyperopt_results, 600 - 100,
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over > 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
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
longer = hl.hyperopt_loss_function(hyperopt_results, 100, datetime(2019, 1, 1),
datetime(2019, 5, 1))
shorter = hl.hyperopt_loss_function(resultsb, 100, datetime(2019, 1, 1), datetime(2019, 5, 1))
longer = hl.hyperopt_loss_function(hyperopt_results, 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
@ -285,11 +323,12 @@ def test_loss_calculation_has_limited_profit(default_conf, hyperopt_results) ->
results_under['profit_percent'] = hyperopt_results['profit_percent'] / 2
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, 600, datetime(2019, 1, 1),
datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, 600, datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, 600, datetime(2019, 1, 1),
datetime(2019, 5, 1))
correct = hl.hyperopt_loss_function(hyperopt_results, 600,
datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, 600,
datetime(2019, 1, 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 under > correct
@ -304,10 +343,10 @@ def test_sharpe_loss_prefers_higher_profits(default_conf, hyperopt_results) -> N
hl = HyperOptLossResolver.load_hyperoptloss(default_conf)
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), datetime(2019, 1, 1),
datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), datetime(2019, 1, 1),
datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < 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)
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), datetime(2019, 1, 1),
datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), datetime(2019, 1, 1),
datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < 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)
correct = hl.hyperopt_loss_function(hyperopt_results, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results), datetime(2019, 1, 1),
datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results), datetime(2019, 1, 1),
datetime(2019, 5, 1))
over = hl.hyperopt_loss_function(results_over, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
under = hl.hyperopt_loss_function(results_under, len(hyperopt_results),
datetime(2019, 1, 1), datetime(2019, 5, 1))
assert over < 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:
hyperopt.current_best_loss = 2
hyperopt.total_epochs = 2
hyperopt.print_results({
hyperopt.print_results(
{
'is_best': True,
'loss': 1,
'current_epoch': 2, # This starts from 1 (in a human-friendly manner)
'results_explanation': 'foo.',
'is_initial_point': False
})
}
)
out, err = capsys.readouterr()
assert ' 2/2: foo. Objective: 1.00000' in out
def test_no_log_if_loss_does_not_improve(hyperopt, caplog) -> None:
hyperopt.current_best_loss = 2
hyperopt.print_results({
hyperopt.print_results(
{
'is_best': False,
'loss': 3,
'current_epoch': 1,
})
}
)
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())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))))
mocker.patch(
'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{
'loss': 1,
'results_explanation': 'foo result',
'params': {
'buy': {},
'sell': {},
'roi': {},
'stoploss': 0.0
}
}]))
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result',
'params': {'buy': {}, 'sell': {}, 'roi': {}, 'stoploss': 0.0}}])
)
patch_exchange(mocker)
# Co-test loading ticker-interval from strategy
del default_conf['ticker_interval']
default_conf.update({
'config': 'config.json.example',
default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1,
'timerange': None,
'spaces': 'default',
'hyperopt_jobs': 1,
})
'hyperopt_jobs': 1, })
hyperopt = Hyperopt(default_conf)
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):
# 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']
df = pd.DataFrame.from_records(trades, columns=labels)
results_metrics = hyperopt._calculate_results_metrics(df)
@ -528,7 +568,11 @@ def test_format_results(hyperopt):
assert result.find('2.0000Σ %')
# 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)
results_metrics = hyperopt._calculate_results_metrics(df)
results['total_profit'] = results_metrics['total_profit']
@ -537,97 +581,32 @@ def test_format_results(hyperopt):
@pytest.mark.parametrize("spaces, expected_results", [
(['buy'], {
'buy': True,
'sell': False,
'roi': False,
'stoploss': False,
'trailing': False
}),
(['sell'], {
'buy': False,
'sell': True,
'roi': False,
'stoploss': False,
'trailing': False
}),
(['roi'], {
'buy': False,
'sell': False,
'roi': True,
'stoploss': False,
'trailing': False
}),
(['stoploss'], {
'buy': False,
'sell': False,
'roi': False,
'stoploss': True,
'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
}),
(['buy'],
{'buy': True, 'sell': False, 'roi': False, 'stoploss': False, 'trailing': False}),
(['sell'],
{'buy': False, 'sell': True, 'roi': False, 'stoploss': False, 'trailing': False}),
(['roi'],
{'buy': False, 'sell': False, 'roi': True, 'stoploss': False, 'trailing': False}),
(['stoploss'],
{'buy': False, 'sell': False, 'roi': False, 'stoploss': True, '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):
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'],
{'pair': 'UNITTEST/BTC'})
populate_buy_trend = hyperopt.custom_hyperopt.buy_strategy_generator({
populate_buy_trend = hyperopt.custom_hyperopt.buy_strategy_generator(
{
'adx-value': 20,
'fastd-value': 20,
'mfi-value': 20,
@ -663,7 +643,8 @@ def test_buy_strategy_generator(hyperopt, testdatadir) -> None:
'mfi-enabled': True,
'rsi-enabled': True,
'trigger': 'bb_lower'
})
}
)
result = populate_buy_trend(dataframe, {'pair': 'UNITTEST/BTC'})
# Check if some indicators are generated. We will not test all of them
assert 'buy' in result
@ -679,14 +660,20 @@ def test_backtest_params(mocker, default_conf) -> None:
'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']
backtest_result = pd.DataFrame.from_records(trades, columns=labels)
mocker.patch('freqtrade.optimize.hyperopt.Backtesting.backtest',
MagicMock(return_value=backtest_result))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(Arrow(2017, 12, 10), Arrow(2017, 12, 13))))
mocker.patch(
'freqtrade.optimize.hyperopt.Backtesting.backtest',
MagicMock(return_value=backtest_result)
)
mocker.patch(
'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(Arrow(2017, 12, 10), Arrow(2017, 12, 13)))
)
patch_exchange(mocker)
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,
}
response_expected = {
'loss':
1.9840569076926293,
'results_explanation':
(' 1 trades. Avg profit 2.31%. Total profit 0.00023300 BTC '
'( 2.31\N{GREEK CAPITAL LETTER SIGMA}%). Avg duration 100.0 min.').encode(
locale.getpreferredencoding(), 'replace').decode('utf-8'),
'params_details': {
'buy': {
'adx-enabled': False,
'loss': 1.9840569076926293,
'results_explanation': (' 1 trades. Avg profit 2.31%. Total profit 0.00023300 BTC '
'( 2.31\N{GREEK CAPITAL LETTER SIGMA}%). Avg duration 100.0 min.'
).encode(locale.getpreferredencoding(), 'replace').decode('utf-8'),
'params_details': {'buy': {'adx-enabled': False,
'adx-value': 0,
'fastd-enabled': True,
'fastd-value': 35,
@ -738,16 +721,12 @@ def test_backtest_params(mocker, default_conf) -> None:
'mfi-value': 0,
'rsi-enabled': False,
'rsi-value': 0,
'trigger': 'macd_cross_signal'
},
'roi': {
0: 0.12000000000000001,
'trigger': 'macd_cross_signal'},
'roi': {0: 0.12000000000000001,
20.0: 0.02,
50.0: 0.01,
110.0: 0
},
'sell': {
'sell-adx-enabled': False,
110.0: 0},
'sell': {'sell-adx-enabled': False,
'sell-adx-value': 0,
'sell-fastd-enabled': True,
'sell-fastd-value': 75,
@ -755,29 +734,19 @@ def test_backtest_params(mocker, default_conf) -> None:
'sell-mfi-value': 0,
'sell-rsi-enabled': False,
'sell-rsi-value': 0,
'sell-trigger': 'macd_cross_signal'
},
'stoploss': {
'stoploss': -0.4
},
'trailing': {
'trailing_only_offset_is_reached': False,
'sell-trigger': 'macd_cross_signal'},
'stoploss': {'stoploss': -0.4},
'trailing': {'trailing_only_offset_is_reached': False,
'trailing_stop': True,
'trailing_stop_positive': 0.02,
'trailing_stop_positive_offset': 0.07
}
},
'params_dict':
optimizer_param,
'results_metrics': {
'avg_profit': 2.3117,
'trailing_stop_positive_offset': 0.07}},
'params_dict': optimizer_param,
'results_metrics': {'avg_profit': 2.3117,
'duration': 100.0,
'profit': 2.3117,
'total_profit': 0.000233,
'trade_count': 1
},
'total_profit':
0.00023300
'trade_count': 1},
'total_profit': 0.00023300
}
hyperopt = Hyperopt(default_conf)
@ -788,8 +757,7 @@ def test_backtest_params(mocker, default_conf) -> None:
def test_clean_hyperopt(mocker, default_conf, caplog):
patch_exchange(mocker)
default_conf.update({
'config': 'config.json.example',
default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1,
'timerange': None,
@ -807,8 +775,7 @@ def test_clean_hyperopt(mocker, default_conf, caplog):
def test_continue_hyperopt(mocker, default_conf, caplog):
patch_exchange(mocker)
default_conf.update({
'config': 'config.json.example',
default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1,
'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())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))))
mocker.patch(
'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{
'loss': 1,
'results_explanation': 'foo result',
'params': {},
'params_details': {
'buy': {
'mfi-value': None
},
'sell': {
'sell-mfi-value': None
},
'roi': {},
'stoploss': {
'stoploss': None
},
'trailing': {
'trailing_stop': None
}
}
}]))
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {},
'params_details': {'buy': {'mfi-value': None},
'sell': {'sell-mfi-value': None},
'roi': {}, 'stoploss': {'stoploss': None},
'trailing': {'trailing_stop': None}}}])
)
patch_exchange(mocker)
default_conf.update({
'config': 'config.json.example',
default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1,
'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())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))))
mocker.patch(
'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{
'loss': 1,
'results_explanation': 'foo result',
'params': {},
'params_details': {
'buy': {
'mfi-value': None
},
'sell': {
'sell-mfi-value': None
},
'roi': {},
'stoploss': {
'stoploss': None
}
}
}]))
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {},
'params_details': {'buy': {'mfi-value': None},
'sell': {'sell-mfi-value': None},
'roi': {}, 'stoploss': {'stoploss': None}}}])
)
patch_exchange(mocker)
default_conf.update({
'config': 'config.json.example',
default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1,
'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())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))))
mocker.patch(
'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{
'loss': 1,
'results_explanation': 'foo result',
'params': {},
'params_details': {
'roi': {},
'stoploss': {
'stoploss': None
}
}
}]))
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {},
'params_details': {'roi': {}, 'stoploss': {'stoploss': None}}}])
)
patch_exchange(mocker)
default_conf.update({
'config': 'config.json.example',
default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1,
'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())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))))
mocker.patch(
'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{
'loss': 1,
'results_explanation': 'foo result',
'params': {
'stoploss': 0.0
}
}]))
'loss': 1, 'results_explanation': 'foo result', 'params': {'stoploss': 0.0}}])
)
patch_exchange(mocker)
default_conf.update({
'config': 'config.json.example',
default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1,
'timerange': None,
'spaces': 'roi stoploss',
'hyperopt_jobs': 1,
})
'hyperopt_jobs': 1, })
hyperopt = Hyperopt(default_conf)
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.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))))
mocker.patch(
'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
patch_exchange(mocker)
default_conf.update({
'config': 'config.json.example',
default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1,
'timerange': None,
'spaces': 'all',
'hyperopt_jobs': 1,
})
'hyperopt_jobs': 1, })
hyperopt = Hyperopt(default_conf)
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())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))))
mocker.patch(
'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{
'loss': 1,
'results_explanation': 'foo result',
'params': {}
}]))
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}])
)
patch_exchange(mocker)
default_conf.update({
'config': 'config.json.example',
default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1,
'timerange': None,
'spaces': 'buy',
'hyperopt_jobs': 1,
})
'hyperopt_jobs': 1, })
hyperopt = Hyperopt(default_conf)
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())
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))))
mocker.patch(
'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_backtest_parallel',
MagicMock(return_value=[{
'loss': 1,
'results_explanation': 'foo result',
'params': {}
}]))
MagicMock(return_value=[{'loss': 1, 'results_explanation': 'foo result', 'params': {}}])
)
patch_exchange(mocker)
default_conf.update({
'config': 'config.json.example',
default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1,
'timerange': None,
'spaces': 'sell',
'hyperopt_jobs': 1,
})
'hyperopt_jobs': 1, })
hyperopt = Hyperopt(default_conf)
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.backtesting.Backtesting.load_bt_data',
MagicMock(return_value=(MagicMock(), None)))
mocker.patch('freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))))
mocker.patch(
'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
patch_exchange(mocker)
default_conf.update({
'config': 'config.json.example',
default_conf.update({'config': 'config.json.example',
'hyperopt': 'DefaultHyperOpt',
'epochs': 1,
'timerange': None,
'spaces': space,
'hyperopt_jobs': 1,
})
'hyperopt_jobs': 1, })
hyperopt = Hyperopt(default_conf)
hyperopt.backtesting.strategy.tickerdata_to_dataframe = MagicMock()