2018-02-12 06:10:21 +00:00
|
|
|
"""
|
|
|
|
This module contains the argument manager class
|
|
|
|
"""
|
|
|
|
import argparse
|
|
|
|
import re
|
2018-07-04 07:31:35 +00:00
|
|
|
from typing import List, NamedTuple, Optional
|
2019-07-11 18:23:23 +00:00
|
|
|
|
2018-04-27 21:16:34 +00:00
|
|
|
import arrow
|
2019-07-25 23:21:31 +00:00
|
|
|
from freqtrade.configuration.cli_options import AVAILABLE_CLI_OPTIONS
|
2019-07-25 18:42:08 +00:00
|
|
|
from freqtrade import constants
|
2019-07-06 22:20:26 +00:00
|
|
|
|
2019-07-21 12:32:29 +00:00
|
|
|
ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_data_dir"]
|
2019-07-06 22:20:26 +00:00
|
|
|
|
2019-06-22 18:10:35 +00:00
|
|
|
ARGS_STRATEGY = ["strategy", "strategy_path"]
|
|
|
|
|
2019-07-18 07:45:47 +00:00
|
|
|
ARGS_MAIN = ARGS_COMMON + ARGS_STRATEGY + ["db_url", "sd_notify"]
|
2019-06-22 18:10:35 +00:00
|
|
|
|
2019-07-06 21:51:01 +00:00
|
|
|
ARGS_COMMON_OPTIMIZE = ["ticker_interval", "timerange",
|
2019-06-22 18:10:35 +00:00
|
|
|
"max_open_trades", "stake_amount", "refresh_pairs"]
|
|
|
|
|
|
|
|
ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions",
|
|
|
|
"live", "strategy_list", "export", "exportfilename"]
|
|
|
|
|
2019-07-22 16:37:34 +00:00
|
|
|
ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path",
|
|
|
|
"position_stacking", "epochs", "spaces",
|
2019-06-22 18:10:35 +00:00
|
|
|
"use_max_market_positions", "print_all", "hyperopt_jobs",
|
2019-07-15 18:17:15 +00:00
|
|
|
"hyperopt_random_state", "hyperopt_min_trades",
|
2019-07-16 04:27:23 +00:00
|
|
|
"hyperopt_continue", "hyperopt_loss"]
|
2019-06-22 18:10:35 +00:00
|
|
|
|
|
|
|
ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"]
|
|
|
|
|
2019-07-06 21:48:39 +00:00
|
|
|
ARGS_LIST_EXCHANGES = ["print_one_column"]
|
2019-06-22 18:10:35 +00:00
|
|
|
|
2019-07-21 12:13:38 +00:00
|
|
|
ARGS_CREATE_USERDIR = ["user_data_dir"]
|
|
|
|
|
2019-06-22 18:21:42 +00:00
|
|
|
ARGS_DOWNLOADER = ARGS_COMMON + ["pairs", "pairs_file", "days", "exchange", "timeframes", "erase"]
|
2019-06-22 18:10:35 +00:00
|
|
|
|
|
|
|
ARGS_PLOT_DATAFRAME = (ARGS_COMMON + ARGS_STRATEGY +
|
2019-06-22 18:32:54 +00:00
|
|
|
["pairs", "indicators1", "indicators2", "plot_limit", "db_url",
|
|
|
|
"trade_source", "export", "exportfilename", "timerange",
|
|
|
|
"refresh_pairs", "live"])
|
2019-06-22 18:10:35 +00:00
|
|
|
|
2019-06-22 18:27:29 +00:00
|
|
|
ARGS_PLOT_PROFIT = (ARGS_COMMON + ARGS_STRATEGY +
|
2019-06-29 14:57:04 +00:00
|
|
|
["pairs", "timerange", "export", "exportfilename", "db_url", "trade_source"])
|
2019-06-22 18:27:29 +00:00
|
|
|
|
2019-06-22 18:10:35 +00:00
|
|
|
|
2018-06-05 21:34:26 +00:00
|
|
|
class TimeRange(NamedTuple):
|
2018-06-05 22:10:18 +00:00
|
|
|
"""
|
2019-07-06 22:20:26 +00:00
|
|
|
NamedTuple defining timerange inputs.
|
2018-06-05 22:10:18 +00:00
|
|
|
[start/stop]type defines if [start/stop]ts shall be used.
|
2019-07-06 22:20:26 +00:00
|
|
|
if *type is None, don't use corresponding startvalue.
|
2018-06-05 22:10:18 +00:00
|
|
|
"""
|
2018-06-05 21:34:26 +00:00
|
|
|
starttype: Optional[str] = None
|
|
|
|
stoptype: Optional[str] = None
|
|
|
|
startts: int = 0
|
|
|
|
stopts: int = 0
|
|
|
|
|
|
|
|
|
2018-02-12 06:10:21 +00:00
|
|
|
class Arguments(object):
|
|
|
|
"""
|
|
|
|
Arguments Class. Manage the arguments received by the cli
|
|
|
|
"""
|
2019-07-18 14:55:35 +00:00
|
|
|
def __init__(self, args: Optional[List[str]], description: str,
|
|
|
|
no_default_config: bool = False) -> None:
|
2018-02-12 06:10:21 +00:00
|
|
|
self.args = args
|
2019-07-18 14:55:35 +00:00
|
|
|
self._parsed_arg: Optional[argparse.Namespace] = None
|
2018-02-12 06:10:21 +00:00
|
|
|
self.parser = argparse.ArgumentParser(description=description)
|
2019-07-18 14:55:35 +00:00
|
|
|
self._no_default_config = no_default_config
|
2018-03-05 01:51:57 +00:00
|
|
|
|
2018-03-17 21:20:45 +00:00
|
|
|
def _load_args(self) -> None:
|
2019-07-18 14:55:35 +00:00
|
|
|
self._build_args(optionlist=ARGS_MAIN)
|
2018-02-12 06:10:21 +00:00
|
|
|
self._build_subcommands()
|
|
|
|
|
2018-03-17 23:02:02 +00:00
|
|
|
def get_parsed_arg(self) -> argparse.Namespace:
|
2018-02-12 06:10:21 +00:00
|
|
|
"""
|
|
|
|
Return the list of arguments
|
|
|
|
:return: List[str] List of arguments
|
|
|
|
"""
|
2019-07-18 14:55:35 +00:00
|
|
|
if self._parsed_arg is None:
|
2018-03-05 01:51:57 +00:00
|
|
|
self._load_args()
|
2019-07-18 14:55:35 +00:00
|
|
|
self._parsed_arg = self._parse_args()
|
2018-02-12 06:10:21 +00:00
|
|
|
|
2019-07-18 14:55:35 +00:00
|
|
|
return self._parsed_arg
|
2018-02-12 06:10:21 +00:00
|
|
|
|
2019-07-18 14:55:35 +00:00
|
|
|
def _parse_args(self) -> argparse.Namespace:
|
2018-02-12 06:10:21 +00:00
|
|
|
"""
|
|
|
|
Parses given arguments and returns an argparse Namespace instance.
|
|
|
|
"""
|
|
|
|
parsed_arg = self.parser.parse_args(self.args)
|
|
|
|
|
2019-02-19 12:14:47 +00:00
|
|
|
# Workaround issue in argparse with action='append' and default value
|
|
|
|
# (see https://bugs.python.org/issue16399)
|
2019-07-18 14:55:35 +00:00
|
|
|
if not self._no_default_config and parsed_arg.config is None:
|
2019-02-19 12:14:47 +00:00
|
|
|
parsed_arg.config = [constants.DEFAULT_CONFIG]
|
|
|
|
|
2018-02-12 06:10:21 +00:00
|
|
|
return parsed_arg
|
|
|
|
|
2019-07-18 14:55:35 +00:00
|
|
|
def _build_args(self, optionlist, parser=None):
|
2019-06-22 18:10:35 +00:00
|
|
|
parser = parser or self.parser
|
|
|
|
|
|
|
|
for val in optionlist:
|
|
|
|
opt = AVAILABLE_CLI_OPTIONS[val]
|
2019-07-02 18:33:27 +00:00
|
|
|
parser.add_argument(*opt.cli, dest=val, **opt.kwargs)
|
2019-06-22 18:10:35 +00:00
|
|
|
|
2018-02-12 06:10:21 +00:00
|
|
|
def _build_subcommands(self) -> None:
|
|
|
|
"""
|
2019-06-18 22:53:38 +00:00
|
|
|
Builds and attaches all subcommands.
|
2018-02-12 06:10:21 +00:00
|
|
|
:return: None
|
|
|
|
"""
|
2019-05-28 17:25:01 +00:00
|
|
|
from freqtrade.optimize import start_backtesting, start_hyperopt, start_edge
|
2019-07-21 12:13:38 +00:00
|
|
|
from freqtrade.utils import start_create_userdir, start_list_exchanges
|
2018-02-12 06:10:21 +00:00
|
|
|
subparsers = self.parser.add_subparsers(dest='subparser')
|
|
|
|
|
|
|
|
# Add backtesting subcommand
|
2019-02-19 12:14:47 +00:00
|
|
|
backtesting_cmd = subparsers.add_parser('backtesting', help='Backtesting module.')
|
2019-05-25 18:00:31 +00:00
|
|
|
backtesting_cmd.set_defaults(func=start_backtesting)
|
2019-07-18 14:55:35 +00:00
|
|
|
self._build_args(optionlist=ARGS_BACKTEST, parser=backtesting_cmd)
|
2018-02-12 06:10:21 +00:00
|
|
|
|
2018-11-14 11:37:15 +00:00
|
|
|
# Add edge subcommand
|
2019-02-19 12:14:47 +00:00
|
|
|
edge_cmd = subparsers.add_parser('edge', help='Edge module.')
|
2019-05-28 17:25:01 +00:00
|
|
|
edge_cmd.set_defaults(func=start_edge)
|
2019-07-18 14:55:35 +00:00
|
|
|
self._build_args(optionlist=ARGS_EDGE, parser=edge_cmd)
|
2018-11-14 11:37:15 +00:00
|
|
|
|
2018-02-12 06:10:21 +00:00
|
|
|
# Add hyperopt subcommand
|
2019-02-19 12:14:47 +00:00
|
|
|
hyperopt_cmd = subparsers.add_parser('hyperopt', help='Hyperopt module.')
|
2019-05-25 18:00:31 +00:00
|
|
|
hyperopt_cmd.set_defaults(func=start_hyperopt)
|
2019-07-18 14:55:35 +00:00
|
|
|
self._build_args(optionlist=ARGS_HYPEROPT, parser=hyperopt_cmd)
|
2018-02-12 06:10:21 +00:00
|
|
|
|
2019-07-21 12:13:38 +00:00
|
|
|
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)
|
|
|
|
|
2019-06-12 09:33:20 +00:00
|
|
|
# Add list-exchanges subcommand
|
2019-06-14 19:04:29 +00:00
|
|
|
list_exchanges_cmd = subparsers.add_parser(
|
|
|
|
'list-exchanges',
|
|
|
|
help='Print available exchanges.'
|
|
|
|
)
|
2019-06-12 09:33:20 +00:00
|
|
|
list_exchanges_cmd.set_defaults(func=start_list_exchanges)
|
2019-07-18 14:55:35 +00:00
|
|
|
self._build_args(optionlist=ARGS_LIST_EXCHANGES, parser=list_exchanges_cmd)
|
2019-06-12 09:33:20 +00:00
|
|
|
|
2018-02-12 06:10:21 +00:00
|
|
|
@staticmethod
|
2018-06-05 21:34:26 +00:00
|
|
|
def parse_timerange(text: Optional[str]) -> TimeRange:
|
2018-02-12 06:10:21 +00:00
|
|
|
"""
|
|
|
|
Parse the value of the argument --timerange to determine what is the range desired
|
|
|
|
:param text: value from --timerange
|
|
|
|
:return: Start and End range period
|
|
|
|
"""
|
|
|
|
if text is None:
|
2018-06-08 17:46:07 +00:00
|
|
|
return TimeRange(None, None, 0, 0)
|
2018-02-12 06:10:21 +00:00
|
|
|
syntax = [(r'^-(\d{8})$', (None, 'date')),
|
|
|
|
(r'^(\d{8})-$', ('date', None)),
|
|
|
|
(r'^(\d{8})-(\d{8})$', ('date', 'date')),
|
2018-06-02 18:59:18 +00:00
|
|
|
(r'^-(\d{10})$', (None, 'date')),
|
|
|
|
(r'^(\d{10})-$', ('date', None)),
|
|
|
|
(r'^(\d{10})-(\d{10})$', ('date', 'date')),
|
2018-02-12 06:10:21 +00:00
|
|
|
(r'^(-\d+)$', (None, 'line')),
|
|
|
|
(r'^(\d+)-$', ('line', None)),
|
|
|
|
(r'^(\d+)-(\d+)$', ('index', 'index'))]
|
|
|
|
for rex, stype in syntax:
|
|
|
|
# Apply the regular expression to text
|
|
|
|
match = re.match(rex, text)
|
|
|
|
if match: # Regex has matched
|
|
|
|
rvals = match.groups()
|
|
|
|
index = 0
|
2018-06-05 21:34:26 +00:00
|
|
|
start: int = 0
|
|
|
|
stop: int = 0
|
2018-02-12 06:10:21 +00:00
|
|
|
if stype[0]:
|
2018-05-31 19:22:46 +00:00
|
|
|
starts = rvals[index]
|
2018-06-05 21:14:28 +00:00
|
|
|
if stype[0] == 'date' and len(starts) == 8:
|
|
|
|
start = arrow.get(starts, 'YYYYMMDD').timestamp
|
2018-04-27 21:16:34 +00:00
|
|
|
else:
|
2018-05-31 19:22:46 +00:00
|
|
|
start = int(starts)
|
2018-02-12 06:10:21 +00:00
|
|
|
index += 1
|
|
|
|
if stype[1]:
|
2018-05-31 19:22:46 +00:00
|
|
|
stops = rvals[index]
|
2018-06-05 21:14:28 +00:00
|
|
|
if stype[1] == 'date' and len(stops) == 8:
|
|
|
|
stop = arrow.get(stops, 'YYYYMMDD').timestamp
|
2018-04-27 21:16:34 +00:00
|
|
|
else:
|
2018-05-31 19:22:46 +00:00
|
|
|
stop = int(stops)
|
2018-06-05 21:34:26 +00:00
|
|
|
return TimeRange(stype[0], stype[1], start, stop)
|
2018-02-12 06:10:21 +00:00
|
|
|
raise Exception('Incorrect syntax for timerange "%s"' % text)
|