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",
"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"
]
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"]
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',
# Use custom message when no subhandler is added
# shown from `main.py`
# required=True
)
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.',
parents=[_common_parser, _strategy_parser],
)
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',
help="Create user-data directory.",
)
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.",
"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.',
'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.',
'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.',
'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, '
'overriding max_open_trades to: %s ...', config.get('max_open_trades'))
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
@ -435,7 +405,7 @@ class Configuration:
configuration instead of the content)
"""
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]})
if logfun:

View File

@ -1,4 +1,5 @@
# pragma pylint: disable=too-few-public-methods
"""
bot constants
"""
@ -18,12 +19,11 @@ 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
MATH_CLOSE_PREC = 1e-14 # Precision used for float comparisons
DEFAULT_DATAFRAME_COLUMNS = ['date', 'open', 'high', 'low', 'close', 'volume']
USERPATH_HYPEROPTS = 'hyperopts'
@ -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,126 +184,71 @@ 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': {
'type': 'string',
'enum': AVAILABLE_DATAHANDLERS,
'default': 'json'
'enum': AVAILABLE_DATAHANDLERS,
'default': 'json'
},
'dataformat_trades': {
'type': 'string',
'enum': AVAILABLE_DATAHANDLERS,
'default': 'jsongz'
'enum': AVAILABLE_DATAHANDLERS,
'default': 'jsongz'
}
},
'definitions': {
'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,33 +384,31 @@ 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',
stralign="right"))
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

File diff suppressed because it is too large Load Diff