From 9dafc2f3c84819cf95bda82ddb0cc8ec380ede3d Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 11 Feb 2020 20:38:29 +0100 Subject: [PATCH 1/5] Load config.json from user_data first --- freqtrade/commands/arguments.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index fe6f49039..d37870ea0 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -6,8 +6,8 @@ from functools import partial from pathlib import Path from typing import Any, Dict, List, Optional -from freqtrade import constants from freqtrade.commands.cli_options import AVAILABLE_CLI_OPTIONS +from freqtrade.constants import DEFAULT_CONFIG ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_data_dir"] @@ -107,10 +107,19 @@ class Arguments: # Workaround issue in argparse with action='append' and default value # (see https://bugs.python.org/issue16399) # Allow no-config for certain commands (like downloading / plotting) - if ('config' in parsed_arg and parsed_arg.config is None and - ((Path.cwd() / constants.DEFAULT_CONFIG).is_file() or - not ('command' in parsed_arg and parsed_arg.command in NO_CONF_REQURIED))): - parsed_arg.config = [constants.DEFAULT_CONFIG] + if ('config' in parsed_arg and parsed_arg.config is None): + conf_required = ('command' in parsed_arg and parsed_arg.command in NO_CONF_REQURIED) + + if 'user_data_dir' in parsed_arg and parsed_arg.user_data_dir is not None: + # Try loading from "user_data/config.json" + cfgfile = Path(parsed_arg.user_data_dir) / DEFAULT_CONFIG + if cfgfile.is_file() or not conf_required: + parsed_arg.config = [str(cfgfile)] + else: + # Else use "config.json". + cfgfile = Path.cwd() / DEFAULT_CONFIG + if cfgfile.is_file() or not conf_required: + parsed_arg.config = [DEFAULT_CONFIG] return parsed_arg From be4a9b5f4b0f9c4026ed043876bbcfb516a83a0d Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 14 Feb 2020 19:37:20 +0100 Subject: [PATCH 2/5] Lowercase freqtrade --- CONTRIBUTING.md | 4 ++-- docs/configuration.md | 2 +- docs/developer.md | 4 ++-- docs/hyperopt.md | 2 +- docs/rest-api.md | 2 +- freqtrade/commands/cli_options.py | 3 ++- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1c83437f6..90594866a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -109,11 +109,11 @@ Exceptions: Contributors may be given commit privileges. Preference will be given to those with: -1. Past contributions to FreqTrade and other related open-source projects. Contributions to FreqTrade include both code (both accepted and pending) and friendly participation in the issue tracker and Pull request reviews. Quantity and quality are considered. +1. Past contributions to Freqtrade and other related open-source projects. Contributions to Freqtrade include both code (both accepted and pending) and friendly participation in the issue tracker and Pull request reviews. Quantity and quality are considered. 1. A coding style that the other core committers find simple, minimal, and clean. 1. Access to resources for cross-platform development and testing. 1. Time to devote to the project regularly. -Being a Committer does not grant write permission on `develop` or `master` for security reasons (Users trust FreqTrade with their Exchange API keys). +Being a Committer does not grant write permission on `develop` or `master` for security reasons (Users trust Freqtrade with their Exchange API keys). After being Committer for some time, a Committer may be named Core Committer and given full repository access. diff --git a/docs/configuration.md b/docs/configuration.md index 211f7a04c..0f0279eb9 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -672,7 +672,7 @@ freqtrade ## Embedding Strategies -FreqTrade provides you with with an easy way to embed the strategy into your configuration file. +Freqtrade provides you with with an easy way to embed the strategy into your configuration file. This is done by utilizing BASE64 encoding and providing this string at the strategy configuration field, in your chosen config file. diff --git a/docs/developer.md b/docs/developer.md index c679b8a49..b128ffd2b 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -1,6 +1,6 @@ # Development Help -This page is intended for developers of FreqTrade, people who want to contribute to the FreqTrade codebase or documentation, or people who want to understand the source code of the application they're running. +This page is intended for developers of Freqtrade, people who want to contribute to the Freqtrade codebase or documentation, or people who want to understand the source code of the application they're running. All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. We [track issues](https://github.com/freqtrade/freqtrade/issues) on [GitHub](https://github.com) and also have a dev channel in [slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtNjU5ODcwNjI1MDU3LTU1MTgxMjkzNmYxNWE1MDEzYzQ3YmU4N2MwZjUyNjJjODRkMDVkNjg4YTAyZGYzYzlhOTZiMTE4ZjQ4YzM0OGE) where you can ask questions. @@ -153,7 +153,7 @@ In VolumePairList, this implements different methods of sorting, does early vali ## Implement a new Exchange (WIP) !!! Note - This section is a Work in Progress and is not a complete guide on how to test a new exchange with FreqTrade. + This section is a Work in Progress and is not a complete guide on how to test a new exchange with Freqtrade. Most exchanges supported by CCXT should work out of the box. diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 3e10f66da..401811a1b 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -182,7 +182,7 @@ add it to the `populate_indicators()` method in your custom hyperopt file. Each hyperparameter tuning requires a target. This is usually defined as a loss function (sometimes also called objective function), which should decrease for more desirable results, and increase for bad results. -By default, FreqTrade uses a loss function, which has been with freqtrade since the beginning and optimizes mostly for short trade duration and avoiding losses. +By default, Freqtrade uses a loss function, which has been with freqtrade since the beginning and optimizes mostly for short trade duration and avoiding losses. A different loss function can be specified by using the `--hyperopt-loss ` argument. This class should be in its own file within the `user_data/hyperopts/` directory. diff --git a/docs/rest-api.md b/docs/rest-api.md index 187a71c97..b68364f39 100644 --- a/docs/rest-api.md +++ b/docs/rest-api.md @@ -74,7 +74,7 @@ docker run -d \ ## Consuming the API You can consume the API by using the script `scripts/rest_client.py`. -The client script only requires the `requests` module, so FreqTrade does not need to be installed on the system. +The client script only requires the `requests` module, so Freqtrade does not need to be installed on the system. ``` bash python3 scripts/rest_client.py [optional parameters] diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 1776955b1..cdc8cb8f1 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -59,7 +59,8 @@ AVAILABLE_CLI_OPTIONS = { ), "config": Arg( '-c', '--config', - help=f'Specify configuration file (default: `{constants.DEFAULT_CONFIG}`). ' + help=f'Specify configuration file (default: `userdir/{constants.DEFAULT_CONFIG}` ' + f'or `config.json` whichever exists). ' f'Multiple --config options may be used. ' f'Can be set to `-` to read config from stdin.', action='append', From 5efbdd25a7776c55eb8fbfb43a0e4b09213b4c41 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 14 Feb 2020 20:04:05 +0100 Subject: [PATCH 3/5] Properly default to user_data/config.json if it exists --- docs/bot-usage.md | 52 +++++++++++++++++++++------------ freqtrade/commands/arguments.py | 10 +++++-- tests/test_arguments.py | 24 +++++++++++++-- tests/test_main.py | 5 +++- 4 files changed, 66 insertions(+), 25 deletions(-) diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 56e6008a1..dbc111d44 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -58,9 +58,10 @@ Common arguments: details. -V, --version show program's version number and exit -c PATH, --config PATH - Specify configuration file (default: `config.json`). - Multiple --config options may be used. Can be set to - `-` to read config from stdin. + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. -d PATH, --datadir PATH Path to directory with historical backtesting data. --userdir PATH, --user-data-dir PATH @@ -71,6 +72,7 @@ Strategy arguments: Specify strategy class name which will be used by the bot. --strategy-path PATH Specify additional strategy lookup path. +. ``` @@ -242,12 +244,15 @@ optional arguments: Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. + --logfile FILE Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. -V, --version show program's version number and exit -c PATH, --config PATH - Specify configuration file (default: `config.json`). - Multiple --config options may be used. Can be set to - `-` to read config from stdin. + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. -d PATH, --datadir PATH Path to directory with historical backtesting data. --userdir PATH, --user-data-dir PATH @@ -280,7 +285,7 @@ usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] [--stake-amount STAKE_AMOUNT] [--fee FLOAT] [--hyperopt NAME] [--hyperopt-path PATH] [--eps] [-e INT] - [--spaces {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...]] + [--spaces {all,buy,sell,roi,stoploss,trailing,default} [{all,buy,sell,roi,stoploss,trailing,default} ...]] [--dmmp] [--print-all] [--no-color] [--print-json] [-j JOBS] [--random-state INT] [--min-trades INT] [--continue] [--hyperopt-loss NAME] @@ -308,9 +313,9 @@ optional arguments: Allow buying the same pair multiple times (position stacking). -e INT, --epochs INT Specify number of epochs (default: 100). - --spaces {all,buy,sell,roi,stoploss} [{all,buy,sell,roi,stoploss} ...] + --spaces {all,buy,sell,roi,stoploss,trailing,default} [{all,buy,sell,roi,stoploss,trailing,default} ...] Specify which parameters to hyperopt. Space-separated - list. Default: `all`. + list. --dmmp, --disable-max-market-positions Disable applying `max_open_trades` during backtest (same as setting `max_open_trades` to a very high @@ -338,16 +343,20 @@ optional arguments: target for optimization is different. Built-in Hyperopt-loss-functions are: DefaultHyperOptLoss, OnlyProfitHyperOptLoss, SharpeHyperOptLoss, - SharpeHyperOptLossDaily (default: `DefaultHyperOptLoss`). + SharpeHyperOptLossDaily.(default: + `DefaultHyperOptLoss`). Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. + --logfile FILE Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. -V, --version show program's version number and exit -c PATH, --config PATH - Specify configuration file (default: `config.json`). - Multiple --config options may be used. Can be set to - `-` to read config from stdin. + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. -d PATH, --datadir PATH Path to directory with historical backtesting data. --userdir PATH, --user-data-dir PATH @@ -358,6 +367,7 @@ Strategy arguments: Specify strategy class name which will be used by the bot. --strategy-path PATH Specify additional strategy lookup path. + ``` ## Edge commands @@ -394,12 +404,15 @@ optional arguments: Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. + --logfile FILE Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. -V, --version show program's version number and exit -c PATH, --config PATH - Specify configuration file (default: `config.json`). - Multiple --config options may be used. Can be set to - `-` to read config from stdin. + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. -d PATH, --datadir PATH Path to directory with historical backtesting data. --userdir PATH, --user-data-dir PATH @@ -410,6 +423,7 @@ Strategy arguments: Specify strategy class name which will be used by the bot. --strategy-path PATH Specify additional strategy lookup path. + ``` To understand edge and how to read the results, please read the [edge documentation](edge.md). diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index d37870ea0..580c9e298 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -111,10 +111,14 @@ class Arguments: conf_required = ('command' in parsed_arg and parsed_arg.command in NO_CONF_REQURIED) if 'user_data_dir' in parsed_arg and parsed_arg.user_data_dir is not None: + user_dir = parsed_arg.user_data_dir + else: + # Default case + user_dir = 'user_data' # Try loading from "user_data/config.json" - cfgfile = Path(parsed_arg.user_data_dir) / DEFAULT_CONFIG - if cfgfile.is_file() or not conf_required: - parsed_arg.config = [str(cfgfile)] + cfgfile = Path(user_dir) / DEFAULT_CONFIG + if cfgfile.is_file(): + parsed_arg.config = [str(cfgfile)] else: # Else use "config.json". cfgfile = Path.cwd() / DEFAULT_CONFIG diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 60da0082a..61bca04a4 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -18,7 +18,8 @@ def test_parse_args_none() -> None: assert isinstance(arguments.parser, argparse.ArgumentParser) -def test_parse_args_defaults() -> None: +def test_parse_args_defaults(mocker) -> None: + mocker.patch.object(Path, "is_file", MagicMock(side_effect=[False, True])) args = Arguments(['trade']).get_parsed_arg() assert args["config"] == ['config.json'] assert args["strategy_path"] is None @@ -26,6 +27,25 @@ def test_parse_args_defaults() -> None: assert args["verbosity"] == 0 +def test_parse_args_default_userdatadir(mocker) -> None: + mocker.patch.object(Path, "is_file", MagicMock(return_value=True)) + args = Arguments(['trade']).get_parsed_arg() + # configuration defaults to user_data if that is available. + assert args["config"] == ['user_data/config.json'] + assert args["strategy_path"] is None + assert args["datadir"] is None + assert args["verbosity"] == 0 + + +def test_parse_args_userdatadir(mocker) -> None: + mocker.patch.object(Path, "is_file", MagicMock(return_value=True)) + args = Arguments(['trade', '--user-data-dir', 'user_data']).get_parsed_arg() + # configuration defaults to user_data if that is available. + assert args["config"] == ['user_data/config.json'] + assert args["strategy_path"] is None + assert args["datadir"] is None + assert args["verbosity"] == 0 + def test_parse_args_config() -> None: args = Arguments(['trade', '-c', '/dev/null']).get_parsed_arg() assert args["config"] == ['/dev/null'] @@ -208,7 +228,7 @@ def test_config_notrequired(mocker) -> None: assert pargs["config"] is None # When file exists: - mocker.patch.object(Path, "is_file", MagicMock(return_value=True)) + mocker.patch.object(Path, "is_file", MagicMock(side_effect=[False, True])) args = [ 'download-data', ] diff --git a/tests/test_main.py b/tests/test_main.py index 1229f748a..70b784002 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -5,8 +5,9 @@ from unittest.mock import MagicMock, PropertyMock import pytest +from pathlib import Path from freqtrade.commands import Arguments -from freqtrade.exceptions import OperationalException, FreqtradeException +from freqtrade.exceptions import FreqtradeException, OperationalException from freqtrade.freqtradebot import FreqtradeBot from freqtrade.main import main from freqtrade.state import State @@ -26,6 +27,7 @@ def test_parse_args_backtesting(mocker) -> None: Test that main() can start backtesting and also ensure we can pass some specific arguments further argument parsing is done in test_arguments.py """ + mocker.patch.object(Path, "is_file", MagicMock(side_effect=[False, True])) backtesting_mock = mocker.patch('freqtrade.commands.start_backtesting') backtesting_mock.__name__ = PropertyMock("start_backtesting") # it's sys.exit(0) at the end of backtesting @@ -42,6 +44,7 @@ def test_parse_args_backtesting(mocker) -> None: def test_main_start_hyperopt(mocker) -> None: + mocker.patch.object(Path, "is_file", MagicMock(side_effect=[False, True])) hyperopt_mock = mocker.patch('freqtrade.commands.start_hyperopt', MagicMock()) hyperopt_mock.__name__ = PropertyMock("start_hyperopt") # it's sys.exit(0) at the end of hyperopt From ecca7164d91a41ee252c93c2533e6e7737d0db71 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 14 Feb 2020 20:13:36 +0100 Subject: [PATCH 4/5] Fix small issue --- tests/test_arguments.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 61bca04a4..22383661b 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -46,6 +46,7 @@ def test_parse_args_userdatadir(mocker) -> None: assert args["datadir"] is None assert args["verbosity"] == 0 + def test_parse_args_config() -> None: args = Arguments(['trade', '-c', '/dev/null']).get_parsed_arg() assert args["config"] == ['/dev/null'] From f024cc40d3808bfd75d55e75f8a8370b86cae57e Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 14 Feb 2020 20:21:09 +0100 Subject: [PATCH 5/5] Fix windows test failure --- tests/test_arguments.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 22383661b..0052a61d0 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -31,7 +31,7 @@ def test_parse_args_default_userdatadir(mocker) -> None: mocker.patch.object(Path, "is_file", MagicMock(return_value=True)) args = Arguments(['trade']).get_parsed_arg() # configuration defaults to user_data if that is available. - assert args["config"] == ['user_data/config.json'] + assert args["config"] == [str(Path('user_data/config.json'))] assert args["strategy_path"] is None assert args["datadir"] is None assert args["verbosity"] == 0 @@ -41,7 +41,7 @@ def test_parse_args_userdatadir(mocker) -> None: mocker.patch.object(Path, "is_file", MagicMock(return_value=True)) args = Arguments(['trade', '--user-data-dir', 'user_data']).get_parsed_arg() # configuration defaults to user_data if that is available. - assert args["config"] == ['user_data/config.json'] + assert args["config"] == [str(Path('user_data/config.json'))] assert args["strategy_path"] is None assert args["datadir"] is None assert args["verbosity"] == 0