Add list-pairs and list-markets subcommands

This commit is contained in:
hroff-1902 2019-10-13 13:12:20 +03:00
parent 31389b38f1
commit 7cf7982565
5 changed files with 108 additions and 2 deletions

View File

@ -2,6 +2,7 @@
This module contains the argument manager class This module contains the argument manager class
""" """
import argparse import argparse
from functools import partial
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
@ -33,6 +34,8 @@ ARGS_LIST_EXCHANGES = ["print_one_column", "list_exchanges_all"]
ARGS_LIST_TIMEFRAMES = ["exchange", "print_one_column"] ARGS_LIST_TIMEFRAMES = ["exchange", "print_one_column"]
ARGS_LIST_PAIRS = ["exchange", "print_list", "base_currency", "quote_currency", "active_only"]
ARGS_CREATE_USERDIR = ["user_data_dir"] ARGS_CREATE_USERDIR = ["user_data_dir"]
ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "exchange", "timeframes", "erase"] ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "exchange", "timeframes", "erase"]
@ -43,7 +46,8 @@ ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit", "db_
ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url", ARGS_PLOT_PROFIT = ["pairs", "timerange", "export", "exportfilename", "db_url",
"trade_source", "ticker_interval"] "trade_source", "ticker_interval"]
NO_CONF_REQURIED = ["download-data", "list-timeframes", "plot-dataframe", "plot-profit"] NO_CONF_REQURIED = ["download-data", "list-timeframes", "list-markets", "list-pairs",
"plot-dataframe", "plot-profit"]
NO_CONF_ALLOWED = ["create-userdir", "list-exchanges"] NO_CONF_ALLOWED = ["create-userdir", "list-exchanges"]
@ -106,7 +110,8 @@ class Arguments:
""" """
from freqtrade.optimize import start_backtesting, start_hyperopt, start_edge from freqtrade.optimize import start_backtesting, start_hyperopt, start_edge
from freqtrade.utils import (start_create_userdir, start_download_data, from freqtrade.utils import (start_create_userdir, start_download_data,
start_list_exchanges, start_list_timeframes) start_list_exchanges, start_list_timeframes,
start_list_pairs)
subparsers = self.parser.add_subparsers(dest='subparser') subparsers = self.parser.add_subparsers(dest='subparser')
@ -147,6 +152,22 @@ class Arguments:
list_timeframes_cmd.set_defaults(func=start_list_timeframes) list_timeframes_cmd.set_defaults(func=start_list_timeframes)
self._build_args(optionlist=ARGS_LIST_TIMEFRAMES, parser=list_timeframes_cmd) self._build_args(optionlist=ARGS_LIST_TIMEFRAMES, parser=list_timeframes_cmd)
# Add list-markets subcommand
list_markets_cmd = subparsers.add_parser(
'list-markets',
help='Print markets on exchange.'
)
list_markets_cmd.set_defaults(func=partial(start_list_pairs, pairs_only=False))
self._build_args(optionlist=ARGS_LIST_PAIRS, parser=list_markets_cmd)
# Add list-pairs subcommand
list_pairs_cmd = subparsers.add_parser(
'list-pairs',
help='Print pairs on exchange.'
)
list_pairs_cmd.set_defaults(func=partial(start_list_pairs, pairs_only=True))
self._build_args(optionlist=ARGS_LIST_PAIRS, parser=list_pairs_cmd)
# Add download-data subcommand # Add download-data subcommand
download_data_cmd = subparsers.add_parser( download_data_cmd = subparsers.add_parser(
'download-data', 'download-data',

View File

@ -255,6 +255,26 @@ AVAILABLE_CLI_OPTIONS = {
help='Print all exchanges known to the ccxt library.', help='Print all exchanges known to the ccxt library.',
action='store_true', action='store_true',
), ),
# List pairs / markets
"print_list": Arg(
'--print-list',
help='Print list of pairs or market symbols. By default data is '
'printed in the tabular format.',
action='store_true',
),
"quote_currency": Arg(
'--quote-currency',
help='Select quote currency.',
),
"base_currency": Arg(
'--base-currency',
help='Select base currency.',
),
"active_only": Arg(
'--active-only',
help='Print only active pairs or markets.',
action='store_true',
),
# Script options # Script options
"pairs": Arg( "pairs": Arg(
'-p', '--pairs', '-p', '--pairs',

View File

@ -280,6 +280,25 @@ class Exchange:
self._load_markets() self._load_markets()
return self._api.markets return self._api.markets
def get_markets(self, base_currency: str = None, quote_currency: str = None,
pairs_only: bool = False, active_only: bool = False) -> Dict:
"""
Return exchange ccxt markets, filtered out by base currency and quote currency
if this was requested in parameters.
TODO: consider moving it to the Dataprovider
"""
markets = self.markets
if base_currency:
markets = {k: v for k, v in markets.items() if v['base'] == base_currency}
if quote_currency:
markets = {k: v for k, v in markets.items() if v['quote'] == quote_currency}
if pairs_only:
markets = {k: v for k, v in markets.items() if '/' in v['symbol']}
if active_only:
markets = {k: v for k, v in markets.items() if v['active']}
return markets
def klines(self, pair_interval: Tuple[str, str], copy=True) -> DataFrame: def klines(self, pair_interval: Tuple[str, str], copy=True) -> DataFrame:
if pair_interval in self._klines: if pair_interval in self._klines:
return self._klines[pair_interval].copy() if copy else self._klines[pair_interval] return self._klines[pair_interval].copy() if copy else self._klines[pair_interval]

View File

@ -121,3 +121,7 @@ def round_dict(d, n):
Rounds float values in the dict to n digits after the decimal point. Rounds float values in the dict to n digits after the decimal point.
""" """
return {k: (round(v, n) if isinstance(v, float) else v) for k, v in d.items()} return {k: (round(v, n) if isinstance(v, float) else v) for k, v in d.items()}
def plural(num, singular: str, plural: str = None) -> str:
return singular if (num == 1 or num == -1) else plural or singular + 's'

View File

@ -4,12 +4,14 @@ from pathlib import Path
from typing import Any, Dict, List from typing import Any, Dict, List
import arrow import arrow
from tabulate import tabulate
from freqtrade import OperationalException from freqtrade import OperationalException
from freqtrade.configuration import Configuration, TimeRange from freqtrade.configuration import Configuration, TimeRange
from freqtrade.configuration.directory_operations import create_userdata_dir from freqtrade.configuration.directory_operations import create_userdata_dir
from freqtrade.data.history import refresh_backtest_ohlcv_data from freqtrade.data.history import refresh_backtest_ohlcv_data
from freqtrade.exchange import available_exchanges, ccxt_exchanges from freqtrade.exchange import available_exchanges, ccxt_exchanges
from freqtrade.misc import plural
from freqtrade.resolvers import ExchangeResolver from freqtrade.resolvers import ExchangeResolver
from freqtrade.state import RunMode from freqtrade.state import RunMode
@ -117,3 +119,43 @@ def start_list_timeframes(args: Dict[str, Any]) -> None:
else: else:
print(f"Timeframes available for the exchange `{config['exchange']['name']}`: " print(f"Timeframes available for the exchange `{config['exchange']['name']}`: "
f"{', '.join(exchange.timeframes)}") f"{', '.join(exchange.timeframes)}")
def start_list_pairs(args: Dict[str, Any], pairs_only: bool = False) -> None:
"""
Print pairs on the exchange
:param args: Cli args from Arguments()
:param pairs_only: if True print only pairs, otherwise print all instruments (markets)
:return: None
"""
config = setup_utils_configuration(args, RunMode.OTHER)
# Init exchange
exchange = ExchangeResolver(config['exchange']['name'], config).exchange
active_only = args.get('active_only', False)
base_currency = args.get('base_currency', '')
quote_currency = args.get('quote_currency', '')
pairs = exchange.get_markets(base_currency=base_currency,
quote_currency=quote_currency,
pairs_only=pairs_only,
active_only=active_only)
if args.get('print_list', False):
# print data as a list
print(f"Exchange {exchange.name} has {len(pairs)} " +
(plural(len(pairs), "pair" if pairs_only else "market")) +
(f" with {base_currency} as base currency" if base_currency else "") +
(" and" if base_currency and quote_currency else "") +
(f" with {quote_currency} as quote currency" if quote_currency else "") +
(f": {sorted(pairs.keys())}" if len(pairs) else "") + ".")
else:
# print data as a table
tabular_data = []
for _, v in pairs.items():
tabular_data.append([v['id'], v['symbol'], v['base'], v['quote'],
"Yes" if v['active'] else "No"])
headers = ['Id', 'Symbol', 'Base', 'Quote', 'Active']
print(tabulate(tabular_data, headers=headers, tablefmt='pipe'))