mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-11-29 08:33:07 +00:00
feat: convert get_valid_pair_combination to generator
This commit is contained in:
@@ -7,7 +7,7 @@ import asyncio
|
||||
import inspect
|
||||
import logging
|
||||
import signal
|
||||
from collections.abc import Coroutine
|
||||
from collections.abc import Coroutine, Generator
|
||||
from copy import deepcopy
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from math import floor, isnan
|
||||
@@ -705,14 +705,22 @@ class Exchange:
|
||||
f"Available currencies are: {', '.join(quote_currencies)}"
|
||||
)
|
||||
|
||||
def get_valid_pair_combination(self, curr_1: str, curr_2: str) -> str:
|
||||
def get_valid_pair_combination(self, curr_1: str, curr_2: str) -> Generator[str, None, None]:
|
||||
"""
|
||||
Get valid pair combination of curr_1 and curr_2 by trying both combinations.
|
||||
"""
|
||||
for pair in [f"{curr_1}/{curr_2}", f"{curr_2}/{curr_1}"]:
|
||||
yielded = False
|
||||
for pair in (
|
||||
f"{curr_1}/{curr_2}",
|
||||
f"{curr_2}/{curr_1}",
|
||||
f"{curr_1}/{curr_2}:{curr_2}",
|
||||
f"{curr_2}/{curr_1}:{curr_1}",
|
||||
):
|
||||
if pair in self.markets and self.markets[pair].get("active"):
|
||||
return pair
|
||||
raise ValueError(f"Could not combine {curr_1} and {curr_2} to get a valid pair.")
|
||||
yielded = True
|
||||
yield pair
|
||||
if not yielded:
|
||||
raise ValueError(f"Could not combine {curr_1} and {curr_2} to get a valid pair.")
|
||||
|
||||
def validate_timeframes(self, timeframe: str | None) -> None:
|
||||
"""
|
||||
@@ -1868,26 +1876,25 @@ class Exchange:
|
||||
return 1.0
|
||||
tickers = self.get_tickers(cached=True)
|
||||
try:
|
||||
pair = self.get_valid_pair_combination(coin, currency)
|
||||
for pair in self.get_valid_pair_combination(coin, currency):
|
||||
ticker: Ticker | None = tickers.get(pair, None)
|
||||
if not ticker:
|
||||
tickers_other: Tickers = self.get_tickers(
|
||||
cached=True,
|
||||
market_type=(
|
||||
TradingMode.SPOT
|
||||
if self.trading_mode != TradingMode.SPOT
|
||||
else TradingMode.FUTURES
|
||||
),
|
||||
)
|
||||
ticker = tickers_other.get(pair, None)
|
||||
if ticker:
|
||||
rate: float | None = ticker.get("last", None)
|
||||
if rate and pair.startswith(currency) and not pair.endswith(currency):
|
||||
rate = 1.0 / rate
|
||||
return rate
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
ticker: Ticker | None = tickers.get(pair, None)
|
||||
if not ticker:
|
||||
tickers_other: Tickers = self.get_tickers(
|
||||
cached=True,
|
||||
market_type=(
|
||||
TradingMode.SPOT
|
||||
if self.trading_mode != TradingMode.SPOT
|
||||
else TradingMode.FUTURES
|
||||
),
|
||||
)
|
||||
ticker = tickers_other.get(pair, None)
|
||||
if ticker:
|
||||
rate: float | None = ticker.get("last", None)
|
||||
if rate and pair.startswith(currency) and not pair.endswith(currency):
|
||||
rate = 1.0 / rate
|
||||
return rate
|
||||
return None
|
||||
|
||||
@retrier
|
||||
@@ -2244,10 +2251,13 @@ class Exchange:
|
||||
# If cost is None or 0.0 -> falsy, return None
|
||||
return None
|
||||
try:
|
||||
comb = self.get_valid_pair_combination(fee_curr, self._config["stake_currency"])
|
||||
tick = self.fetch_ticker(comb)
|
||||
|
||||
fee_to_quote_rate = safe_value_fallback2(tick, tick, "last", "ask")
|
||||
for comb in self.get_valid_pair_combination(
|
||||
fee_curr, self._config["stake_currency"]
|
||||
):
|
||||
tick = self.fetch_ticker(comb)
|
||||
fee_to_quote_rate = safe_value_fallback2(tick, tick, "last", "ask")
|
||||
if tick:
|
||||
break
|
||||
except (ValueError, ExchangeError):
|
||||
fee_to_quote_rate = self._config["exchange"].get("unknown_fee_rate", None)
|
||||
if not fee_to_quote_rate:
|
||||
|
||||
@@ -2021,10 +2021,7 @@ def test_get_conversion_rate(default_conf_usdt, mocker, exchange_name):
|
||||
},
|
||||
}
|
||||
tick2 = {
|
||||
"XRP/USDT": {
|
||||
"symbol": "XRP/USDT",
|
||||
"bid": 0.5,
|
||||
"ask": 1,
|
||||
"ADA/USDT:USDT": {
|
||||
"last": 2.5,
|
||||
}
|
||||
}
|
||||
@@ -2044,7 +2041,7 @@ def test_get_conversion_rate(default_conf_usdt, mocker, exchange_name):
|
||||
assert api_mock.fetch_tickers.call_count == 1
|
||||
api_mock.fetch_tickers.reset_mock()
|
||||
|
||||
assert exchange.get_conversion_rate("XRP", "USDT") == 2.5
|
||||
assert exchange.get_conversion_rate("ADA", "USDT") == 2.5
|
||||
# Only the call to the "others" market
|
||||
assert api_mock.fetch_tickers.call_count == 1
|
||||
|
||||
@@ -4122,10 +4119,16 @@ def test_get_valid_pair_combination(default_conf, mocker, markets):
|
||||
)
|
||||
ex = Exchange(default_conf)
|
||||
|
||||
assert ex.get_valid_pair_combination("ETH", "BTC") == "ETH/BTC"
|
||||
assert ex.get_valid_pair_combination("BTC", "ETH") == "ETH/BTC"
|
||||
assert next(ex.get_valid_pair_combination("ETH", "BTC")) == "ETH/BTC"
|
||||
assert next(ex.get_valid_pair_combination("BTC", "ETH")) == "ETH/BTC"
|
||||
multicombs = list(ex.get_valid_pair_combination("ETH", "USDT"))
|
||||
assert len(multicombs) == 2
|
||||
assert "ETH/USDT" in multicombs
|
||||
assert "ETH/USDT:USDT" in multicombs
|
||||
|
||||
with pytest.raises(ValueError, match=r"Could not combine.* to get a valid pair."):
|
||||
ex.get_valid_pair_combination("NOPAIR", "ETH")
|
||||
for x in ex.get_valid_pair_combination("NOPAIR", "ETH"):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
@@ -4021,7 +4021,7 @@ def test_get_real_amount_fees_order(
|
||||
default_conf_usdt, market_buy_order_usdt_doublefee, fee, mocker
|
||||
):
|
||||
tfo_mock = mocker.patch(f"{EXMS}.get_trades_for_order", return_value=[])
|
||||
mocker.patch(f"{EXMS}.get_valid_pair_combination", return_value="BNB/USDT")
|
||||
mocker.patch(f"{EXMS}.get_valid_pair_combination", return_value=["BNB/USDT"])
|
||||
mocker.patch(f"{EXMS}.fetch_ticker", return_value={"last": 200})
|
||||
trade = Trade(
|
||||
pair="LTC/USDT",
|
||||
|
||||
@@ -586,7 +586,7 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers):
|
||||
fetch_positions=MagicMock(return_value=mock_pos),
|
||||
get_tickers=tickers,
|
||||
get_valid_pair_combination=MagicMock(
|
||||
side_effect=lambda a, b: f"{b}/{a}" if a == "USDT" else f"{a}/{b}"
|
||||
side_effect=lambda a, b: [f"{b}/{a}" if a == "USDT" else f"{a}/{b}"]
|
||||
),
|
||||
)
|
||||
default_conf_usdt["dry_run"] = False
|
||||
|
||||
@@ -556,7 +556,7 @@ def test_api_balance(botclient, mocker, rpc_balance, tickers):
|
||||
ftbot.config["dry_run"] = False
|
||||
mocker.patch(f"{EXMS}.get_balances", return_value=rpc_balance)
|
||||
mocker.patch(f"{EXMS}.get_tickers", tickers)
|
||||
mocker.patch(f"{EXMS}.get_valid_pair_combination", side_effect=lambda a, b: f"{a}/{b}")
|
||||
mocker.patch(f"{EXMS}.get_valid_pair_combination", side_effect=lambda a, b: [f"{a}/{b}"])
|
||||
ftbot.wallets.update()
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/balance")
|
||||
|
||||
@@ -960,7 +960,7 @@ async def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance
|
||||
default_conf["dry_run"] = False
|
||||
mocker.patch(f"{EXMS}.get_balances", return_value=rpc_balance)
|
||||
mocker.patch(f"{EXMS}.get_tickers", tickers)
|
||||
mocker.patch(f"{EXMS}.get_valid_pair_combination", side_effect=lambda a, b: f"{a}/{b}")
|
||||
mocker.patch(f"{EXMS}.get_valid_pair_combination", side_effect=lambda a, b: [f"{a}/{b}"])
|
||||
|
||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
patch_get_signal(freqtradebot)
|
||||
@@ -1049,7 +1049,7 @@ async def test_telegram_balance_handle_futures(
|
||||
mocker.patch(f"{EXMS}.get_balances", return_value=rpc_balance)
|
||||
mocker.patch(f"{EXMS}.fetch_positions", return_value=mock_pos)
|
||||
mocker.patch(f"{EXMS}.get_tickers", tickers)
|
||||
mocker.patch(f"{EXMS}.get_valid_pair_combination", side_effect=lambda a, b: f"{a}/{b}")
|
||||
mocker.patch(f"{EXMS}.get_valid_pair_combination", side_effect=lambda a, b: [f"{a}/{b}"])
|
||||
|
||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
patch_get_signal(freqtradebot)
|
||||
|
||||
Reference in New Issue
Block a user