Merge pull request #1998 from freqtrade/fix/pax_balance
Support all types of pairs for /balance
This commit is contained in:
commit
61b24180f0
@ -270,6 +270,15 @@ class Exchange(object):
|
|||||||
f'Pair {pair} is not available on {self.name}. '
|
f'Pair {pair} is not available on {self.name}. '
|
||||||
f'Please remove {pair} from your whitelist.')
|
f'Please remove {pair} from your whitelist.')
|
||||||
|
|
||||||
|
def get_valid_pair_combination(self, curr_1, curr_2) -> str:
|
||||||
|
"""
|
||||||
|
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}"]:
|
||||||
|
if pair in self.markets and self.markets[pair].get('active'):
|
||||||
|
return pair
|
||||||
|
raise DependencyException(f"Could not combine {curr_1} and {curr_2} to get a valid pair.")
|
||||||
|
|
||||||
def validate_timeframes(self, timeframe: List[str]) -> None:
|
def validate_timeframes(self, timeframe: List[str]) -> None:
|
||||||
"""
|
"""
|
||||||
Checks if ticker interval from config is a supported timeframe on the exchange
|
Checks if ticker interval from config is a supported timeframe on the exchange
|
||||||
@ -504,7 +513,7 @@ class Exchange(object):
|
|||||||
def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict:
|
def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict:
|
||||||
if refresh or pair not in self._cached_ticker.keys():
|
if refresh or pair not in self._cached_ticker.keys():
|
||||||
try:
|
try:
|
||||||
if pair not in self._api.markets:
|
if pair not in self._api.markets or not self._api.markets[pair].get('active'):
|
||||||
raise DependencyException(f"Pair {pair} not available")
|
raise DependencyException(f"Pair {pair} not available")
|
||||||
data = self._api.fetch_ticker(pair)
|
data = self._api.fetch_ticker(pair)
|
||||||
try:
|
try:
|
||||||
|
@ -281,10 +281,11 @@ class RPC(object):
|
|||||||
rate = 1.0
|
rate = 1.0
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
if coin in('USDT', 'USD', 'EUR'):
|
pair = self._freqtrade.exchange.get_valid_pair_combination(coin, "BTC")
|
||||||
rate = 1.0 / self._freqtrade.get_sell_rate('BTC/' + coin, False)
|
if pair.startswith("BTC"):
|
||||||
|
rate = 1.0 / self._freqtrade.get_sell_rate(pair, False)
|
||||||
else:
|
else:
|
||||||
rate = self._freqtrade.get_sell_rate(coin + '/BTC', False)
|
rate = self._freqtrade.get_sell_rate(pair, False)
|
||||||
except (TemporaryError, DependencyException):
|
except (TemporaryError, DependencyException):
|
||||||
logger.warning(f" Could not get rate for pair {coin}.")
|
logger.warning(f" Could not get rate for pair {coin}.")
|
||||||
continue
|
continue
|
||||||
|
@ -932,7 +932,7 @@ def test_get_ticker(default_conf, mocker, exchange_name):
|
|||||||
'last': 0.0001,
|
'last': 0.0001,
|
||||||
}
|
}
|
||||||
api_mock.fetch_ticker = MagicMock(return_value=tick)
|
api_mock.fetch_ticker = MagicMock(return_value=tick)
|
||||||
api_mock.markets = {'ETH/BTC': {}}
|
api_mock.markets = {'ETH/BTC': {'active': True}}
|
||||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||||
# retrieve original ticker
|
# retrieve original ticker
|
||||||
ticker = exchange.get_ticker(pair='ETH/BTC')
|
ticker = exchange.get_ticker(pair='ETH/BTC')
|
||||||
@ -1477,10 +1477,11 @@ def test_stoploss_limit_order_dry_run(default_conf, mocker):
|
|||||||
|
|
||||||
|
|
||||||
def test_merge_ft_has_dict(default_conf, mocker):
|
def test_merge_ft_has_dict(default_conf, mocker):
|
||||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=MagicMock()))
|
mocker.patch.multiple('freqtrade.exchange.Exchange',
|
||||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
_init_ccxt=MagicMock(return_value=MagicMock()),
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
_load_async_markets=MagicMock(),
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
validate_pairs=MagicMock(),
|
||||||
|
validate_timeframes=MagicMock())
|
||||||
ex = Exchange(default_conf)
|
ex = Exchange(default_conf)
|
||||||
assert ex._ft_has == Exchange._ft_has_default
|
assert ex._ft_has == Exchange._ft_has_default
|
||||||
|
|
||||||
@ -1501,3 +1502,18 @@ def test_merge_ft_has_dict(default_conf, mocker):
|
|||||||
assert ex._ft_has != Exchange._ft_has_default
|
assert ex._ft_has != Exchange._ft_has_default
|
||||||
assert not ex._ft_has['stoploss_on_exchange']
|
assert not ex._ft_has['stoploss_on_exchange']
|
||||||
assert ex._ft_has['DeadBeef'] == 20
|
assert ex._ft_has['DeadBeef'] == 20
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_valid_pair_combination(default_conf, mocker, markets):
|
||||||
|
mocker.patch.multiple('freqtrade.exchange.Exchange',
|
||||||
|
_init_ccxt=MagicMock(return_value=MagicMock()),
|
||||||
|
_load_async_markets=MagicMock(),
|
||||||
|
validate_pairs=MagicMock(),
|
||||||
|
validate_timeframes=MagicMock(),
|
||||||
|
markets=PropertyMock(return_value=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"
|
||||||
|
with pytest.raises(DependencyException, match=r"Could not combine.* to get a valid pair."):
|
||||||
|
ex.get_valid_pair_combination("NOPAIR", "ETH")
|
||||||
|
@ -324,7 +324,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, markets,
|
|||||||
assert prec_satoshi(stats['best_rate'], 6.2)
|
assert prec_satoshi(stats['best_rate'], 6.2)
|
||||||
|
|
||||||
|
|
||||||
def test_rpc_balance_handle(default_conf, mocker):
|
def test_rpc_balance_handle_error(default_conf, mocker):
|
||||||
mock_balance = {
|
mock_balance = {
|
||||||
'BTC': {
|
'BTC': {
|
||||||
'free': 10.0,
|
'free': 10.0,
|
||||||
@ -371,6 +371,72 @@ def test_rpc_balance_handle(default_conf, mocker):
|
|||||||
assert result['total'] == 12.0
|
assert result['total'] == 12.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_rpc_balance_handle(default_conf, mocker):
|
||||||
|
mock_balance = {
|
||||||
|
'BTC': {
|
||||||
|
'free': 10.0,
|
||||||
|
'total': 12.0,
|
||||||
|
'used': 2.0,
|
||||||
|
},
|
||||||
|
'ETH': {
|
||||||
|
'free': 1.0,
|
||||||
|
'total': 5.0,
|
||||||
|
'used': 4.0,
|
||||||
|
},
|
||||||
|
'PAX': {
|
||||||
|
'free': 5.0,
|
||||||
|
'total': 10.0,
|
||||||
|
'used': 5.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.rpc.fiat_convert.Market',
|
||||||
|
ticker=MagicMock(return_value={'price_usd': 15000.0}),
|
||||||
|
)
|
||||||
|
patch_exchange(mocker)
|
||||||
|
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||||
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
get_balances=MagicMock(return_value=mock_balance),
|
||||||
|
get_ticker=MagicMock(
|
||||||
|
side_effect=lambda p, r: {'bid': 100} if p == "BTC/PAX" else {'bid': 0.01}),
|
||||||
|
get_valid_pair_combination=MagicMock(
|
||||||
|
side_effect=lambda a, b: f"{b}/{a}" if a == "PAX" else f"{a}/{b}")
|
||||||
|
)
|
||||||
|
|
||||||
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
|
rpc = RPC(freqtradebot)
|
||||||
|
rpc._fiat_converter = CryptoToFiatConverter()
|
||||||
|
|
||||||
|
result = rpc._rpc_balance(default_conf['fiat_display_currency'])
|
||||||
|
assert prec_satoshi(result['total'], 12.15)
|
||||||
|
assert prec_satoshi(result['value'], 182250)
|
||||||
|
assert 'USD' == result['symbol']
|
||||||
|
assert result['currencies'] == [
|
||||||
|
{'currency': 'BTC',
|
||||||
|
'available': 10.0,
|
||||||
|
'balance': 12.0,
|
||||||
|
'pending': 2.0,
|
||||||
|
'est_btc': 12.0,
|
||||||
|
},
|
||||||
|
{'available': 1.0,
|
||||||
|
'balance': 5.0,
|
||||||
|
'currency': 'ETH',
|
||||||
|
'est_btc': 0.05,
|
||||||
|
'pending': 4.0
|
||||||
|
},
|
||||||
|
{'available': 5.0,
|
||||||
|
'balance': 10.0,
|
||||||
|
'currency': 'PAX',
|
||||||
|
'est_btc': 0.1,
|
||||||
|
'pending': 5.0}
|
||||||
|
]
|
||||||
|
assert result['total'] == 12.15
|
||||||
|
|
||||||
|
|
||||||
def test_rpc_start(mocker, default_conf) -> None:
|
def test_rpc_start(mocker, default_conf) -> None:
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
|
@ -244,6 +244,8 @@ def test_api_balance(botclient, mocker, rpc_balance):
|
|||||||
}
|
}
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance)
|
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker', side_effect=mock_ticker)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker', side_effect=mock_ticker)
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination',
|
||||||
|
side_effect=lambda a, b: f"{a}/{b}")
|
||||||
|
|
||||||
rc = client_get(client, f"{BASE_URI}/balance")
|
rc = client_get(client, f"{BASE_URI}/balance")
|
||||||
assert_response(rc)
|
assert_response(rc)
|
||||||
|
@ -518,6 +518,8 @@ def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance) -> N
|
|||||||
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance)
|
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker', side_effect=mock_ticker)
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker', side_effect=mock_ticker)
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination',
|
||||||
|
side_effect=lambda a, b: f"{a}/{b}")
|
||||||
|
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
|
Loading…
Reference in New Issue
Block a user