Merge pull request #2260 from freqtrade/args_vars
Configuration/Arguments refactoing (don't pass Namespace around).
This commit is contained in:
commit
19ce7180be
@ -91,7 +91,8 @@ df.groupby("pair")["sell_reason"].value_counts()
|
|||||||
|
|
||||||
### Load multiple configuration files
|
### Load multiple configuration files
|
||||||
|
|
||||||
This option can be useful to inspect the results of passing in multiple configs
|
This option can be useful to inspect the results of passing in multiple configs.
|
||||||
|
This will also run through the whole Configuration initialization, so the configuration is completely initialized to be passed to other methods.
|
||||||
|
|
||||||
``` python
|
``` python
|
||||||
import json
|
import json
|
||||||
@ -101,7 +102,16 @@ from freqtrade.configuration import Configuration
|
|||||||
config = Configuration.from_files(["config1.json", "config2.json"])
|
config = Configuration.from_files(["config1.json", "config2.json"])
|
||||||
|
|
||||||
# Show the config in memory
|
# Show the config in memory
|
||||||
print(json.dumps(config, indent=1))
|
print(json.dumps(config, indent=2))
|
||||||
|
```
|
||||||
|
|
||||||
|
For Interactive environments, have an additional configuration specifying `user_data_dir` and pass this in last, so you don't have to change directories while running the bot.
|
||||||
|
Best avoid relative paths, since this starts at the storage location of the jupyter notebook, unless the directory is changed.
|
||||||
|
|
||||||
|
``` json
|
||||||
|
{
|
||||||
|
"user_data_dir": "~/.freqtrade/"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Load exchange data to a pandas dataframe
|
### Load exchange data to a pandas dataframe
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
This module contains the argument manager class
|
This module contains the argument manager class
|
||||||
"""
|
"""
|
||||||
import argparse
|
import argparse
|
||||||
from typing import List, Optional
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from freqtrade.configuration.cli_options import AVAILABLE_CLI_OPTIONS
|
|
||||||
from freqtrade import constants
|
from freqtrade import constants
|
||||||
|
from freqtrade.configuration.cli_options import AVAILABLE_CLI_OPTIONS
|
||||||
|
|
||||||
ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_data_dir"]
|
ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_data_dir"]
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", "db_
|
|||||||
ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url",
|
ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url",
|
||||||
"trade_source", "ticker_interval"]
|
"trade_source", "ticker_interval"]
|
||||||
|
|
||||||
NO_CONF_REQURIED = ["download-data", "plot-dataframe", "plot-profit"]
|
NO_CONF_REQURIED = ["create-userdir", "download-data", "plot-dataframe", "plot-profit"]
|
||||||
|
|
||||||
|
|
||||||
class Arguments:
|
class Arguments:
|
||||||
@ -57,7 +57,7 @@ class Arguments:
|
|||||||
self._build_args(optionlist=ARGS_MAIN)
|
self._build_args(optionlist=ARGS_MAIN)
|
||||||
self._build_subcommands()
|
self._build_subcommands()
|
||||||
|
|
||||||
def get_parsed_arg(self) -> argparse.Namespace:
|
def get_parsed_arg(self) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Return the list of arguments
|
Return the list of arguments
|
||||||
:return: List[str] List of arguments
|
:return: List[str] List of arguments
|
||||||
@ -66,7 +66,7 @@ class Arguments:
|
|||||||
self._load_args()
|
self._load_args()
|
||||||
self._parsed_arg = self._parse_args()
|
self._parsed_arg = self._parse_args()
|
||||||
|
|
||||||
return self._parsed_arg
|
return vars(self._parsed_arg)
|
||||||
|
|
||||||
def _parse_args(self) -> argparse.Namespace:
|
def _parse_args(self) -> argparse.Namespace:
|
||||||
"""
|
"""
|
||||||
|
@ -3,7 +3,6 @@ This module contains the configuration class
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
from argparse import Namespace
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Dict, List, Optional
|
from typing import Any, Callable, Dict, List, Optional
|
||||||
@ -28,7 +27,7 @@ class 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: Namespace, 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
|
||||||
self.runmode = runmode
|
self.runmode = runmode
|
||||||
@ -50,9 +49,16 @@ class Configuration:
|
|||||||
and merging their contents.
|
and merging their contents.
|
||||||
Files are loaded in sequence, parameters in later configuration files
|
Files are loaded in sequence, parameters in later configuration files
|
||||||
override the same parameter from an earlier file (last definition wins).
|
override the same parameter from an earlier file (last definition wins).
|
||||||
|
Runs through the whole Configuration initialization, so all expected config entries
|
||||||
|
are available to interactive environments.
|
||||||
:param files: List of file paths
|
:param files: List of file paths
|
||||||
:return: configuration dictionary
|
:return: configuration dictionary
|
||||||
"""
|
"""
|
||||||
|
c = Configuration({"config": files}, RunMode.OTHER)
|
||||||
|
return c.get_config()
|
||||||
|
|
||||||
|
def load_from_files(self, files: List[str]) -> Dict[str, Any]:
|
||||||
|
|
||||||
# Keep this method as staticmethod, so it can be used from interactive environments
|
# Keep this method as staticmethod, so it can be used from interactive environments
|
||||||
config: Dict[str, Any] = {}
|
config: Dict[str, Any] = {}
|
||||||
|
|
||||||
@ -82,7 +88,7 @@ class Configuration:
|
|||||||
:return: Configuration dictionary
|
:return: Configuration dictionary
|
||||||
"""
|
"""
|
||||||
# Load all configs
|
# Load all configs
|
||||||
config: Dict[str, Any] = Configuration.from_files(self.args.config)
|
config: Dict[str, Any] = self.load_from_files(self.args["config"])
|
||||||
|
|
||||||
self._process_common_options(config)
|
self._process_common_options(config)
|
||||||
|
|
||||||
@ -107,13 +113,10 @@ class Configuration:
|
|||||||
the -v/--verbose, --logfile options
|
the -v/--verbose, --logfile options
|
||||||
"""
|
"""
|
||||||
# Log level
|
# Log level
|
||||||
if 'verbosity' in self.args and self.args.verbosity:
|
config.update({'verbosity': self.args.get("verbosity", 0)})
|
||||||
config.update({'verbosity': self.args.verbosity})
|
|
||||||
else:
|
|
||||||
config.update({'verbosity': 0})
|
|
||||||
|
|
||||||
if 'logfile' in self.args and self.args.logfile:
|
if 'logfile' in self.args and self.args["logfile"]:
|
||||||
config.update({'logfile': self.args.logfile})
|
config.update({'logfile': self.args["logfile"]})
|
||||||
|
|
||||||
setup_logging(config)
|
setup_logging(config)
|
||||||
|
|
||||||
@ -122,15 +125,15 @@ class Configuration:
|
|||||||
self._process_logging_options(config)
|
self._process_logging_options(config)
|
||||||
|
|
||||||
# Set strategy if not specified in config and or if it's non default
|
# Set strategy if not specified in config and or if it's non default
|
||||||
if self.args.strategy != constants.DEFAULT_STRATEGY or not config.get('strategy'):
|
if self.args.get("strategy") != constants.DEFAULT_STRATEGY or not config.get('strategy'):
|
||||||
config.update({'strategy': self.args.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: {}')
|
logstring='Using additional Strategy lookup path: {}')
|
||||||
|
|
||||||
if ('db_url' in self.args and self.args.db_url and
|
if ('db_url' in self.args and self.args["db_url"] 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 ...')
|
||||||
|
|
||||||
if config.get('dry_run', False):
|
if config.get('dry_run', False):
|
||||||
@ -153,7 +156,7 @@ class Configuration:
|
|||||||
config['max_open_trades'] = float('inf')
|
config['max_open_trades'] = float('inf')
|
||||||
|
|
||||||
# Support for sd_notify
|
# Support for sd_notify
|
||||||
if 'sd_notify' in self.args and self.args.sd_notify:
|
if 'sd_notify' in self.args and self.args["sd_notify"]:
|
||||||
config['internals'].update({'sd_notify': True})
|
config['internals'].update({'sd_notify': True})
|
||||||
|
|
||||||
def _process_datadir_options(self, config: Dict[str, Any]) -> None:
|
def _process_datadir_options(self, config: Dict[str, Any]) -> None:
|
||||||
@ -162,12 +165,12 @@ class Configuration:
|
|||||||
--user-data, --datadir
|
--user-data, --datadir
|
||||||
"""
|
"""
|
||||||
# Check exchange parameter here - otherwise `datadir` might be wrong.
|
# Check exchange parameter here - otherwise `datadir` might be wrong.
|
||||||
if "exchange" in self.args and self.args.exchange:
|
if "exchange" in self.args and self.args["exchange"]:
|
||||||
config['exchange']['name'] = self.args.exchange
|
config['exchange']['name'] = self.args["exchange"]
|
||||||
logger.info(f"Using exchange {config['exchange']['name']}")
|
logger.info(f"Using exchange {config['exchange']['name']}")
|
||||||
|
|
||||||
if 'user_data_dir' in self.args and self.args.user_data_dir:
|
if 'user_data_dir' in self.args and self.args["user_data_dir"]:
|
||||||
config.update({'user_data_dir': self.args.user_data_dir})
|
config.update({'user_data_dir': self.args["user_data_dir"]})
|
||||||
elif 'user_data_dir' not in config:
|
elif 'user_data_dir' not in config:
|
||||||
# Default to cwd/user_data (legacy option ...)
|
# Default to cwd/user_data (legacy option ...)
|
||||||
config.update({'user_data_dir': str(Path.cwd() / "user_data")})
|
config.update({'user_data_dir': str(Path.cwd() / "user_data")})
|
||||||
@ -176,10 +179,7 @@ class Configuration:
|
|||||||
config['user_data_dir'] = create_userdata_dir(config['user_data_dir'], create_dir=False)
|
config['user_data_dir'] = create_userdata_dir(config['user_data_dir'], create_dir=False)
|
||||||
logger.info('Using user-data directory: %s ...', config['user_data_dir'])
|
logger.info('Using user-data directory: %s ...', config['user_data_dir'])
|
||||||
|
|
||||||
if 'datadir' in self.args and self.args.datadir:
|
config.update({'datadir': create_datadir(config, self.args.get("datadir", None))})
|
||||||
config.update({'datadir': create_datadir(config, self.args.datadir)})
|
|
||||||
else:
|
|
||||||
config.update({'datadir': create_datadir(config, None)})
|
|
||||||
logger.info('Using data directory: %s ...', config.get('datadir'))
|
logger.info('Using data directory: %s ...', config.get('datadir'))
|
||||||
|
|
||||||
def _process_optimize_options(self, config: Dict[str, Any]) -> None:
|
def _process_optimize_options(self, config: Dict[str, Any]) -> None:
|
||||||
@ -192,12 +192,12 @@ class Configuration:
|
|||||||
self._args_to_config(config, argname='position_stacking',
|
self._args_to_config(config, argname='position_stacking',
|
||||||
logstring='Parameter --enable-position-stacking detected ...')
|
logstring='Parameter --enable-position-stacking detected ...')
|
||||||
|
|
||||||
if 'use_max_market_positions' in self.args and not self.args.use_max_market_positions:
|
if 'use_max_market_positions' in self.args and not self.args["use_max_market_positions"]:
|
||||||
config.update({'use_max_market_positions': False})
|
config.update({'use_max_market_positions': False})
|
||||||
logger.info('Parameter --disable-max-market-positions detected ...')
|
logger.info('Parameter --disable-max-market-positions detected ...')
|
||||||
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('Parameter --max_open_trades detected, '
|
logger.info('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'))
|
||||||
else:
|
else:
|
||||||
@ -229,12 +229,12 @@ class Configuration:
|
|||||||
logstring='Storing backtest results to {} ...')
|
logstring='Storing backtest results to {} ...')
|
||||||
|
|
||||||
# Edge section:
|
# Edge section:
|
||||||
if 'stoploss_range' in self.args and self.args.stoploss_range:
|
if 'stoploss_range' in self.args and self.args["stoploss_range"]:
|
||||||
txt_range = eval(self.args.stoploss_range)
|
txt_range = eval(self.args["stoploss_range"])
|
||||||
config['edge'].update({'stoploss_range_min': txt_range[0]})
|
config['edge'].update({'stoploss_range_min': txt_range[0]})
|
||||||
config['edge'].update({'stoploss_range_max': txt_range[1]})
|
config['edge'].update({'stoploss_range_max': txt_range[1]})
|
||||||
config['edge'].update({'stoploss_range_step': txt_range[2]})
|
config['edge'].update({'stoploss_range_step': txt_range[2]})
|
||||||
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',
|
self._args_to_config(config, argname='hyperopt',
|
||||||
@ -254,7 +254,7 @@ class Configuration:
|
|||||||
self._args_to_config(config, argname='print_all',
|
self._args_to_config(config, 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"]:
|
||||||
logger.info('Parameter --no-color detected ...')
|
logger.info('Parameter --no-color detected ...')
|
||||||
config.update({'print_colorized': False})
|
config.update({'print_colorized': False})
|
||||||
else:
|
else:
|
||||||
@ -324,9 +324,9 @@ class Configuration:
|
|||||||
sample: logfun=len (prints the length of the found
|
sample: logfun=len (prints the length of the found
|
||||||
configuration instead of the content)
|
configuration instead of the content)
|
||||||
"""
|
"""
|
||||||
if argname in self.args and getattr(self.args, argname):
|
if argname in self.args and self.args[argname]:
|
||||||
|
|
||||||
config.update({argname: getattr(self.args, argname)})
|
config.update({argname: self.args[argname]})
|
||||||
if logfun:
|
if logfun:
|
||||||
logger.info(logstring.format(logfun(config[argname])))
|
logger.info(logstring.format(logfun(config[argname])))
|
||||||
else:
|
else:
|
||||||
@ -346,8 +346,8 @@ class Configuration:
|
|||||||
if "pairs" in config:
|
if "pairs" in config:
|
||||||
return
|
return
|
||||||
|
|
||||||
if "pairs_file" in self.args and self.args.pairs_file:
|
if "pairs_file" in self.args and self.args["pairs_file"]:
|
||||||
pairs_file = Path(self.args.pairs_file)
|
pairs_file = Path(self.args["pairs_file"])
|
||||||
logger.info(f'Reading pairs file "{pairs_file}".')
|
logger.info(f'Reading pairs file "{pairs_file}".')
|
||||||
# Download pairs from the pairs file if no config is specified
|
# Download pairs from the pairs file if no config is specified
|
||||||
# or if pairs file is specified explicitely
|
# or if pairs file is specified explicitely
|
||||||
@ -358,7 +358,7 @@ class Configuration:
|
|||||||
config['pairs'].sort()
|
config['pairs'].sort()
|
||||||
return
|
return
|
||||||
|
|
||||||
if "config" in self.args and self.args.config:
|
if "config" in self.args and self.args["config"]:
|
||||||
logger.info("Using pairlist from configuration.")
|
logger.info("Using pairlist from configuration.")
|
||||||
config['pairs'] = config.get('exchange', {}).get('pair_whitelist')
|
config['pairs'] = config.get('exchange', {}).get('pair_whitelist')
|
||||||
else:
|
else:
|
||||||
|
@ -11,7 +11,6 @@ if sys.version_info < (3, 6):
|
|||||||
|
|
||||||
# flake8: noqa E402
|
# flake8: noqa E402
|
||||||
import logging
|
import logging
|
||||||
from argparse import Namespace
|
|
||||||
from typing import Any, List
|
from typing import Any, List
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade import OperationalException
|
||||||
@ -32,12 +31,12 @@ def main(sysargv: List[str] = None) -> None:
|
|||||||
worker = None
|
worker = None
|
||||||
try:
|
try:
|
||||||
arguments = Arguments(sysargv)
|
arguments = Arguments(sysargv)
|
||||||
args: Namespace = arguments.get_parsed_arg()
|
args = arguments.get_parsed_arg()
|
||||||
|
|
||||||
# A subcommand has been issued.
|
# A subcommand has been issued.
|
||||||
# Means if Backtesting or Hyperopt have been called we exit the bot
|
# Means if Backtesting or Hyperopt have been called we exit the bot
|
||||||
if hasattr(args, 'func'):
|
if 'func' in args:
|
||||||
args.func(args)
|
args['func'](args)
|
||||||
# TODO: fetch return_code as returned by the command function here
|
# TODO: fetch return_code as returned by the command function here
|
||||||
return_code = 0
|
return_code = 0
|
||||||
else:
|
else:
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import logging
|
import logging
|
||||||
from argparse import Namespace
|
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from filelock import FileLock, Timeout
|
from filelock import FileLock, Timeout
|
||||||
@ -12,7 +11,7 @@ from freqtrade.utils import setup_utils_configuration
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def setup_configuration(args: Namespace, method: RunMode) -> Dict[str, Any]:
|
def setup_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Prepare the configuration for the Hyperopt module
|
Prepare the configuration for the Hyperopt module
|
||||||
:param args: Cli args from Arguments()
|
:param args: Cli args from Arguments()
|
||||||
@ -28,7 +27,7 @@ def setup_configuration(args: Namespace, method: RunMode) -> Dict[str, Any]:
|
|||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def start_backtesting(args: Namespace) -> None:
|
def start_backtesting(args: Dict[str, Any]) -> None:
|
||||||
"""
|
"""
|
||||||
Start Backtesting script
|
Start Backtesting script
|
||||||
:param args: Cli args from Arguments()
|
:param args: Cli args from Arguments()
|
||||||
@ -47,7 +46,7 @@ def start_backtesting(args: Namespace) -> None:
|
|||||||
backtesting.start()
|
backtesting.start()
|
||||||
|
|
||||||
|
|
||||||
def start_hyperopt(args: Namespace) -> None:
|
def start_hyperopt(args: Dict[str, Any]) -> None:
|
||||||
"""
|
"""
|
||||||
Start hyperopt script
|
Start hyperopt script
|
||||||
:param args: Cli args from Arguments()
|
:param args: Cli args from Arguments()
|
||||||
@ -85,7 +84,7 @@ def start_hyperopt(args: Namespace) -> None:
|
|||||||
# Same in Edge and Backtesting start() functions.
|
# Same in Edge and Backtesting start() functions.
|
||||||
|
|
||||||
|
|
||||||
def start_edge(args: Namespace) -> None:
|
def start_edge(args: Dict[str, Any]) -> None:
|
||||||
"""
|
"""
|
||||||
Start Edge script
|
Start Edge script
|
||||||
:param args: Cli args from Arguments()
|
:param args: Cli args from Arguments()
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
from argparse import Namespace
|
from typing import Any, Dict
|
||||||
|
|
||||||
from freqtrade import OperationalException
|
from freqtrade import OperationalException
|
||||||
from freqtrade.state import RunMode
|
from freqtrade.state import RunMode
|
||||||
from freqtrade.utils import setup_utils_configuration
|
from freqtrade.utils import setup_utils_configuration
|
||||||
|
|
||||||
|
|
||||||
def validate_plot_args(args: Namespace):
|
def validate_plot_args(args: Dict[str, Any]):
|
||||||
args_tmp = vars(args)
|
if not args.get('datadir') and not args.get('config'):
|
||||||
if not args_tmp.get('datadir') and not args_tmp.get('config'):
|
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
"You need to specify either `--datadir` or `--config` "
|
"You need to specify either `--datadir` or `--config` "
|
||||||
"for plot-profit and plot-dataframe.")
|
"for plot-profit and plot-dataframe.")
|
||||||
|
|
||||||
|
|
||||||
def start_plot_dataframe(args: Namespace) -> None:
|
def start_plot_dataframe(args: Dict[str, Any]) -> None:
|
||||||
"""
|
"""
|
||||||
Entrypoint for dataframe plotting
|
Entrypoint for dataframe plotting
|
||||||
"""
|
"""
|
||||||
@ -24,7 +24,7 @@ def start_plot_dataframe(args: Namespace) -> None:
|
|||||||
load_and_plot_trades(config)
|
load_and_plot_trades(config)
|
||||||
|
|
||||||
|
|
||||||
def start_plot_profit(args: Namespace) -> None:
|
def start_plot_profit(args: Dict[str, Any]) -> None:
|
||||||
"""
|
"""
|
||||||
Entrypoint for plot_profit
|
Entrypoint for plot_profit
|
||||||
"""
|
"""
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from argparse import Namespace
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
@ -16,7 +15,7 @@ from freqtrade.state import RunMode
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def setup_utils_configuration(args: Namespace, method: RunMode) -> Dict[str, Any]:
|
def setup_utils_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Prepare the configuration for utils subcommands
|
Prepare the configuration for utils subcommands
|
||||||
:param args: Cli args from Arguments()
|
:param args: Cli args from Arguments()
|
||||||
@ -33,34 +32,34 @@ def setup_utils_configuration(args: Namespace, method: RunMode) -> Dict[str, Any
|
|||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def start_list_exchanges(args: Namespace) -> None:
|
def start_list_exchanges(args: Dict[str, Any]) -> None:
|
||||||
"""
|
"""
|
||||||
Print available exchanges
|
Print available exchanges
|
||||||
:param args: Cli args from Arguments()
|
:param args: Cli args from Arguments()
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if args.print_one_column:
|
if args['print_one_column']:
|
||||||
print('\n'.join(available_exchanges()))
|
print('\n'.join(available_exchanges()))
|
||||||
else:
|
else:
|
||||||
print(f"Exchanges supported by ccxt and available for Freqtrade: "
|
print(f"Exchanges supported by ccxt and available for Freqtrade: "
|
||||||
f"{', '.join(available_exchanges())}")
|
f"{', '.join(available_exchanges())}")
|
||||||
|
|
||||||
|
|
||||||
def start_create_userdir(args: Namespace) -> None:
|
def start_create_userdir(args: Dict[str, Any]) -> None:
|
||||||
"""
|
"""
|
||||||
Create "user_data" directory to contain user data strategies, hyperopts, ...)
|
Create "user_data" directory to contain user data strategies, hyperopts, ...)
|
||||||
:param args: Cli args from Arguments()
|
:param args: Cli args from Arguments()
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
if "user_data_dir" in args and args.user_data_dir:
|
if "user_data_dir" in args and args["user_data_dir"]:
|
||||||
create_userdata_dir(args.user_data_dir, create_dir=True)
|
create_userdata_dir(args["user_data_dir"], create_dir=True)
|
||||||
else:
|
else:
|
||||||
logger.warning("`create-userdir` requires --userdir to be set.")
|
logger.warning("`create-userdir` requires --userdir to be set.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def start_download_data(args: Namespace) -> None:
|
def start_download_data(args: Dict[str, Any]) -> None:
|
||||||
"""
|
"""
|
||||||
Download data (former download_backtest_data.py script)
|
Download data (former download_backtest_data.py script)
|
||||||
"""
|
"""
|
||||||
|
@ -4,17 +4,16 @@ Main Freqtrade worker class.
|
|||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from argparse import Namespace
|
from typing import Any, Callable, Dict, Optional
|
||||||
from typing import Any, Callable, Optional
|
|
||||||
import sdnotify
|
import sdnotify
|
||||||
|
|
||||||
from freqtrade import (constants, OperationalException, TemporaryError,
|
from freqtrade import (OperationalException, TemporaryError, __version__,
|
||||||
__version__)
|
constants)
|
||||||
from freqtrade.configuration import Configuration
|
from freqtrade.configuration import Configuration
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
from freqtrade.state import State
|
|
||||||
from freqtrade.rpc import RPCMessageType
|
from freqtrade.rpc import RPCMessageType
|
||||||
|
from freqtrade.state import State
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ class Worker:
|
|||||||
Freqtradebot worker class
|
Freqtradebot worker class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, args: Namespace, config=None) -> None:
|
def __init__(self, args: Dict[str, Any], config=None) -> None:
|
||||||
"""
|
"""
|
||||||
Init all variables and objects the bot needs to work
|
Init all variables and objects the bot needs to work
|
||||||
"""
|
"""
|
||||||
|
@ -12,48 +12,48 @@ def test_parse_args_none() -> None:
|
|||||||
arguments = Arguments([])
|
arguments = Arguments([])
|
||||||
assert isinstance(arguments, Arguments)
|
assert isinstance(arguments, Arguments)
|
||||||
x = arguments.get_parsed_arg()
|
x = arguments.get_parsed_arg()
|
||||||
assert isinstance(x, argparse.Namespace)
|
assert isinstance(x, dict)
|
||||||
assert isinstance(arguments.parser, argparse.ArgumentParser)
|
assert isinstance(arguments.parser, argparse.ArgumentParser)
|
||||||
|
|
||||||
|
|
||||||
def test_parse_args_defaults() -> None:
|
def test_parse_args_defaults() -> None:
|
||||||
args = Arguments([]).get_parsed_arg()
|
args = Arguments([]).get_parsed_arg()
|
||||||
assert args.config == ['config.json']
|
assert args["config"] == ['config.json']
|
||||||
assert args.strategy_path is None
|
assert args["strategy_path"] is None
|
||||||
assert args.datadir is None
|
assert args["datadir"] is None
|
||||||
assert args.verbosity == 0
|
assert args["verbosity"] == 0
|
||||||
|
|
||||||
|
|
||||||
def test_parse_args_config() -> None:
|
def test_parse_args_config() -> None:
|
||||||
args = Arguments(['-c', '/dev/null']).get_parsed_arg()
|
args = Arguments(['-c', '/dev/null']).get_parsed_arg()
|
||||||
assert args.config == ['/dev/null']
|
assert args["config"] == ['/dev/null']
|
||||||
|
|
||||||
args = Arguments(['--config', '/dev/null']).get_parsed_arg()
|
args = Arguments(['--config', '/dev/null']).get_parsed_arg()
|
||||||
assert args.config == ['/dev/null']
|
assert args["config"] == ['/dev/null']
|
||||||
|
|
||||||
args = Arguments(['--config', '/dev/null',
|
args = Arguments(['--config', '/dev/null',
|
||||||
'--config', '/dev/zero'],).get_parsed_arg()
|
'--config', '/dev/zero'],).get_parsed_arg()
|
||||||
assert args.config == ['/dev/null', '/dev/zero']
|
assert args["config"] == ['/dev/null', '/dev/zero']
|
||||||
|
|
||||||
|
|
||||||
def test_parse_args_db_url() -> None:
|
def test_parse_args_db_url() -> None:
|
||||||
args = Arguments(['--db-url', 'sqlite:///test.sqlite']).get_parsed_arg()
|
args = Arguments(['--db-url', 'sqlite:///test.sqlite']).get_parsed_arg()
|
||||||
assert args.db_url == 'sqlite:///test.sqlite'
|
assert args["db_url"] == 'sqlite:///test.sqlite'
|
||||||
|
|
||||||
|
|
||||||
def test_parse_args_verbose() -> None:
|
def test_parse_args_verbose() -> None:
|
||||||
args = Arguments(['-v']).get_parsed_arg()
|
args = Arguments(['-v']).get_parsed_arg()
|
||||||
assert args.verbosity == 1
|
assert args["verbosity"] == 1
|
||||||
|
|
||||||
args = Arguments(['--verbose']).get_parsed_arg()
|
args = Arguments(['--verbose']).get_parsed_arg()
|
||||||
assert args.verbosity == 1
|
assert args["verbosity"] == 1
|
||||||
|
|
||||||
|
|
||||||
def test_common_scripts_options() -> None:
|
def test_common_scripts_options() -> None:
|
||||||
args = Arguments(['download-data', '-p', 'ETH/BTC', 'XRP/BTC']).get_parsed_arg()
|
args = Arguments(['download-data', '-p', 'ETH/BTC', 'XRP/BTC']).get_parsed_arg()
|
||||||
|
|
||||||
assert args.pairs == ['ETH/BTC', 'XRP/BTC']
|
assert args["pairs"] == ['ETH/BTC', 'XRP/BTC']
|
||||||
assert hasattr(args, "func")
|
assert "func" in args
|
||||||
|
|
||||||
|
|
||||||
def test_parse_args_version() -> None:
|
def test_parse_args_version() -> None:
|
||||||
@ -68,7 +68,7 @@ def test_parse_args_invalid() -> None:
|
|||||||
|
|
||||||
def test_parse_args_strategy() -> None:
|
def test_parse_args_strategy() -> None:
|
||||||
args = Arguments(['--strategy', 'SomeStrategy']).get_parsed_arg()
|
args = Arguments(['--strategy', 'SomeStrategy']).get_parsed_arg()
|
||||||
assert args.strategy == 'SomeStrategy'
|
assert args["strategy"] == 'SomeStrategy'
|
||||||
|
|
||||||
|
|
||||||
def test_parse_args_strategy_invalid() -> None:
|
def test_parse_args_strategy_invalid() -> None:
|
||||||
@ -78,7 +78,7 @@ def test_parse_args_strategy_invalid() -> None:
|
|||||||
|
|
||||||
def test_parse_args_strategy_path() -> None:
|
def test_parse_args_strategy_path() -> None:
|
||||||
args = Arguments(['--strategy-path', '/some/path']).get_parsed_arg()
|
args = Arguments(['--strategy-path', '/some/path']).get_parsed_arg()
|
||||||
assert args.strategy_path == '/some/path'
|
assert args["strategy_path"] == '/some/path'
|
||||||
|
|
||||||
|
|
||||||
def test_parse_args_strategy_path_invalid() -> None:
|
def test_parse_args_strategy_path_invalid() -> None:
|
||||||
@ -105,14 +105,14 @@ def test_parse_args_backtesting_custom() -> None:
|
|||||||
'SampleStrategy'
|
'SampleStrategy'
|
||||||
]
|
]
|
||||||
call_args = Arguments(args).get_parsed_arg()
|
call_args = Arguments(args).get_parsed_arg()
|
||||||
assert call_args.config == ['test_conf.json']
|
assert call_args["config"] == ['test_conf.json']
|
||||||
assert call_args.verbosity == 0
|
assert call_args["verbosity"] == 0
|
||||||
assert call_args.subparser == 'backtesting'
|
assert call_args["subparser"] == 'backtesting'
|
||||||
assert call_args.func is not None
|
assert call_args["func"] is not None
|
||||||
assert call_args.ticker_interval == '1m'
|
assert call_args["ticker_interval"] == '1m'
|
||||||
assert call_args.refresh_pairs is True
|
assert call_args["refresh_pairs"] is True
|
||||||
assert type(call_args.strategy_list) is list
|
assert type(call_args["strategy_list"]) is list
|
||||||
assert len(call_args.strategy_list) == 2
|
assert len(call_args["strategy_list"]) == 2
|
||||||
|
|
||||||
|
|
||||||
def test_parse_args_hyperopt_custom() -> None:
|
def test_parse_args_hyperopt_custom() -> None:
|
||||||
@ -123,12 +123,13 @@ def test_parse_args_hyperopt_custom() -> None:
|
|||||||
'--spaces', 'buy'
|
'--spaces', 'buy'
|
||||||
]
|
]
|
||||||
call_args = Arguments(args).get_parsed_arg()
|
call_args = Arguments(args).get_parsed_arg()
|
||||||
assert call_args.config == ['test_conf.json']
|
assert call_args["config"] == ['test_conf.json']
|
||||||
assert call_args.epochs == 20
|
assert call_args["epochs"] == 20
|
||||||
assert call_args.verbosity == 0
|
assert call_args["verbosity"] == 0
|
||||||
assert call_args.subparser == 'hyperopt'
|
assert call_args["subparser"] == 'hyperopt'
|
||||||
assert call_args.spaces == ['buy']
|
assert call_args["spaces"] == ['buy']
|
||||||
assert call_args.func is not None
|
assert call_args["func"] is not None
|
||||||
|
assert callable(call_args["func"])
|
||||||
|
|
||||||
|
|
||||||
def test_download_data_options() -> None:
|
def test_download_data_options() -> None:
|
||||||
@ -139,12 +140,12 @@ def test_download_data_options() -> None:
|
|||||||
'--days', '30',
|
'--days', '30',
|
||||||
'--exchange', 'binance'
|
'--exchange', 'binance'
|
||||||
]
|
]
|
||||||
args = Arguments(args).get_parsed_arg()
|
pargs = Arguments(args).get_parsed_arg()
|
||||||
|
|
||||||
assert args.pairs_file == 'file_with_pairs'
|
assert pargs["pairs_file"] == 'file_with_pairs'
|
||||||
assert args.datadir == 'datadir/directory'
|
assert pargs["datadir"] == 'datadir/directory'
|
||||||
assert args.days == 30
|
assert pargs["days"] == 30
|
||||||
assert args.exchange == 'binance'
|
assert pargs["exchange"] == 'binance'
|
||||||
|
|
||||||
|
|
||||||
def test_plot_dataframe_options() -> None:
|
def test_plot_dataframe_options() -> None:
|
||||||
@ -158,10 +159,10 @@ def test_plot_dataframe_options() -> None:
|
|||||||
]
|
]
|
||||||
pargs = Arguments(args).get_parsed_arg()
|
pargs = Arguments(args).get_parsed_arg()
|
||||||
|
|
||||||
assert pargs.indicators1 == ["sma10", "sma100"]
|
assert pargs["indicators1"] == ["sma10", "sma100"]
|
||||||
assert pargs.indicators2 == ["macd", "fastd", "fastk"]
|
assert pargs["indicators2"] == ["macd", "fastd", "fastk"]
|
||||||
assert pargs.plot_limit == 30
|
assert pargs["plot_limit"] == 30
|
||||||
assert pargs.pairs == ["UNITTEST/BTC"]
|
assert pargs["pairs"] == ["UNITTEST/BTC"]
|
||||||
|
|
||||||
|
|
||||||
def test_plot_profit_options() -> None:
|
def test_plot_profit_options() -> None:
|
||||||
@ -173,9 +174,9 @@ def test_plot_profit_options() -> None:
|
|||||||
]
|
]
|
||||||
pargs = Arguments(args).get_parsed_arg()
|
pargs = Arguments(args).get_parsed_arg()
|
||||||
|
|
||||||
assert pargs.trade_source == "DB"
|
assert pargs["trade_source"] == "DB"
|
||||||
assert pargs.pairs == ["UNITTEST/BTC"]
|
assert pargs["pairs"] == ["UNITTEST/BTC"]
|
||||||
assert pargs.db_url == "sqlite:///whatever.sqlite"
|
assert pargs["db_url"] == "sqlite:///whatever.sqlite"
|
||||||
|
|
||||||
|
|
||||||
def test_check_int_positive() -> None:
|
def test_check_int_positive() -> None:
|
||||||
|
@ -871,7 +871,7 @@ def test_pairlist_resolving_fallback(mocker):
|
|||||||
|
|
||||||
args = Arguments(arglist).get_parsed_arg()
|
args = Arguments(arglist).get_parsed_arg()
|
||||||
# Fix flaky tests if config.json exists
|
# Fix flaky tests if config.json exists
|
||||||
args.config = None
|
args["config"] = None
|
||||||
|
|
||||||
configuration = Configuration(args)
|
configuration = Configuration(args)
|
||||||
config = configuration.get_config()
|
config = configuration.get_config()
|
||||||
|
@ -27,11 +27,12 @@ def test_parse_args_backtesting(mocker) -> None:
|
|||||||
main(['backtesting'])
|
main(['backtesting'])
|
||||||
assert backtesting_mock.call_count == 1
|
assert backtesting_mock.call_count == 1
|
||||||
call_args = backtesting_mock.call_args[0][0]
|
call_args = backtesting_mock.call_args[0][0]
|
||||||
assert call_args.config == ['config.json']
|
assert call_args["config"] == ['config.json']
|
||||||
assert call_args.verbosity == 0
|
assert call_args["verbosity"] == 0
|
||||||
assert call_args.subparser == 'backtesting'
|
assert call_args["subparser"] == 'backtesting'
|
||||||
assert call_args.func is not None
|
assert call_args["func"] is not None
|
||||||
assert call_args.ticker_interval is None
|
assert callable(call_args["func"])
|
||||||
|
assert call_args["ticker_interval"] is None
|
||||||
|
|
||||||
|
|
||||||
def test_main_start_hyperopt(mocker) -> None:
|
def test_main_start_hyperopt(mocker) -> None:
|
||||||
@ -42,10 +43,11 @@ def test_main_start_hyperopt(mocker) -> None:
|
|||||||
main(['hyperopt'])
|
main(['hyperopt'])
|
||||||
assert hyperopt_mock.call_count == 1
|
assert hyperopt_mock.call_count == 1
|
||||||
call_args = hyperopt_mock.call_args[0][0]
|
call_args = hyperopt_mock.call_args[0][0]
|
||||||
assert call_args.config == ['config.json']
|
assert call_args["config"] == ['config.json']
|
||||||
assert call_args.verbosity == 0
|
assert call_args["verbosity"] == 0
|
||||||
assert call_args.subparser == 'hyperopt'
|
assert call_args["subparser"] == 'hyperopt'
|
||||||
assert call_args.func is not None
|
assert call_args["func"] is not None
|
||||||
|
assert callable(call_args["func"])
|
||||||
|
|
||||||
|
|
||||||
def test_main_fatal_exception(mocker, default_conf, caplog) -> None:
|
def test_main_fatal_exception(mocker, default_conf, caplog) -> None:
|
||||||
|
@ -344,7 +344,7 @@ def test_start_plot_profit_error(mocker):
|
|||||||
argsp = get_args(args)
|
argsp = get_args(args)
|
||||||
# Make sure we use no config. Details: #2241
|
# Make sure we use no config. Details: #2241
|
||||||
# not resetting config causes random failures if config.json exists
|
# not resetting config causes random failures if config.json exists
|
||||||
argsp.config = []
|
argsp["config"] = []
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
start_plot_profit(argsp)
|
start_plot_profit(argsp)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user