diff --git a/freqtrade/commands/list_commands.py b/freqtrade/commands/list_commands.py index b8e479462..e3c598786 100644 --- a/freqtrade/commands/list_commands.py +++ b/freqtrade/commands/list_commands.py @@ -1,11 +1,14 @@ import csv import logging import sys -from typing import Any, Dict, List, Union +from typing import Any, Dict, List import rapidjson from colorama import Fore, Style from colorama import init as colorama_init +from rich.console import Console +from rich.table import Table +from rich.text import Text from tabulate import tabulate from freqtrade.configuration import setup_utils_configuration @@ -14,7 +17,7 @@ from freqtrade.exceptions import ConfigurationError, OperationalException from freqtrade.exchange import list_available_exchanges, market_is_active from freqtrade.misc import parse_db_uri_for_logging, plural from freqtrade.resolvers import ExchangeResolver, StrategyResolver -from freqtrade.types import ValidExchangesType +from freqtrade.types.valid_exchanges_type import ValidExchangesType logger = logging.getLogger(__name__) @@ -26,51 +29,55 @@ def start_list_exchanges(args: Dict[str, Any]) -> None: :param args: Cli args from Arguments() :return: None """ - available_exchanges = list_available_exchanges(args["list_exchanges_all"]) + available_exchanges: List[ValidExchangesType] = list_available_exchanges( + args["list_exchanges_all"] + ) if args["print_one_column"]: print("\n".join([e["name"] for e in available_exchanges])) else: - headers = { - "name": "Exchange name", - "supported": "Supported", - "trade_modes": "Markets", - "comment": "Reason", - } - headers.update({"valid": "Valid"} if args["list_exchanges_all"] else {}) + if args["list_exchanges_all"]: + title = ( + f"All exchanges supported by the ccxt library " + f"({len(available_exchanges)} exchanges):" + ) + else: + available_exchanges = [e for e in available_exchanges if e["valid"] is not False] + title = f"Exchanges available for Freqtrade ({len(available_exchanges)} exchanges):" - def build_entry(exchange: ValidExchangesType, valid: bool): - valid_entry = {"valid": exchange["valid"]} if valid else {} - result: Dict[str, Union[str, bool]] = { - "name": exchange["name"], - **valid_entry, - "supported": "Official" if exchange["supported"] else "", - "trade_modes": ("DEX: " if exchange["dex"] else "") - + ", ".join( - (f"{a['margin_mode']} " if a["margin_mode"] else "") + a["trading_mode"] + table = Table(title=title) + + table.add_column("Exchange Name") + table.add_column("Markets") + table.add_column("Reason") + + for exchange in available_exchanges: + name = Text(exchange["name"]) + if exchange["supported"]: + name.append(" (Official)", style="italic") + name.stylize("green bold") + + trade_modes = Text( + ", ".join( + (f"{a.get('margin_mode', '')} {a["trading_mode"]}").lstrip() for a in exchange["trade_modes"] ), - "comment": exchange["comment"], - } - - return result - - if args["list_exchanges_all"]: - exchanges = [build_entry(e, True) for e in available_exchanges] - title = f"All exchanges supported by the ccxt library ({len(exchanges)} exchanges):" - else: - exchanges = [ - build_entry(e, False) for e in available_exchanges if e["valid"] is not False - ] - title = f"Exchanges available for Freqtrade ({len(exchanges)} exchanges):" - - print(title) - print( - tabulate( - exchanges, - headers=headers, + style="", ) - ) + if exchange["dex"]: + trade_modes = Text("DEX: ") + trade_modes + trade_modes.stylize("bold", 0, 3) + + table.add_row( + name, + trade_modes, + exchange["comment"], + style=None if exchange["valid"] else "red", + ) + # table.add_row(*[exchange[header] for header in headers]) + + console = Console() + console.print(table) def _print_objs_tabular(objs: List, print_colorized: bool) -> None: diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 02b234b6c..818533630 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -116,7 +116,7 @@ def test_list_exchanges(capsys): start_list_exchanges(get_args(args)) captured = capsys.readouterr() - assert re.match(r"Exchanges available for Freqtrade.*", captured.out) + assert re.search(r".*Exchanges available for Freqtrade.*", captured.out) assert re.search(r".*binance.*", captured.out) assert re.search(r".*bybit.*", captured.out) @@ -139,7 +139,7 @@ def test_list_exchanges(capsys): start_list_exchanges(get_args(args)) captured = capsys.readouterr() - assert re.match(r"All exchanges supported by the ccxt library.*", captured.out) + assert re.search(r"All exchanges supported by the ccxt library.*", captured.out) assert re.search(r".*binance.*", captured.out) assert re.search(r".*bingx.*", captured.out) assert re.search(r".*bitmex.*", captured.out)