From 153434561dc598d8857cf3e31ef09a16f5e6a05e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 30 Nov 2019 19:53:22 +0100 Subject: [PATCH 1/9] Add test_pairlist method --- freqtrade/configuration/arguments.py | 13 ++++++++++++- freqtrade/pairlist/PrecisionFilter.py | 1 + freqtrade/utils.py | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index b23366d7a..3add8e04a 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -37,6 +37,8 @@ ARGS_LIST_TIMEFRAMES = ["exchange", "print_one_column"] ARGS_LIST_PAIRS = ["exchange", "print_list", "list_pairs_print_json", "print_one_column", "print_csv", "base_currencies", "quote_currencies", "list_pairs_all"] +ARGS_TEST_PAIRLIST = ["config", "quote_currencies"] + ARGS_CREATE_USERDIR = ["user_data_dir", "reset"] ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"] @@ -63,6 +65,7 @@ class Arguments: """ Arguments Class. Manage the arguments received by the cli """ + def __init__(self, args: Optional[List[str]]) -> None: self.args = args self._parsed_arg: Optional[argparse.Namespace] = None @@ -122,7 +125,7 @@ class Arguments: from freqtrade.utils import (start_create_userdir, start_download_data, start_list_exchanges, start_list_markets, start_new_hyperopt, start_new_strategy, - start_list_timeframes, start_trading) + start_list_timeframes, start_test_pairlist, start_trading) from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit subparsers = self.parser.add_subparsers(dest='command', @@ -211,6 +214,14 @@ class Arguments: list_pairs_cmd.set_defaults(func=partial(start_list_markets, pairs_only=True)) self._build_args(optionlist=ARGS_LIST_PAIRS, parser=list_pairs_cmd) + # Add test-pairlist subcommand + test_pairlist_cmd = subparsers.add_parser( + 'test-pairlist', + help='Test your pairlist configuration.', + ) + test_pairlist_cmd.set_defaults(func=start_test_pairlist) + self._build_args(optionlist=ARGS_TEST_PAIRLIST, parser=test_pairlist_cmd) + # Add download-data subcommand download_data_cmd = subparsers.add_parser( 'download-data', diff --git a/freqtrade/pairlist/PrecisionFilter.py b/freqtrade/pairlist/PrecisionFilter.py index d7b2c96ae..aedcc5a88 100644 --- a/freqtrade/pairlist/PrecisionFilter.py +++ b/freqtrade/pairlist/PrecisionFilter.py @@ -48,6 +48,7 @@ class PrecisionFilter(IPairList): """ Filters and sorts pairlists and assigns and returns them again. """ + stoploss = None if self._config.get('stoploss') is not None: # Precalculate sanitized stoploss value to avoid recalculation for every pair stoploss = 1 - abs(self._config.get('stoploss')) diff --git a/freqtrade/utils.py b/freqtrade/utils.py index c71080d5a..f43bdaed2 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -322,3 +322,23 @@ def start_list_markets(args: Dict[str, Any], pairs_only: bool = False) -> None: args.get('list_pairs_print_json', False) or args.get('print_csv', False)): print(f"{summary_str}.") + + +def start_test_pairlist(args: Dict[str, Any]) -> None: + """ + Test Pairlists + """ + from freqtrade.pairlist.pairlistmanager import PairListManager + config = setup_utils_configuration(args, RunMode.UTIL_EXCHANGE) + + exchange = ExchangeResolver(config['exchange']['name'], config, validate=False).exchange + + quote_currencies = args.get('quote_currencies', [config.get('stake_currency')]) + + for curr in quote_currencies: + config['stake_currency'] = curr + # Do not use ticker_interval set in the config + pairlists = PairListManager(exchange, config) + pairlists.refresh_pairlist() + print(f"Pairs for {curr}: ") + print(pairlists.whitelist) From 150a497cb4b0c4f530103c820458ec2eb69fbb11 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 2 Dec 2019 06:56:19 +0100 Subject: [PATCH 2/9] output pairlist after fetching all --- freqtrade/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/freqtrade/utils.py b/freqtrade/utils.py index f43bdaed2..099c1142e 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -334,11 +334,14 @@ def start_test_pairlist(args: Dict[str, Any]) -> None: exchange = ExchangeResolver(config['exchange']['name'], config, validate=False).exchange quote_currencies = args.get('quote_currencies', [config.get('stake_currency')]) - + results = {} for curr in quote_currencies: config['stake_currency'] = curr # Do not use ticker_interval set in the config pairlists = PairListManager(exchange, config) pairlists.refresh_pairlist() + results[curr] = pairlists.whitelist + + for curr, pairlist in results.items(): print(f"Pairs for {curr}: ") - print(pairlists.whitelist) + print(pairlist) From 0b03c6c786ffb972cfc71ac408f961ca93ad2a64 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 2 Dec 2019 07:00:38 +0100 Subject: [PATCH 3/9] Implement to json --- freqtrade/configuration/arguments.py | 2 +- freqtrade/utils.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index 3add8e04a..25e4386a8 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -37,7 +37,7 @@ ARGS_LIST_TIMEFRAMES = ["exchange", "print_one_column"] ARGS_LIST_PAIRS = ["exchange", "print_list", "list_pairs_print_json", "print_one_column", "print_csv", "base_currencies", "quote_currencies", "list_pairs_all"] -ARGS_TEST_PAIRLIST = ["config", "quote_currencies"] +ARGS_TEST_PAIRLIST = ["config", "quote_currencies", "print_one_column", "list_pairs_print_json"] ARGS_CREATE_USERDIR = ["user_data_dir", "reset"] diff --git a/freqtrade/utils.py b/freqtrade/utils.py index 099c1142e..b7873876a 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -344,4 +344,13 @@ def start_test_pairlist(args: Dict[str, Any]) -> None: for curr, pairlist in results.items(): print(f"Pairs for {curr}: ") - print(pairlist) + summary_str = "" + if args.get('print_list', False): + # print data as a list, with human-readable summary + print(f"{summary_str}: {', '.join(pairlist)}.") + elif args.get('print_one_column', False): + print('\n'.join(pairlist)) + elif args.get('list_pairs_print_json', False): + print(rapidjson.dumps(list(pairlist), default=str)) + else: + print(pairlist) From 683406b57d9daf0c3c609a011b1ff99d0f8ae6a7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 3 Dec 2019 06:36:43 +0100 Subject: [PATCH 4/9] correct fallback to stake_currency --- freqtrade/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/utils.py b/freqtrade/utils.py index b7873876a..da0ef1fff 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -333,7 +333,9 @@ def start_test_pairlist(args: Dict[str, Any]) -> None: exchange = ExchangeResolver(config['exchange']['name'], config, validate=False).exchange - quote_currencies = args.get('quote_currencies', [config.get('stake_currency')]) + quote_currencies = args.get('quote_currencies') + if not quote_currencies: + quote_currencies = [config.get('stake_currency')] results = {} for curr in quote_currencies: config['stake_currency'] = curr From 298e8b23320d4ac035ddf14b327a72e0d5b11b2b Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 3 Dec 2019 15:10:27 +0100 Subject: [PATCH 5/9] Add testcase for test_pairlist --- freqtrade/utils.py | 2 +- tests/test_utils.py | 40 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/freqtrade/utils.py b/freqtrade/utils.py index da0ef1fff..817d642a0 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -326,7 +326,7 @@ def start_list_markets(args: Dict[str, Any], pairs_only: bool = False) -> None: def start_test_pairlist(args: Dict[str, Any]) -> None: """ - Test Pairlists + Test Pairlist configuration """ from freqtrade.pairlist.pairlistmanager import PairListManager config = setup_utils_configuration(args, RunMode.UTIL_EXCHANGE) diff --git a/tests/test_utils.py b/tests/test_utils.py index 1258c939c..67ec8409b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -10,8 +10,9 @@ from freqtrade.utils import (setup_utils_configuration, start_create_userdir, start_download_data, start_list_exchanges, start_list_markets, start_list_timeframes, start_new_hyperopt, start_new_strategy, - start_trading) -from tests.conftest import get_args, log_has, log_has_re, patch_exchange + start_test_pairlist, start_trading) +from tests.conftest import (get_args, log_has, log_has_re, patch_exchange, + patched_configuration_load_config_file) def test_setup_utils_configuration(): @@ -573,7 +574,7 @@ def test_download_data_no_exchange(mocker, caplog): ) args = [ "download-data", - ] + ] pargs = get_args(args) pargs['config'] = None with pytest.raises(OperationalException, @@ -623,3 +624,36 @@ def test_download_data_trades(mocker, caplog): assert dl_mock.call_args[1]['timerange'].starttype == "date" assert dl_mock.call_count == 1 assert convert_mock.call_count == 1 + + +def test_start_test_pairlist(mocker, caplog, markets, tickers, default_conf, capsys): + mocker.patch.multiple('freqtrade.exchange.Exchange', + markets=PropertyMock(return_value=markets), + exchange_has=MagicMock(return_value=True), + get_tickers=tickers + ) + + default_conf['pairlists'] = [ + { + "method": "VolumePairList", + "number_assets": 5, + "sort_key": "quoteVolume", + }, + {"method": "PrecisionFilter"}, + {"method": "PriceFilter", "low_price_ratio": 0.02}, + ] + + patched_configuration_load_config_file(mocker, default_conf) + args = [ + 'test-pairlist', + '-c', 'config.json.example' + ] + + start_test_pairlist(get_args(args)) + + assert log_has_re(r"^Using resolved pairlist VolumePairList.*", caplog) + assert log_has_re(r"^Using resolved pairlist PrecisionFilter.*", caplog) + assert log_has_re(r"^Using resolved pairlist PriceFilter.*", caplog) + captured = capsys.readouterr() + assert re.match(r"Pairs for .*", captured.out) + assert re.match("['ETH/BTC', 'TKN/BTC', 'BLK/BTC', 'LTC/BTC', 'XRP/BTC']", captured.out) From b33e47a49ee164b916dd5441299d1fb0019c0888 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 3 Dec 2019 16:15:10 +0100 Subject: [PATCH 6/9] Update documentation with test-pairlist --- docs/backtesting.md | 9 +++++---- docs/configuration.md | 3 +++ docs/utils.md | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 19814303b..68782bb9c 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -11,14 +11,15 @@ Now you have good Buy and Sell strategies and some historic data, you want to te real data. This is what we call [backtesting](https://en.wikipedia.org/wiki/Backtesting). -Backtesting will use the crypto-currencies (pairs) from your config file -and load ticker data from `user_data/data/` by default. -If no data is available for the exchange / pair / ticker interval combination, backtesting will -ask you to download them first using `freqtrade download-data`. +Backtesting will use the crypto-currencies (pairs) from your config file and load ticker data from `user_data/data/` by default. +If no data is available for the exchange / pair / ticker interval combination, backtesting will ask you to download them first using `freqtrade download-data`. For details on downloading, please refer to the [Data Downloading](data-download.md) section in the documentation. The result of backtesting will confirm if your bot has better odds of making a profit than a loss. +!!! Tip "Using dynamic pairlists for backtesting" + While using dynamic pairlists during backtesting is not possible, a dynamic pairlist using current data can be generated via the [`test-pairlist`](utils.md#test-pairlist) command, and needs to be specified as `"pair_whitelist"` attribute in the configuration. + ### Run a backtesting against the currencies listed in your config file #### With 5 min tickers (Per default) diff --git a/docs/configuration.md b/docs/configuration.md index 024760fb9..5ad1a886e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -405,6 +405,9 @@ Inactive markets and blacklisted pairs are always removed from the resulting `pa * [`PrecisionFilter`](#precision-filter) * [`PriceFilter`](#price-pair-filter) +!!! Tip "Testing pairlists" + Pairlist configurations can be quite tricky to get right. Best use the [`test-pairlist`](utils.md#test-pairlist) subcommand to test your configuration quickly. + #### Static Pair List By default, the `StaticPairList` method is used, which uses a statically defined pair whitelist from the configuration. diff --git a/docs/utils.md b/docs/utils.md index ca4b645a5..19368059a 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -234,3 +234,35 @@ $ freqtrade -c config_binance.json list-pairs --all --base BTC ETH --quote USDT ``` $ freqtrade list-markets --exchange kraken --all ``` + +## Test pairlist + +Use the `test-pairlist` subcommand to test the configuration of [dynamic pairlists](configuration.md#pairlists). + +Requires a configuration with specified `pairlists` attribute. +Can be used to generate static pairlists to be used during backtesting / hyperopt. + +### Examples + +Show whitelist when using a [dynamic pairlist](configuration.md#pairlists). + +``` +freqtrade test-pairlist --config config.json --quote USDT BTC +``` + +``` +usage: freqtrade test-pairlist [-h] [-c PATH] + [--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]] + [-1] [--print-json] + +optional arguments: + -h, --help show this help message 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. + --quote QUOTE_CURRENCY [QUOTE_CURRENCY ...] + Specify quote currency(-ies). Space-separated list. + -1, --one-column Print output in one column. + --print-json Print list of pairs or market symbols in JSON format. +``` From 0ba804d051d8b94f1e99cba45adc696f66e12f21 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Dec 2019 12:14:37 +0100 Subject: [PATCH 7/9] Address first part of feedback --- freqtrade/utils.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/freqtrade/utils.py b/freqtrade/utils.py index 817d642a0..c174f189e 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -346,11 +346,8 @@ def start_test_pairlist(args: Dict[str, Any]) -> None: for curr, pairlist in results.items(): print(f"Pairs for {curr}: ") - summary_str = "" - if args.get('print_list', False): - # print data as a list, with human-readable summary - print(f"{summary_str}: {', '.join(pairlist)}.") - elif args.get('print_one_column', False): + + if args.get('print_one_column', False): print('\n'.join(pairlist)) elif args.get('list_pairs_print_json', False): print(rapidjson.dumps(list(pairlist), default=str)) From 51f074ba4bce67985a3af587e46981e5b15e73e4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Dec 2019 12:25:57 +0100 Subject: [PATCH 8/9] Don't print quote-currency for -1 --- freqtrade/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/utils.py b/freqtrade/utils.py index c174f189e..9a00f1f26 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -345,7 +345,8 @@ def start_test_pairlist(args: Dict[str, Any]) -> None: results[curr] = pairlists.whitelist for curr, pairlist in results.items(): - print(f"Pairs for {curr}: ") + if not args.get('print_one_column', False): + print(f"Pairs for {curr}: ") if args.get('print_one_column', False): print('\n'.join(pairlist)) From 16a50fbe4e76ac304a3adbc3040bd538ca7456e4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 4 Dec 2019 14:30:53 +0100 Subject: [PATCH 9/9] Resort documentation --- docs/utils.md | 68 ++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/docs/utils.md b/docs/utils.md index 19368059a..4ad32055a 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -43,20 +43,6 @@ The file will be named inline with your class name, and will not overwrite exist Results will be located in `user_data/strategies/.py`. -### Sample usage of new-strategy - -```bash -freqtrade new-strategy --strategy AwesomeStrategy -``` - -With custom user directory - -```bash -freqtrade new-strategy --userdir ~/.freqtrade/ --strategy AwesomeStrategy -``` - -### new-strategy complete options - ``` output usage: freqtrade new-strategy [-h] [--userdir PATH] [-s NAME] [--template {full,minimal}] @@ -75,6 +61,18 @@ optional arguments: ``` +### Sample usage of new-strategy + +```bash +freqtrade new-strategy --strategy AwesomeStrategy +``` + +With custom user directory + +```bash +freqtrade new-strategy --userdir ~/.freqtrade/ --strategy AwesomeStrategy +``` + ## Create new hyperopt Creates a new hyperopt from a template similar to SampleHyperopt. @@ -82,20 +80,6 @@ The file will be named inline with your class name, and will not overwrite exist Results will be located in `user_data/hyperopts/.py`. -### Sample usage of new-hyperopt - -```bash -freqtrade new-hyperopt --hyperopt AwesomeHyperopt -``` - -With custom user directory - -```bash -freqtrade new-hyperopt --userdir ~/.freqtrade/ --hyperopt AwesomeHyperopt -``` - -### new-hyperopt complete options - ``` output usage: freqtrade new-hyperopt [-h] [--userdir PATH] [--hyperopt NAME] [--template {full,minimal}] @@ -112,6 +96,18 @@ optional arguments: `full`. ``` +### Sample usage of new-hyperopt + +```bash +freqtrade new-hyperopt --hyperopt AwesomeHyperopt +``` + +With custom user directory + +```bash +freqtrade new-hyperopt --userdir ~/.freqtrade/ --hyperopt AwesomeHyperopt +``` + ## List Exchanges Use the `list-exchanges` subcommand to see the exchanges available for the bot. @@ -242,14 +238,6 @@ Use the `test-pairlist` subcommand to test the configuration of [dynamic pairlis Requires a configuration with specified `pairlists` attribute. Can be used to generate static pairlists to be used during backtesting / hyperopt. -### Examples - -Show whitelist when using a [dynamic pairlist](configuration.md#pairlists). - -``` -freqtrade test-pairlist --config config.json --quote USDT BTC -``` - ``` usage: freqtrade test-pairlist [-h] [-c PATH] [--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]] @@ -266,3 +254,11 @@ optional arguments: -1, --one-column Print output in one column. --print-json Print list of pairs or market symbols in JSON format. ``` + +### Examples + +Show whitelist when using a [dynamic pairlist](configuration.md#pairlists). + +``` +freqtrade test-pairlist --config config.json --quote USDT BTC +```