feat(exchange): honor subclass has overrides in exchange validation

matstedt 1 month ago
parent 505606218f
commit 5bbfcdd143

@ -54,22 +54,25 @@ def available_exchanges(ccxt_module: CcxtModuleType | None = None) -> list[str]:
return [x for x in exchanges if validate_exchange(x)[0]]
def _exchange_has_helper(ex_mod: ccxt.Exchange, required: dict[str, list[str]]) -> list[str]:
def _exchange_has_helper(ex_has: dict[str, Any], required: dict[str, list[str]]) -> list[str]:
"""
Checks availability of methods (or their replacement)s in ex_mod.has
:param ex_mod: ccxt Exchange module
Checks availability of methods (or their replacement)s in a merged has-dict.
:param ex_has: merged "has" dict (ccxt + freqtrade overrides)
:param required: dict of required methods, with possible replacement methods as list
:return: list of missing required methods
"""
return [
k
for k, v in required.items()
if ex_mod.has.get(k) is not True
and (len(v) == 0 or not (all(ex_mod.has.get(x) for x in v)))
if ex_has.get(k) is not True and (len(v) == 0 or not (all(ex_has.get(x) for x in v)))
]
def validate_exchange(exchange: str) -> tuple[bool, str, str, ccxt.Exchange | None]:
def validate_exchange(
exchange: str,
ft_has_overrides: dict[str, Any] | None = None,
) -> tuple[bool, str, str, ccxt.Exchange | None]:
"""
returns: can_use, reason, exchange_object
with Reason including both missing and missing_opt
@ -82,17 +85,22 @@ def validate_exchange(exchange: str) -> tuple[bool, str, str, ccxt.Exchange | No
if not ex_mod or not ex_mod.has:
return False, "", "", None
ex_has = dict(ex_mod.has or {})
if ft_has_overrides:
ex_has.update(ft_has_overrides)
result = True
reasons = []
reasons_fut = ""
missing = _exchange_has_helper(ex_mod, EXCHANGE_HAS_REQUIRED)
missing = _exchange_has_helper(ex_has, EXCHANGE_HAS_REQUIRED)
if missing:
result = False
reasons.append(f"missing: {', '.join(missing)}")
missing_opt = _exchange_has_helper(ex_mod, EXCHANGE_HAS_OPTIONAL)
missing_opt = _exchange_has_helper(ex_has, EXCHANGE_HAS_OPTIONAL)
missing_futures = _exchange_has_helper(ex_mod, EXCHANGE_HAS_OPTIONAL_FUTURES)
missing_futures = _exchange_has_helper(ex_has, EXCHANGE_HAS_OPTIONAL_FUTURES)
if exchange.lower() in BAD_EXCHANGES:
result = False
@ -111,8 +119,17 @@ def _build_exchange_list_entry(
exchange_name: str, exchangeClasses: dict[str, Any]
) -> ValidExchangesType:
exchange_name = exchange_name.lower()
valid, comment, comment_fut, ex_mod = validate_exchange(exchange_name)
mapped_exchange_name = MAP_EXCHANGE_CHILDCLASS.get(exchange_name, exchange_name).lower()
resolved = exchangeClasses.get(mapped_exchange_name)
ft_has_overrides = None
if resolved:
get_ft_has = getattr(resolved["class"], "get_ft_has", None)
if callable(get_ft_has):
ft_has_overrides = get_ft_has() or None
valid, comment, comment_fut, ex_mod = validate_exchange(exchange_name, ft_has_overrides)
is_alias = getattr(ex_mod, "alias", False)
result: ValidExchangesType = {
"name": getattr(ex_mod, "name", exchange_name),
@ -128,7 +145,7 @@ def _build_exchange_list_entry(
else None,
"trade_modes": [{"trading_mode": "spot", "margin_mode": ""}],
}
if resolved := exchangeClasses.get(mapped_exchange_name):
if resolved:
supported_modes: list[TradeModeType] = [
{"trading_mode": tm.value, "margin_mode": mm.value}
for tm, mm in resolved["class"]._supported_trading_mode_margin_pairs

Loading…
Cancel
Save