2018-02-04 06:42:03 +00:00
|
|
|
"""
|
|
|
|
This module contains the configuration class
|
|
|
|
"""
|
2018-03-25 19:37:14 +00:00
|
|
|
import logging
|
2019-07-25 18:35:20 +00:00
|
|
|
import warnings
|
2019-08-29 04:54:28 +00:00
|
|
|
from copy import deepcopy
|
2019-07-21 12:13:38 +00:00
|
|
|
from pathlib import Path
|
2019-08-10 17:58:04 +00:00
|
|
|
from typing import Any, Callable, Dict, List, Optional
|
2018-07-04 07:31:35 +00:00
|
|
|
|
2019-12-30 14:02:17 +00:00
|
|
|
from freqtrade import constants
|
2019-07-11 18:23:23 +00:00
|
|
|
from freqtrade.configuration.check_exchange import check_exchange
|
2019-10-08 23:37:29 +00:00
|
|
|
from freqtrade.configuration.deprecated_settings import process_temporary_deprecated_settings
|
2020-09-28 17:39:41 +00:00
|
|
|
from freqtrade.configuration.directory_operations import create_datadir, create_userdata_dir
|
2021-07-31 15:43:10 +00:00
|
|
|
from freqtrade.configuration.environment_vars import enironment_vars_to_dict
|
2021-04-06 09:59:58 +00:00
|
|
|
from freqtrade.configuration.load_config import load_config_file, load_file
|
2021-06-08 19:20:35 +00:00
|
|
|
from freqtrade.enums import NON_UTIL_MODES, TRADING_MODES, RunMode
|
2019-12-30 14:02:17 +00:00
|
|
|
from freqtrade.exceptions import OperationalException
|
2019-07-06 21:31:48 +00:00
|
|
|
from freqtrade.loggers import setup_logging
|
2021-07-12 12:08:01 +00:00
|
|
|
from freqtrade.misc import deep_merge_dicts, parse_db_uri_for_logging
|
2019-02-19 12:14:47 +00:00
|
|
|
|
2020-09-28 17:39:41 +00:00
|
|
|
|
2019-07-06 21:31:48 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
2018-07-19 19:12:27 +00:00
|
|
|
|
|
|
|
|
2019-09-12 01:39:52 +00:00
|
|
|
class Configuration:
|
2018-02-04 06:42:03 +00:00
|
|
|
"""
|
|
|
|
Class to read and init the bot configuration
|
|
|
|
Reuse this class for the bot, backtesting, hyperopt and every script that required configuration
|
|
|
|
"""
|
2018-11-14 10:37:53 +00:00
|
|
|
|
2019-09-12 18:16:39 +00:00
|
|
|
def __init__(self, args: Dict[str, Any], runmode: RunMode = None) -> None:
|
2018-02-04 06:42:03 +00:00
|
|
|
self.args = args
|
2018-05-30 20:38:09 +00:00
|
|
|
self.config: Optional[Dict[str, Any]] = None
|
2018-12-25 13:23:59 +00:00
|
|
|
self.runmode = runmode
|
2018-02-04 06:42:03 +00:00
|
|
|
|
2019-07-11 18:23:23 +00:00
|
|
|
def get_config(self) -> Dict[str, Any]:
|
|
|
|
"""
|
|
|
|
Return the config. Use this method to get the bot config
|
|
|
|
:return: Dict: Bot config
|
|
|
|
"""
|
|
|
|
if self.config is None:
|
|
|
|
self.config = self.load_config()
|
|
|
|
|
|
|
|
return self.config
|
|
|
|
|
2019-08-10 17:57:49 +00:00
|
|
|
@staticmethod
|
|
|
|
def from_files(files: List[str]) -> Dict[str, Any]:
|
2018-02-04 06:42:03 +00:00
|
|
|
"""
|
2019-08-10 17:57:49 +00:00
|
|
|
Iterate through the config files passed in, loading all of them
|
|
|
|
and merging their contents.
|
|
|
|
Files are loaded in sequence, parameters in later configuration files
|
|
|
|
override the same parameter from an earlier file (last definition wins).
|
2019-09-13 05:08:22 +00:00
|
|
|
Runs through the whole Configuration initialization, so all expected config entries
|
|
|
|
are available to interactive environments.
|
2019-08-10 17:57:49 +00:00
|
|
|
:param files: List of file paths
|
|
|
|
:return: configuration dictionary
|
2018-02-04 06:42:03 +00:00
|
|
|
"""
|
2020-08-26 18:52:09 +00:00
|
|
|
c = Configuration({'config': files}, RunMode.OTHER)
|
2019-09-13 05:08:22 +00:00
|
|
|
return c.get_config()
|
|
|
|
|
|
|
|
def load_from_files(self, files: List[str]) -> Dict[str, Any]:
|
|
|
|
|
2019-08-10 17:57:49 +00:00
|
|
|
# Keep this method as staticmethod, so it can be used from interactive environments
|
2019-02-19 12:14:47 +00:00
|
|
|
config: Dict[str, Any] = {}
|
2019-07-15 19:17:57 +00:00
|
|
|
|
2019-08-16 12:56:38 +00:00
|
|
|
if not files:
|
2019-08-29 04:54:28 +00:00
|
|
|
return deepcopy(constants.MINIMAL_CONFIG)
|
2019-08-16 12:56:38 +00:00
|
|
|
|
2019-07-15 19:17:57 +00:00
|
|
|
# We expect here a list of config filenames
|
2019-08-10 17:57:49 +00:00
|
|
|
for path in files:
|
|
|
|
logger.info(f'Using config: {path} ...')
|
2019-06-19 21:04:11 +00:00
|
|
|
|
2019-02-19 12:14:47 +00:00
|
|
|
# Merge config options, overwriting old values
|
2019-08-10 11:24:14 +00:00
|
|
|
config = deep_merge_dicts(load_config_file(path), config)
|
2021-07-31 15:43:10 +00:00
|
|
|
|
|
|
|
# Load environment variables
|
|
|
|
env_data = enironment_vars_to_dict()
|
|
|
|
config = deep_merge_dicts(env_data, config)
|
|
|
|
|
2020-05-23 08:44:43 +00:00
|
|
|
config['config_files'] = files
|
2019-08-10 17:58:04 +00:00
|
|
|
# Normalize config
|
2019-07-15 19:17:57 +00:00
|
|
|
if 'internals' not in config:
|
|
|
|
config['internals'] = {}
|
2019-10-08 23:37:29 +00:00
|
|
|
if 'ask_strategy' not in config:
|
|
|
|
config['ask_strategy'] = {}
|
2019-07-15 19:17:57 +00:00
|
|
|
|
2019-11-09 13:15:47 +00:00
|
|
|
if 'pairlists' not in config:
|
|
|
|
config['pairlists'] = []
|
|
|
|
|
2019-08-10 17:58:04 +00:00
|
|
|
return config
|
|
|
|
|
2019-07-15 19:17:57 +00:00
|
|
|
def load_config(self) -> Dict[str, Any]:
|
|
|
|
"""
|
|
|
|
Extract information for sys.argv and load the bot configuration
|
|
|
|
:return: Configuration dictionary
|
|
|
|
"""
|
|
|
|
# Load all configs
|
2019-10-24 04:30:07 +00:00
|
|
|
config: Dict[str, Any] = self.load_from_files(self.args.get("config", []))
|
2019-07-16 20:00:19 +00:00
|
|
|
|
2019-09-20 05:02:54 +00:00
|
|
|
# Keep a copy of the original configuration file
|
|
|
|
config['original_config'] = deepcopy(config)
|
|
|
|
|
2020-02-27 06:01:00 +00:00
|
|
|
self._process_logging_options(config)
|
|
|
|
|
2019-11-01 15:42:57 +00:00
|
|
|
self._process_runmode(config)
|
|
|
|
|
2019-07-16 20:00:19 +00:00
|
|
|
self._process_common_options(config)
|
2019-07-15 19:17:57 +00:00
|
|
|
|
2019-11-01 15:42:57 +00:00
|
|
|
self._process_trading_options(config)
|
|
|
|
|
2019-07-16 20:00:19 +00:00
|
|
|
self._process_optimize_options(config)
|
2019-07-15 19:17:57 +00:00
|
|
|
|
2019-07-16 20:00:19 +00:00
|
|
|
self._process_plot_options(config)
|
2019-07-15 19:17:57 +00:00
|
|
|
|
2021-04-22 07:07:13 +00:00
|
|
|
self._process_data_options(config)
|
|
|
|
|
2019-08-16 12:56:57 +00:00
|
|
|
# Check if the exchange set by the user is supported
|
|
|
|
check_exchange(config, config.get('experimental', {}).get('block_bad_exchanges', True))
|
|
|
|
|
|
|
|
self._resolve_pairs_list(config)
|
|
|
|
|
2019-10-08 23:37:29 +00:00
|
|
|
process_temporary_deprecated_settings(config)
|
|
|
|
|
2019-07-15 19:17:57 +00:00
|
|
|
return config
|
2018-02-04 06:42:03 +00:00
|
|
|
|
2019-07-16 20:00:19 +00:00
|
|
|
def _process_logging_options(self, config: Dict[str, Any]) -> None:
|
2018-03-03 21:39:39 +00:00
|
|
|
"""
|
2019-05-29 18:57:14 +00:00
|
|
|
Extract information for sys.argv and load logging configuration:
|
2019-07-06 23:53:13 +00:00
|
|
|
the -v/--verbose, --logfile options
|
2018-03-03 21:39:39 +00:00
|
|
|
"""
|
|
|
|
# Log level
|
2020-08-26 18:52:09 +00:00
|
|
|
config.update({'verbosity': self.args.get('verbosity', 0)})
|
2019-03-29 19:12:44 +00:00
|
|
|
|
2020-08-26 18:52:09 +00:00
|
|
|
if 'logfile' in self.args and self.args['logfile']:
|
|
|
|
config.update({'logfile': self.args['logfile']})
|
2019-03-29 19:12:44 +00:00
|
|
|
|
2019-07-06 21:31:48 +00:00
|
|
|
setup_logging(config)
|
2018-03-03 21:39:39 +00:00
|
|
|
|
2019-11-01 15:42:57 +00:00
|
|
|
def _process_trading_options(self, config: Dict[str, Any]) -> None:
|
|
|
|
if config['runmode'] not in TRADING_MODES:
|
|
|
|
return
|
|
|
|
|
|
|
|
if config.get('dry_run', False):
|
|
|
|
logger.info('Dry run is enabled')
|
|
|
|
if config.get('db_url') in [None, constants.DEFAULT_DB_PROD_URL]:
|
|
|
|
# Default to in-memory db for dry_run if not specified
|
|
|
|
config['db_url'] = constants.DEFAULT_DB_DRYRUN_URL
|
|
|
|
else:
|
|
|
|
if not config.get('db_url', None):
|
|
|
|
config['db_url'] = constants.DEFAULT_DB_PROD_URL
|
|
|
|
logger.info('Dry run is disabled')
|
|
|
|
|
2021-07-12 12:08:01 +00:00
|
|
|
logger.info(f'Using DB: "{parse_db_uri_for_logging(config["db_url"])}"')
|
2019-11-01 15:42:57 +00:00
|
|
|
|
2019-07-21 14:19:31 +00:00
|
|
|
def _process_common_options(self, config: Dict[str, Any]) -> None:
|
|
|
|
|
2019-07-15 19:17:57 +00:00
|
|
|
# Set strategy if not specified in config and or if it's non default
|
2020-08-26 18:52:09 +00:00
|
|
|
if self.args.get('strategy') or not config.get('strategy'):
|
|
|
|
config.update({'strategy': self.args.get('strategy')})
|
2019-07-15 19:17:57 +00:00
|
|
|
|
2019-07-22 16:37:34 +00:00
|
|
|
self._args_to_config(config, argname='strategy_path',
|
|
|
|
logstring='Using additional Strategy lookup path: {}')
|
2019-07-15 19:17:57 +00:00
|
|
|
|
2020-08-26 18:52:09 +00:00
|
|
|
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']})
|
2018-06-07 03:26:39 +00:00
|
|
|
logger.info('Parameter --db-url detected ...')
|
|
|
|
|
2018-10-10 18:13:56 +00:00
|
|
|
if config.get('forcebuy_enable', False):
|
|
|
|
logger.warning('`forcebuy` RPC message enabled.')
|
|
|
|
|
2019-07-15 19:17:57 +00:00
|
|
|
# Support for sd_notify
|
2020-08-26 18:52:09 +00:00
|
|
|
if 'sd_notify' in self.args and self.args['sd_notify']:
|
2019-07-15 19:17:57 +00:00
|
|
|
config['internals'].update({'sd_notify': True})
|
2018-03-03 21:39:39 +00:00
|
|
|
|
2019-07-16 20:00:19 +00:00
|
|
|
def _process_datadir_options(self, config: Dict[str, Any]) -> None:
|
2019-05-29 18:57:14 +00:00
|
|
|
"""
|
2019-07-21 14:19:31 +00:00
|
|
|
Extract information for sys.argv and load directory configurations
|
|
|
|
--user-data, --datadir
|
2019-05-29 18:57:14 +00:00
|
|
|
"""
|
2019-08-29 04:42:56 +00:00
|
|
|
# Check exchange parameter here - otherwise `datadir` might be wrong.
|
2020-08-26 18:52:09 +00:00
|
|
|
if 'exchange' in self.args and self.args['exchange']:
|
|
|
|
config['exchange']['name'] = self.args['exchange']
|
2019-08-29 04:42:56 +00:00
|
|
|
logger.info(f"Using exchange {config['exchange']['name']}")
|
|
|
|
|
2019-10-25 05:07:01 +00:00
|
|
|
if 'pair_whitelist' not in config['exchange']:
|
|
|
|
config['exchange']['pair_whitelist'] = []
|
|
|
|
|
2020-08-26 18:52:09 +00:00
|
|
|
if 'user_data_dir' in self.args and self.args['user_data_dir']:
|
|
|
|
config.update({'user_data_dir': self.args['user_data_dir']})
|
2019-07-21 12:13:38 +00:00
|
|
|
elif 'user_data_dir' not in config:
|
2019-07-21 12:32:29 +00:00
|
|
|
# Default to cwd/user_data (legacy option ...)
|
2020-08-26 18:52:09 +00:00
|
|
|
config.update({'user_data_dir': str(Path.cwd() / 'user_data')})
|
2019-07-28 13:11:41 +00:00
|
|
|
|
2019-07-21 12:32:29 +00:00
|
|
|
# reset to user_data_dir so this contains the absolute path.
|
2019-07-31 17:39:54 +00:00
|
|
|
config['user_data_dir'] = create_userdata_dir(config['user_data_dir'], create_dir=False)
|
2019-07-21 12:32:29 +00:00
|
|
|
logger.info('Using user-data directory: %s ...', config['user_data_dir'])
|
2019-07-21 12:13:38 +00:00
|
|
|
|
2020-08-26 18:52:09 +00:00
|
|
|
config.update({'datadir': create_datadir(config, self.args.get('datadir', None))})
|
2019-07-04 17:53:50 +00:00
|
|
|
logger.info('Using data directory: %s ...', config.get('datadir'))
|
2019-05-29 18:57:14 +00:00
|
|
|
|
2019-10-19 09:13:10 +00:00
|
|
|
if self.args.get('exportfilename'):
|
|
|
|
self._args_to_config(config, argname='exportfilename',
|
|
|
|
logstring='Storing backtest results to {} ...')
|
2020-03-15 08:39:45 +00:00
|
|
|
config['exportfilename'] = Path(config['exportfilename'])
|
2019-10-19 09:13:10 +00:00
|
|
|
else:
|
|
|
|
config['exportfilename'] = (config['user_data_dir']
|
2020-06-28 07:45:23 +00:00
|
|
|
/ 'backtest_results')
|
2019-10-19 09:13:10 +00:00
|
|
|
|
2019-07-16 20:00:19 +00:00
|
|
|
def _process_optimize_options(self, config: Dict[str, Any]) -> None:
|
|
|
|
|
2019-04-23 18:58:27 +00:00
|
|
|
# This will override the strategy configuration
|
2020-06-01 18:52:33 +00:00
|
|
|
self._args_to_config(config, argname='timeframe',
|
|
|
|
logstring='Parameter -i/--timeframe detected ... '
|
|
|
|
'Using timeframe: {} ...')
|
2018-02-09 07:35:38 +00:00
|
|
|
|
2019-04-24 19:13:57 +00:00
|
|
|
self._args_to_config(config, argname='position_stacking',
|
2019-04-24 19:02:05 +00:00
|
|
|
logstring='Parameter --enable-position-stacking detected ...')
|
2018-07-17 18:26:59 +00:00
|
|
|
|
2020-11-23 19:29:29 +00:00
|
|
|
self._args_to_config(
|
|
|
|
config, argname='enable_protections',
|
|
|
|
logstring='Parameter --enable-protections detected, enabling Protections. ...')
|
2019-11-01 15:42:57 +00:00
|
|
|
|
2019-09-12 18:16:39 +00:00
|
|
|
if 'use_max_market_positions' in self.args and not self.args["use_max_market_positions"]:
|
2018-07-17 19:05:03 +00:00
|
|
|
config.update({'use_max_market_positions': False})
|
|
|
|
logger.info('Parameter --disable-max-market-positions detected ...')
|
|
|
|
logger.info('max_open_trades set to unlimited ...')
|
2020-08-26 18:52:09 +00:00
|
|
|
elif 'max_open_trades' in self.args and self.args['max_open_trades']:
|
|
|
|
config.update({'max_open_trades': self.args['max_open_trades']})
|
2019-12-21 21:17:51 +00:00
|
|
|
logger.info('Parameter --max-open-trades detected, '
|
2019-04-14 08:17:06 +00:00
|
|
|
'overriding max_open_trades to: %s ...', config.get('max_open_trades'))
|
2019-11-01 15:42:57 +00:00
|
|
|
elif config['runmode'] in NON_UTIL_MODES:
|
2018-07-17 19:05:03 +00:00
|
|
|
logger.info('Using max_open_trades: %s ...', config.get('max_open_trades'))
|
2021-02-26 18:48:06 +00:00
|
|
|
# Setting max_open_trades to infinite if -1
|
|
|
|
if config.get('max_open_trades') == -1:
|
|
|
|
config['max_open_trades'] = float('inf')
|
2018-02-09 07:35:38 +00:00
|
|
|
|
2021-02-25 19:14:33 +00:00
|
|
|
if self.args.get('stake_amount', None):
|
|
|
|
# Convert explicitly to float to support CLI argument for both unlimited and value
|
|
|
|
try:
|
|
|
|
self.args['stake_amount'] = float(self.args['stake_amount'])
|
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
|
2021-08-14 13:34:43 +00:00
|
|
|
self._args_to_config(config, argname='timeframe_detail',
|
2021-08-14 14:56:49 +00:00
|
|
|
logstring='Parameter --timeframe-detail detected, '
|
2021-08-14 13:34:43 +00:00
|
|
|
'using {} for intra-candle backtesting ...')
|
2021-10-30 08:50:40 +00:00
|
|
|
|
|
|
|
self._args_to_config(config, argname='backtest_show_pair_list',
|
|
|
|
logstring='Parameter --show-pair-list detected.')
|
|
|
|
|
2019-04-24 19:13:57 +00:00
|
|
|
self._args_to_config(config, argname='stake_amount',
|
2019-12-21 21:17:51 +00:00
|
|
|
logstring='Parameter --stake-amount detected, '
|
2019-04-24 19:02:05 +00:00
|
|
|
'overriding stake_amount to: {} ...')
|
2021-02-10 18:45:59 +00:00
|
|
|
self._args_to_config(config, argname='dry_run_wallet',
|
|
|
|
logstring='Parameter --dry-run-wallet detected, '
|
|
|
|
'overriding dry_run_wallet to: {} ...')
|
2019-10-05 13:29:00 +00:00
|
|
|
self._args_to_config(config, argname='fee',
|
|
|
|
logstring='Parameter --fee detected, '
|
|
|
|
'setting fee to: {} ...')
|
|
|
|
|
2019-04-24 19:13:57 +00:00
|
|
|
self._args_to_config(config, argname='timerange',
|
2019-04-24 19:02:05 +00:00
|
|
|
logstring='Parameter --timerange detected: {} ...')
|
2018-02-09 07:35:38 +00:00
|
|
|
|
2019-07-16 20:00:19 +00:00
|
|
|
self._process_datadir_options(config)
|
2018-02-09 07:35:38 +00:00
|
|
|
|
2019-04-24 19:13:57 +00:00
|
|
|
self._args_to_config(config, argname='strategy_list',
|
2019-09-18 19:57:17 +00:00
|
|
|
logstring='Using strategy list of {} strategies', logfun=len)
|
2018-07-27 21:00:50 +00:00
|
|
|
|
2020-06-01 18:52:33 +00:00
|
|
|
self._args_to_config(config, argname='timeframe',
|
|
|
|
logstring='Overriding timeframe with Command line argument')
|
2018-07-27 21:00:50 +00:00
|
|
|
|
2019-04-24 19:13:57 +00:00
|
|
|
self._args_to_config(config, argname='export',
|
2019-04-24 19:02:05 +00:00
|
|
|
logstring='Parameter --export detected: {} ...')
|
2018-02-09 07:35:38 +00:00
|
|
|
|
2021-10-21 04:58:40 +00:00
|
|
|
self._args_to_config(config, argname='backtest_breakdown',
|
|
|
|
logstring='Parameter --breakdown detected ...')
|
2021-03-26 15:40:50 +00:00
|
|
|
|
2021-06-29 18:22:30 +00:00
|
|
|
self._args_to_config(config, argname='disableparamexport',
|
|
|
|
logstring='Parameter --disableparamexport detected: {} ...')
|
2021-10-17 14:29:19 +00:00
|
|
|
|
2019-04-24 19:27:32 +00:00
|
|
|
# Edge section:
|
2019-09-12 18:16:39 +00:00
|
|
|
if 'stoploss_range' in self.args and self.args["stoploss_range"]:
|
|
|
|
txt_range = eval(self.args["stoploss_range"])
|
2018-11-14 15:31:23 +00:00
|
|
|
config['edge'].update({'stoploss_range_min': txt_range[0]})
|
|
|
|
config['edge'].update({'stoploss_range_max': txt_range[1]})
|
|
|
|
config['edge'].update({'stoploss_range_step': txt_range[2]})
|
2019-09-12 18:16:39 +00:00
|
|
|
logger.info('Parameter --stoplosses detected: %s ...', self.args["stoploss_range"])
|
2018-11-14 15:31:23 +00:00
|
|
|
|
2019-04-24 19:27:32 +00:00
|
|
|
# Hyperopt section
|
2019-04-24 19:13:57 +00:00
|
|
|
self._args_to_config(config, argname='hyperopt',
|
2019-09-18 19:57:17 +00:00
|
|
|
logstring='Using Hyperopt class name: {}')
|
2019-03-04 06:24:05 +00:00
|
|
|
|
2019-07-22 16:37:34 +00:00
|
|
|
self._args_to_config(config, argname='hyperopt_path',
|
|
|
|
logstring='Using additional Hyperopt lookup path: {}')
|
|
|
|
|
2020-09-27 15:00:23 +00:00
|
|
|
self._args_to_config(config, argname='hyperoptexportfilename',
|
|
|
|
logstring='Using hyperopt file: {}')
|
|
|
|
|
2019-04-24 19:13:57 +00:00
|
|
|
self._args_to_config(config, argname='epochs',
|
2019-04-24 19:12:08 +00:00
|
|
|
logstring='Parameter --epochs detected ... '
|
|
|
|
'Will run Hyperopt with for {} epochs ...'
|
|
|
|
)
|
2018-03-02 13:46:32 +00:00
|
|
|
|
2019-04-24 19:13:57 +00:00
|
|
|
self._args_to_config(config, argname='spaces',
|
2019-04-24 19:12:08 +00:00
|
|
|
logstring='Parameter -s/--spaces detected: {}')
|
2018-03-04 08:51:22 +00:00
|
|
|
|
2019-04-24 19:13:57 +00:00
|
|
|
self._args_to_config(config, argname='print_all',
|
2019-04-24 19:12:08 +00:00
|
|
|
logstring='Parameter --print-all detected ...')
|
2019-04-21 22:10:01 +00:00
|
|
|
|
2019-09-12 18:16:39 +00:00
|
|
|
if 'print_colorized' in self.args and not self.args["print_colorized"]:
|
2019-08-12 18:07:29 +00:00
|
|
|
logger.info('Parameter --no-color detected ...')
|
2019-08-12 20:13:04 +00:00
|
|
|
config.update({'print_colorized': False})
|
|
|
|
else:
|
|
|
|
config.update({'print_colorized': True})
|
2019-08-03 16:09:42 +00:00
|
|
|
|
2019-08-15 18:39:04 +00:00
|
|
|
self._args_to_config(config, argname='print_json',
|
|
|
|
logstring='Parameter --print-json detected ...')
|
|
|
|
|
2020-03-05 00:58:33 +00:00
|
|
|
self._args_to_config(config, argname='export_csv',
|
|
|
|
logstring='Parameter --export-csv detected: {}')
|
|
|
|
|
2019-04-24 19:13:57 +00:00
|
|
|
self._args_to_config(config, argname='hyperopt_jobs',
|
2019-04-24 19:12:08 +00:00
|
|
|
logstring='Parameter -j/--job-workers detected: {}')
|
2019-04-22 21:30:09 +00:00
|
|
|
|
2019-04-24 19:12:08 +00:00
|
|
|
self._args_to_config(config, argname='hyperopt_random_state',
|
|
|
|
logstring='Parameter --random-state detected: {}')
|
2019-05-01 12:27:58 +00:00
|
|
|
|
|
|
|
self._args_to_config(config, argname='hyperopt_min_trades',
|
|
|
|
logstring='Parameter --min-trades detected: {}')
|
|
|
|
|
2019-07-16 04:45:13 +00:00
|
|
|
self._args_to_config(config, argname='hyperopt_loss',
|
2019-09-18 19:57:17 +00:00
|
|
|
logstring='Using Hyperopt loss class name: {}')
|
2019-07-15 19:35:42 +00:00
|
|
|
|
2019-11-26 12:01:42 +00:00
|
|
|
self._args_to_config(config, argname='hyperopt_show_index',
|
|
|
|
logstring='Parameter -n/--index detected: {}')
|
|
|
|
|
|
|
|
self._args_to_config(config, argname='hyperopt_list_best',
|
|
|
|
logstring='Parameter --best detected: {}')
|
|
|
|
|
|
|
|
self._args_to_config(config, argname='hyperopt_list_profitable',
|
|
|
|
logstring='Parameter --profitable detected: {}')
|
|
|
|
|
2020-02-11 15:02:08 +00:00
|
|
|
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',
|
|
|
|
logstring='Parameter --max-trades detected: {}')
|
|
|
|
|
2020-02-08 22:21:42 +00:00
|
|
|
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',
|
|
|
|
logstring='Parameter --max-avg-time detected: {}')
|
|
|
|
|
|
|
|
self._args_to_config(config, argname='hyperopt_list_min_avg_profit',
|
|
|
|
logstring='Parameter --min-avg-profit detected: {}')
|
|
|
|
|
2020-02-11 20:29:55 +00:00
|
|
|
self._args_to_config(config, argname='hyperopt_list_max_avg_profit',
|
|
|
|
logstring='Parameter --max-avg-profit detected: {}')
|
|
|
|
|
2020-02-08 22:21:42 +00:00
|
|
|
self._args_to_config(config, argname='hyperopt_list_min_total_profit',
|
|
|
|
logstring='Parameter --min-total-profit detected: {}')
|
|
|
|
|
2020-02-11 20:29:55 +00:00
|
|
|
self._args_to_config(config, argname='hyperopt_list_max_total_profit',
|
|
|
|
logstring='Parameter --max-total-profit detected: {}')
|
|
|
|
|
2020-03-22 01:22:06 +00:00
|
|
|
self._args_to_config(config, argname='hyperopt_list_min_objective',
|
|
|
|
logstring='Parameter --min-objective detected: {}')
|
|
|
|
|
|
|
|
self._args_to_config(config, argname='hyperopt_list_max_objective',
|
|
|
|
logstring='Parameter --max-objective detected: {}')
|
|
|
|
|
2019-11-26 12:01:42 +00:00
|
|
|
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',
|
|
|
|
logstring='Parameter --no-header detected: {}')
|
2021-10-13 17:54:35 +00:00
|
|
|
|
|
|
|
self._args_to_config(config, argname="hyperopt_ignore_missing_space",
|
|
|
|
logstring="Paramter --ignore-missing-space detected: {}")
|
2019-11-26 12:01:42 +00:00
|
|
|
|
2019-07-16 20:00:19 +00:00
|
|
|
def _process_plot_options(self, config: Dict[str, Any]) -> None:
|
|
|
|
|
2019-06-16 11:31:24 +00:00
|
|
|
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='indicators2',
|
|
|
|
logstring='Using indicators2: {}')
|
|
|
|
|
2020-05-02 09:26:12 +00:00
|
|
|
self._args_to_config(config, argname='trade_ids',
|
|
|
|
logstring='Filtering on trade_ids: {}')
|
|
|
|
|
2019-06-16 11:31:24 +00:00
|
|
|
self._args_to_config(config, argname='plot_limit',
|
|
|
|
logstring='Limiting plot to: {}')
|
2020-05-02 09:26:12 +00:00
|
|
|
|
2021-05-30 14:11:24 +00:00
|
|
|
self._args_to_config(config, argname='plot_auto_open',
|
|
|
|
logstring='Parameter --auto-open detected.')
|
|
|
|
|
2019-06-22 14:18:49 +00:00
|
|
|
self._args_to_config(config, argname='trade_source',
|
|
|
|
logstring='Using trades from: {}')
|
2019-07-15 19:17:57 +00:00
|
|
|
|
2019-08-16 13:27:33 +00:00
|
|
|
self._args_to_config(config, argname='erase',
|
|
|
|
logstring='Erase detected. Deleting existing data.')
|
|
|
|
|
2020-03-15 20:27:45 +00:00
|
|
|
self._args_to_config(config, argname='no_trades',
|
|
|
|
logstring='Parameter --no-trades detected.')
|
|
|
|
|
2019-08-16 12:56:38 +00:00
|
|
|
self._args_to_config(config, argname='timeframes',
|
|
|
|
logstring='timeframes --timeframes: {}')
|
|
|
|
|
|
|
|
self._args_to_config(config, argname='days',
|
|
|
|
logstring='Detected --days: {}')
|
2019-12-27 12:46:25 +00:00
|
|
|
|
2021-10-17 14:09:56 +00:00
|
|
|
self._args_to_config(config, argname='include_inactive',
|
|
|
|
logstring='Detected --include-inactive-pairs: {}')
|
|
|
|
|
2019-10-08 18:31:01 +00:00
|
|
|
self._args_to_config(config, argname='download_trades',
|
|
|
|
logstring='Detected --dl-trades: {}')
|
2019-08-16 12:56:38 +00:00
|
|
|
|
2019-12-27 12:46:25 +00:00
|
|
|
self._args_to_config(config, argname='dataformat_ohlcv',
|
|
|
|
logstring='Using "{}" to store OHLCV data.')
|
|
|
|
|
2019-12-28 09:01:54 +00:00
|
|
|
self._args_to_config(config, argname='dataformat_trades',
|
2019-12-27 12:46:25 +00:00
|
|
|
logstring='Using "{}" to store trades data.')
|
|
|
|
|
2021-04-22 07:07:13 +00:00
|
|
|
def _process_data_options(self, config: Dict[str, Any]) -> None:
|
|
|
|
|
|
|
|
self._args_to_config(config, argname='new_pairs_days',
|
|
|
|
logstring='Detected --new-pairs-days: {}')
|
|
|
|
|
2019-07-16 20:00:19 +00:00
|
|
|
def _process_runmode(self, config: Dict[str, Any]) -> None:
|
|
|
|
|
2020-02-27 18:36:54 +00:00
|
|
|
self._args_to_config(config, argname='dry_run',
|
|
|
|
logstring='Parameter --dry-run detected, '
|
|
|
|
'overriding dry_run to: {} ...')
|
|
|
|
|
2019-07-15 19:17:57 +00:00
|
|
|
if not self.runmode:
|
|
|
|
# Handle real mode, infer dry/live from config
|
|
|
|
self.runmode = RunMode.DRY_RUN if config.get('dry_run', True) else RunMode.LIVE
|
2020-02-27 06:01:00 +00:00
|
|
|
logger.info(f"Runmode set to {self.runmode.value}.")
|
2019-07-15 19:17:57 +00:00
|
|
|
|
|
|
|
config.update({'runmode': self.runmode})
|
2019-06-16 11:31:24 +00:00
|
|
|
|
2019-07-15 19:17:57 +00:00
|
|
|
def _args_to_config(self, config: Dict[str, Any], argname: str,
|
2019-07-25 18:35:20 +00:00
|
|
|
logstring: str, logfun: Optional[Callable] = None,
|
|
|
|
deprecated_msg: Optional[str] = None) -> None:
|
2019-07-15 19:17:57 +00:00
|
|
|
"""
|
|
|
|
:param config: Configuration dictionary
|
|
|
|
:param argname: Argumentname in self.args - will be copied to config dict.
|
|
|
|
:param logstring: Logging String
|
|
|
|
:param logfun: logfun is applied to the configuration entry before passing
|
|
|
|
that entry to the log string using .format().
|
|
|
|
sample: logfun=len (prints the length of the found
|
|
|
|
configuration instead of the content)
|
|
|
|
"""
|
2019-10-05 13:29:00 +00:00
|
|
|
if (argname in self.args and self.args[argname] is not None
|
2019-10-05 13:34:31 +00:00
|
|
|
and self.args[argname] is not False):
|
2019-07-15 19:17:57 +00:00
|
|
|
|
2019-09-12 18:16:39 +00:00
|
|
|
config.update({argname: self.args[argname]})
|
2019-07-15 19:17:57 +00:00
|
|
|
if logfun:
|
|
|
|
logger.info(logstring.format(logfun(config[argname])))
|
|
|
|
else:
|
|
|
|
logger.info(logstring.format(config[argname]))
|
2019-07-25 18:35:20 +00:00
|
|
|
if deprecated_msg:
|
|
|
|
warnings.warn(f"DEPRECATED: {deprecated_msg}", DeprecationWarning)
|
2019-08-16 12:56:57 +00:00
|
|
|
|
|
|
|
def _resolve_pairs_list(self, config: Dict[str, Any]) -> None:
|
|
|
|
"""
|
|
|
|
Helper for download script.
|
|
|
|
Takes first found:
|
|
|
|
* -p (pairs argument)
|
|
|
|
* --pairs-file
|
|
|
|
* whitelist from config
|
|
|
|
"""
|
|
|
|
|
2019-08-16 13:52:59 +00:00
|
|
|
if "pairs" in config:
|
2021-04-17 08:47:32 +00:00
|
|
|
config['exchange']['pair_whitelist'] = config['pairs']
|
2019-08-16 12:56:57 +00:00
|
|
|
return
|
|
|
|
|
2019-09-12 18:16:39 +00:00
|
|
|
if "pairs_file" in self.args and self.args["pairs_file"]:
|
|
|
|
pairs_file = Path(self.args["pairs_file"])
|
2019-08-16 12:56:57 +00:00
|
|
|
logger.info(f'Reading pairs file "{pairs_file}".')
|
|
|
|
# Download pairs from the pairs file if no config is specified
|
2021-06-25 13:45:49 +00:00
|
|
|
# or if pairs file is specified explicitly
|
2019-08-16 12:56:57 +00:00
|
|
|
if not pairs_file.exists():
|
2019-08-16 13:52:59 +00:00
|
|
|
raise OperationalException(f'No pairs file found with path "{pairs_file}".')
|
2021-04-06 09:59:58 +00:00
|
|
|
config['pairs'] = load_file(pairs_file)
|
|
|
|
config['pairs'].sort()
|
2019-08-16 13:52:59 +00:00
|
|
|
return
|
2019-08-16 12:56:57 +00:00
|
|
|
|
2020-08-26 18:52:09 +00:00
|
|
|
if 'config' in self.args and self.args['config']:
|
2019-08-16 12:56:57 +00:00
|
|
|
logger.info("Using pairlist from configuration.")
|
|
|
|
config['pairs'] = config.get('exchange', {}).get('pair_whitelist')
|
2019-08-16 13:52:59 +00:00
|
|
|
else:
|
|
|
|
# Fall back to /dl_path/pairs.json
|
2020-08-26 18:52:09 +00:00
|
|
|
pairs_file = config['datadir'] / 'pairs.json'
|
2019-08-16 13:52:59 +00:00
|
|
|
if pairs_file.exists():
|
2021-04-06 09:59:58 +00:00
|
|
|
config['pairs'] = load_file(pairs_file)
|
2019-08-21 04:58:56 +00:00
|
|
|
if 'pairs' in config:
|
|
|
|
config['pairs'].sort()
|