diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 130c55518..647531d3a 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -104,6 +104,7 @@ user_data/ You can add the entry "user_data_dir" setting to your configuration, to always point your bot to this directory. Alternatively, pass in `--userdir` to every command. +The bot will fail to start if the directory does not exist, but will create necessary subdirectories. This directory should contain your custom strategies, custom hyperopts and hyperopt loss functions, backtesting historical data (downloaded using either backtesting command or the download script) and plot outputs. diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index e56c2c06a..a2ce54e8b 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -178,7 +178,7 @@ class Configuration(object): config.update({'user_data_dir': str(Path.cwd() / "user_data")}) # reset to user_data_dir so this contains the absolute path. - config['user_data_dir'] = create_userdata_dir(config['user_data_dir']) + 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']) if 'datadir' in self.args and self.args.datadir: diff --git a/freqtrade/configuration/directory_operations.py b/freqtrade/configuration/directory_operations.py index 7542c2b80..395accd90 100644 --- a/freqtrade/configuration/directory_operations.py +++ b/freqtrade/configuration/directory_operations.py @@ -2,6 +2,7 @@ import logging from typing import Any, Dict, Optional from pathlib import Path +from freqtrade import OperationalException logger = logging.getLogger(__name__) @@ -20,12 +21,26 @@ def create_datadir(config: Dict[str, Any], datadir: Optional[str] = None) -> str return str(folder) -def create_userdata_dir(directory: str) -> Path: +def create_userdata_dir(directory: str, create_dir=False) -> Path: + """ + Create userdata directory structure. + if create_dir is True, then the parent-directory will be created if it does not exist. + Sub-directories will always be created if the parent directory exists. + Raises OperationalException if given a non-existing directory. + :param directory: Directory to check + :param create_dir: Create directory if it does not exist. + :return: Path object containing the directory + """ sub_dirs = ["backtest_results", "data", "hyperopts", "hyperopt_results", "plot", "strategies", ] folder = Path(directory) if not folder.is_dir(): - folder.mkdir(parents=True) - logger.info(f'Created user-data directory: {folder}') + if create_dir: + folder.mkdir(parents=True) + logger.info(f'Created user-data directory: {folder}') + else: + raise OperationalException( + f"Directory `{folder}` does not exist. " + "Please use `freqtrade create-userdir` to create a user directory") # Create required subdirectories for f in sub_dirs: diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index 4dd3760db..838578e25 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -335,7 +335,7 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non ) mocker.patch( 'freqtrade.configuration.configuration.create_userdata_dir', - lambda x: Path(x) + lambda x, *args, **kwargs: Path(x) ) arglist = [ '--config', 'config.json', @@ -625,7 +625,7 @@ def test_create_userdata_dir(mocker, default_conf, caplog) -> None: mocker.patch.object(Path, "is_dir", MagicMock(return_value=False)) md = mocker.patch.object(Path, 'mkdir', MagicMock()) - x = create_userdata_dir('/tmp/bar') + x = create_userdata_dir('/tmp/bar', create_dir=True) assert md.call_count == 7 assert md.call_args[1]['parents'] is False assert log_has('Created user-data directory: /tmp/bar', caplog.record_tuples) @@ -641,6 +641,15 @@ def test_create_userdata_dir_exists(mocker, default_conf, caplog) -> None: assert md.call_count == 0 +def test_create_userdata_dir_exists_exception(mocker, default_conf, caplog) -> None: + mocker.patch.object(Path, "is_dir", MagicMock(return_value=False)) + md = mocker.patch.object(Path, 'mkdir', MagicMock()) + + with pytest.raises(OperationalException, match=r'Directory `/tmp/bar` does not exist.*'): + create_userdata_dir('/tmp/bar', create_dir=False) + assert md.call_count == 0 + + def test_validate_tsl(default_conf): default_conf['trailing_stop'] = True default_conf['trailing_stop_positive'] = 0 diff --git a/freqtrade/utils.py b/freqtrade/utils.py index fa6bc2d1d..2c7902ebd 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -49,7 +49,7 @@ def start_create_userdir(args: Namespace) -> None: :return: None """ if "user_data_dir" in args and args.user_data_dir: - create_userdata_dir(args.user_data_dir) + create_userdata_dir(args.user_data_dir, create_dir=True) else: logger.warning("`create-userdir` requires --userdir to be set.") sys.exit(1)