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', parents=[_common_parser, _strategy_parser],
help='Hyperopt module.', )
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
@ -435,7 +405,7 @@ class Configuration:
configuration instead of the content) configuration instead of the content)
""" """
if (argname in self.args and self.args[argname] is not None if (argname in self.args and self.args[argname] is not None
and self.args[argname] is not False): and self.args[argname] is not False):
config.update({argname: self.args[argname]}) config.update({argname: self.args[argname]})
if logfun: if logfun:

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,12 +19,11 @@ 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
DEFAULT_DATAFRAME_COLUMNS = ['date', 'open', 'high', 'low', 'close', 'volume'] DEFAULT_DATAFRAME_COLUMNS = ['date', 'open', 'high', 'low', 'close', 'volume']
USERPATH_HYPEROPTS = 'hyperopts' USERPATH_HYPEROPTS = 'hyperopts'
@ -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,126 +184,71 @@ 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': {
'type': 'string', 'type': 'string',
'enum': AVAILABLE_DATAHANDLERS, 'enum': AVAILABLE_DATAHANDLERS,
'default': 'json' 'default': 'json'
}, },
'dataformat_trades': { 'dataformat_trades': {
'type': 'string', 'type': 'string',
'enum': AVAILABLE_DATAHANDLERS, 'enum': AVAILABLE_DATAHANDLERS,
'default': 'jsongz' 'default': 'jsongz'
} }
}, },
'definitions': { 'definitions': {
'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,33 +384,31 @@ 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'), stralign="right"))
headers='keys',
tablefmt='psql',
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

File diff suppressed because it is too large Load Diff