mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-14 04:03:55 +00:00
Enhance list-exchanges with more information
This commit is contained in:
parent
b5d1017779
commit
250ae2d006
|
@ -30,18 +30,34 @@ def start_list_exchanges(args: Dict[str, Any]) -> None:
|
||||||
if args['print_one_column']:
|
if args['print_one_column']:
|
||||||
print('\n'.join([e['name'] for e in exchanges]))
|
print('\n'.join([e['name'] for e in exchanges]))
|
||||||
else:
|
else:
|
||||||
if args['list_exchanges_all']:
|
|
||||||
print("All exchanges supported by the ccxt library:")
|
|
||||||
else:
|
|
||||||
print("Exchanges available for Freqtrade:")
|
|
||||||
exchanges = [e for e in exchanges if e['valid'] is not False]
|
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'name': 'Exchange name',
|
'name': 'Exchange name',
|
||||||
'valid': 'Valid',
|
'valid': 'Valid',
|
||||||
'comment': 'reason',
|
'supported': 'Supported',
|
||||||
|
'trade_modes': 'Markets',
|
||||||
|
'comment': 'Reason',
|
||||||
}
|
}
|
||||||
print(tabulate(exchanges, headers=headers))
|
|
||||||
|
def build_entry(exchange, valid):
|
||||||
|
valid_entry = {'valid': exchange['valid']} if valid else {}
|
||||||
|
result = {
|
||||||
|
'name': exchange['name'],
|
||||||
|
**valid_entry,
|
||||||
|
'supported': 'Official' if exchange['supported'] else '',
|
||||||
|
'trade_modes': ', '.join(exchange['trade_modes']),
|
||||||
|
'comment': exchange['comment'],
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
if args['list_exchanges_all']:
|
||||||
|
print("All exchanges supported by the ccxt library:")
|
||||||
|
exchanges = [build_entry(e, True) for e in exchanges]
|
||||||
|
else:
|
||||||
|
print("Exchanges available for Freqtrade:")
|
||||||
|
exchanges = [build_entry(e, False) for e in exchanges if e['valid'] is not False]
|
||||||
|
|
||||||
|
print(tabulate(exchanges, headers=headers, ))
|
||||||
|
|
||||||
|
|
||||||
def _print_objs_tabular(objs: List, print_colorized: bool) -> None:
|
def _print_objs_tabular(objs: List, print_colorized: bool) -> None:
|
||||||
|
|
|
@ -9,7 +9,8 @@ import ccxt
|
||||||
from ccxt import (DECIMAL_PLACES, ROUND, ROUND_DOWN, ROUND_UP, SIGNIFICANT_DIGITS, TICK_SIZE,
|
from ccxt import (DECIMAL_PLACES, ROUND, ROUND_DOWN, ROUND_UP, SIGNIFICANT_DIGITS, TICK_SIZE,
|
||||||
TRUNCATE, decimal_to_precision)
|
TRUNCATE, decimal_to_precision)
|
||||||
|
|
||||||
from freqtrade.exchange.common import BAD_EXCHANGES, EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED
|
from freqtrade.exchange.common import (BAD_EXCHANGES, EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED,
|
||||||
|
SUPPORTED_EXCHANGES)
|
||||||
from freqtrade.exchange.types import ValidExchangesType
|
from freqtrade.exchange.types import ValidExchangesType
|
||||||
from freqtrade.util import FtPrecise
|
from freqtrade.util import FtPrecise
|
||||||
from freqtrade.util.datetime_helpers import dt_from_ts, dt_ts
|
from freqtrade.util.datetime_helpers import dt_from_ts, dt_ts
|
||||||
|
@ -56,15 +57,39 @@ def validate_exchange(exchange: str) -> Tuple[bool, str]:
|
||||||
return True, ''
|
return True, ''
|
||||||
|
|
||||||
|
|
||||||
|
def build_exchange_list_entry(
|
||||||
|
exchange_name: str, exchangeClasses: Dict[str, Any]) -> ValidExchangesType:
|
||||||
|
valid, comment = validate_exchange(exchange_name)
|
||||||
|
result = {
|
||||||
|
'name': exchange_name,
|
||||||
|
'valid': valid,
|
||||||
|
'supported': exchange_name.lower() in SUPPORTED_EXCHANGES,
|
||||||
|
'comment': comment,
|
||||||
|
'trade_modes': ['spot'],
|
||||||
|
}
|
||||||
|
if resolved := exchangeClasses.get(exchange_name.lower()):
|
||||||
|
supported_modes = ['spot'] + [
|
||||||
|
f"{mm.value} {tm.value}"
|
||||||
|
for tm, mm in resolved['class']._supported_trading_mode_margin_pairs
|
||||||
|
]
|
||||||
|
result.update({
|
||||||
|
'trade_modes': supported_modes,
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def validate_exchanges(all_exchanges: bool) -> List[ValidExchangesType]:
|
def validate_exchanges(all_exchanges: bool) -> List[ValidExchangesType]:
|
||||||
"""
|
"""
|
||||||
:return: List of tuples with exchangename, valid, reason.
|
:return: List of tuples with exchangename, valid, reason.
|
||||||
"""
|
"""
|
||||||
exchanges = ccxt_exchanges() if all_exchanges else available_exchanges()
|
exchanges = ccxt_exchanges() if all_exchanges else available_exchanges()
|
||||||
|
from freqtrade.resolvers.exchange_resolver import ExchangeResolver
|
||||||
|
|
||||||
|
subclassed = {e['name'].lower(): e for e in ExchangeResolver.search_all_objects({}, False)}
|
||||||
|
|
||||||
exchanges_valid: List[ValidExchangesType] = [
|
exchanges_valid: List[ValidExchangesType] = [
|
||||||
{'name': e, 'valid': valid, 'comment': comment}
|
build_exchange_list_entry(e, subclassed) for e in exchanges
|
||||||
for e, valid, comment in ((e, *validate_exchange(e)) for e in exchanges)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
return exchanges_valid
|
return exchanges_valid
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
This module loads custom exchanges
|
This module loads custom exchanges
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional
|
from inspect import isclass
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
import freqtrade.exchange as exchanges
|
import freqtrade.exchange as exchanges
|
||||||
from freqtrade.constants import Config, ExchangeConfig
|
from freqtrade.constants import Config, ExchangeConfig
|
||||||
|
@ -72,3 +73,26 @@ class ExchangeResolver(IResolver):
|
||||||
f"Impossible to load Exchange '{exchange_name}'. This class does not exist "
|
f"Impossible to load Exchange '{exchange_name}'. This class does not exist "
|
||||||
"or contains Python code errors."
|
"or contains Python code errors."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def search_all_objects(cls, config: Config, enum_failed: bool,
|
||||||
|
recursive: bool = False) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Searches for valid objects
|
||||||
|
:param config: Config object
|
||||||
|
:param enum_failed: If True, will return None for modules which fail.
|
||||||
|
Otherwise, failing modules are skipped.
|
||||||
|
:param recursive: Recursively walk directory tree searching for strategies
|
||||||
|
:return: List of dicts containing 'name', 'class' and 'location' entries
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
for exchange_name in dir(exchanges):
|
||||||
|
exchange = getattr(exchanges, exchange_name)
|
||||||
|
if isclass(exchange) and issubclass(exchange, Exchange):
|
||||||
|
result.append({
|
||||||
|
'name': exchange_name,
|
||||||
|
'class': exchange,
|
||||||
|
'location': exchange.__module__,
|
||||||
|
'location_rel: ': exchange.__module__.replace('freqtrade.', ''),
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
|
|
@ -41,7 +41,7 @@ class IResolver:
|
||||||
object_type: Type[Any]
|
object_type: Type[Any]
|
||||||
object_type_str: str
|
object_type_str: str
|
||||||
user_subdir: Optional[str] = None
|
user_subdir: Optional[str] = None
|
||||||
initial_search_path: Optional[Path]
|
initial_search_path: Optional[Path] = None
|
||||||
# Optional config setting containing a path (strategy_path, freqaimodel_path)
|
# Optional config setting containing a path (strategy_path, freqaimodel_path)
|
||||||
extra_path: Optional[str] = None
|
extra_path: Optional[str] = None
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user