Repository: freqtrade/freqtrade

Issue:
Backtest with dynamic-whitelist?
Is it possible to backtest using --dynamic-whitelist?


Discussion:
at the moment it is not possible.

It would be a good idea, but i'm not sure how to implement it yet.
- what should that be based on? the current highest volumes - or historic (at that point in time)?

if using historic data, it would require to have all (non-blacklisted) pairs of the exchange downloaded for the required period - depending on the exchange that could be a ton of pairs.

If using current volume - it would taint falsify the result as this is not how `--dynamic-whitelist` really works, which can change the top 20 pairs if the volume for them changes.


I'ts possible to get a complete pair list from Exchange(i use binance), so we can download all the pair story, and backtest using --dynamic-whitelist?
> I'ts possible to get a complete pair list from Exchange(i use binance), so we can download all the pair story, and backtest using --dynamic-whitelist?

Yes, use `freqtrade list-pairs --exchange binance --all --print-json`

Or even only those for BTC stake (quote) currency, for example: `freqtrade list-pairs --exchange binance --all --print-json --quote BTC`

For further details see https://www.freqtrade.io/en/latest/utils/#list-pairslist-markets

Btw, `--dynamic-whitelist` was deprecated and removed, it no longer exists.
See https://www.freqtrade.io/en/latest/deprecated/#the-dynamic-whitelist-command-line-option

Thanks you 
Is there a way to get the top XX volume list pairlist from the excange?
> Is there a way to get the top XX volume list pairlist from the excange?

yes, pls read the docs: https://www.freqtrade.io/en/latest/configuration/#dynamic-pairlists
not for backtesting - not yet anyway - but i'm working on something like this.

however that'll be a "current snapshot" ... not how it was back then (volumes today won't reflect volumes from last month ...).



Resolving patch:
diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py
--- a/freqtrade/configuration/arguments.py
+++ b/freqtrade/configuration/arguments.py
@@ -37,6 +37,8 @@
 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", "print_one_column", "list_pairs_print_json"]
+
 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 @@ def _build_subcommands(self) -> None:
         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 @@ def _build_subcommands(self) -> None:
         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
--- a/freqtrade/pairlist/PrecisionFilter.py
+++ b/freqtrade/pairlist/PrecisionFilter.py
@@ -48,6 +48,7 @@ def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]:
         """
         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
--- a/freqtrade/utils.py
+++ b/freqtrade/utils.py
@@ -322,3 +322,35 @@ 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 Pairlist configuration
+    """
+    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')
+    if not quote_currencies:
+        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():
+        if not args.get('print_one_column', False):
+            print(f"Pairs for {curr}: ")
+
+        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))
+        else:
+            print(pairlist)


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -10,8 +10,9 @@
                              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)