Merge pull request #1276 from freqtrade/fix/1272

solve /balance crashes
This commit is contained in:
Matthias 2018-11-02 16:05:42 +01:00 committed by GitHub
commit 9cadb188d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 7 deletions

View File

@ -375,6 +375,8 @@ 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:
raise DependencyException(f"Pair {pair} not available")
data = self._api.fetch_ticker(pair) data = self._api.fetch_ticker(pair)
try: try:
self._cached_ticker[pair] = { self._cached_ticker[pair] = {

View File

@ -10,10 +10,10 @@ from typing import Dict, Any, List, Optional
import arrow import arrow
import sqlalchemy as sql import sqlalchemy as sql
from numpy import mean, nan_to_num from numpy import mean, nan_to_num, NAN
from pandas import DataFrame from pandas import DataFrame
from freqtrade import TemporaryError from freqtrade import TemporaryError, DependencyException
from freqtrade.fiat_convert import CryptoToFiatConverter from freqtrade.fiat_convert import CryptoToFiatConverter
from freqtrade.misc import shorten_date from freqtrade.misc import shorten_date
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
@ -93,7 +93,10 @@ class RPC(object):
if trade.open_order_id: if trade.open_order_id:
order = self._freqtrade.exchange.get_order(trade.open_order_id, trade.pair) order = self._freqtrade.exchange.get_order(trade.open_order_id, trade.pair)
# calculate profit and send message to user # calculate profit and send message to user
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid'] try:
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
except DependencyException:
current_rate = NAN
current_profit = trade.calc_profit_percent(current_rate) current_profit = trade.calc_profit_percent(current_rate)
fmt_close_profit = (f'{round(trade.close_profit * 100, 2):.2f}%' fmt_close_profit = (f'{round(trade.close_profit * 100, 2):.2f}%'
if trade.close_profit else None) if trade.close_profit else None)
@ -122,7 +125,10 @@ class RPC(object):
trades_list = [] trades_list = []
for trade in trades: for trade in trades:
# calculate profit and send message to user # calculate profit and send message to user
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid'] try:
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
except DependencyException:
current_rate = NAN
trade_perc = (100 * trade.calc_profit_percent(current_rate)) trade_perc = (100 * trade.calc_profit_percent(current_rate))
trades_list.append([ trades_list.append([
trade.id, trade.id,
@ -207,7 +213,10 @@ class RPC(object):
profit_closed_percent.append(profit_percent) profit_closed_percent.append(profit_percent)
else: else:
# Get current rate # Get current rate
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid'] try:
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
except DependencyException:
current_rate = NAN
profit_percent = trade.calc_profit_percent(rate=current_rate) profit_percent = trade.calc_profit_percent(rate=current_rate)
profit_all_coin.append( profit_all_coin.append(
@ -275,7 +284,7 @@ class RPC(object):
rate = 1.0 / self._freqtrade.exchange.get_ticker('BTC/USDT', False)['bid'] rate = 1.0 / self._freqtrade.exchange.get_ticker('BTC/USDT', False)['bid']
else: else:
rate = self._freqtrade.exchange.get_ticker(coin + '/BTC', False)['bid'] rate = self._freqtrade.exchange.get_ticker(coin + '/BTC', False)['bid']
except TemporaryError: except (TemporaryError, DependencyException):
continue continue
est_btc: float = rate * balance['total'] est_btc: float = rate * balance['total']
total = total + est_btc total = total + est_btc

View File

@ -572,6 +572,7 @@ def test_get_ticker(default_conf, mocker):
'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': {}}
exchange = get_patched_exchange(mocker, default_conf, api_mock) exchange = get_patched_exchange(mocker, default_conf, api_mock)
# retrieve original ticker # retrieve original ticker
ticker = exchange.get_ticker(pair='ETH/BTC') ticker = exchange.get_ticker(pair='ETH/BTC')
@ -614,6 +615,9 @@ def test_get_ticker(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, api_mock) exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_ticker(pair='ETH/BTC', refresh=True) exchange.get_ticker(pair='ETH/BTC', refresh=True)
with pytest.raises(DependencyException, match=r'Pair XRP/ETH not available'):
exchange.get_ticker(pair='XRP/ETH', refresh=True)
def test_get_history(default_conf, mocker, caplog): def test_get_history(default_conf, mocker, caplog):
exchange = get_patched_exchange(mocker, default_conf) exchange = get_patched_exchange(mocker, default_conf)

View File

@ -5,8 +5,9 @@ from datetime import datetime
from unittest.mock import MagicMock, ANY from unittest.mock import MagicMock, ANY
import pytest import pytest
from numpy import isnan
from freqtrade import TemporaryError from freqtrade import TemporaryError, DependencyException
from freqtrade.fiat_convert import CryptoToFiatConverter from freqtrade.fiat_convert import CryptoToFiatConverter
from freqtrade.freqtradebot import FreqtradeBot from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
@ -61,6 +62,27 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
'open_order': '(limit buy rem=0.00000000)' 'open_order': '(limit buy rem=0.00000000)'
} == results[0] } == results[0]
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
# invalidate ticker cache
rpc._freqtrade.exchange._cached_ticker = {}
results = rpc._rpc_trade_status()
assert isnan(results[0]['current_profit'])
assert isnan(results[0]['current_rate'])
assert {
'trade_id': 1,
'pair': 'ETH/BTC',
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
'date': ANY,
'open_rate': 1.099e-05,
'close_rate': None,
'current_rate': ANY,
'amount': 90.99181074,
'close_profit': None,
'current_profit': ANY,
'open_order': '(limit buy rem=0.00000000)'
} == results[0]
def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None: def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None:
patch_coinmarketcap(mocker) patch_coinmarketcap(mocker)
@ -87,6 +109,15 @@ def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None:
assert 'ETH/BTC' in result['Pair'].all() assert 'ETH/BTC' in result['Pair'].all()
assert '-0.59%' in result['Profit'].all() assert '-0.59%' in result['Profit'].all()
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
# invalidate ticker cache
rpc._freqtrade.exchange._cached_ticker = {}
result = rpc._rpc_status_table()
assert 'just now' in result['Since'].all()
assert 'ETH/BTC' in result['Pair'].all()
assert 'nan%' in result['Profit'].all()
def test_rpc_daily_profit(default_conf, update, ticker, fee, def test_rpc_daily_profit(default_conf, update, ticker, fee,
limit_buy_order, limit_sell_order, markets, mocker) -> None: limit_buy_order, limit_sell_order, markets, mocker) -> None:
@ -208,6 +239,20 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
assert stats['best_pair'] == 'ETH/BTC' assert stats['best_pair'] == 'ETH/BTC'
assert prec_satoshi(stats['best_rate'], 6.2) assert prec_satoshi(stats['best_rate'], 6.2)
# Test non-available pair
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
# invalidate ticker cache
rpc._freqtrade.exchange._cached_ticker = {}
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
assert stats['trade_count'] == 2
assert stats['first_trade_date'] == 'just now'
assert stats['latest_trade_date'] == 'just now'
assert stats['avg_duration'] == '0:00:00'
assert stats['best_pair'] == 'ETH/BTC'
assert prec_satoshi(stats['best_rate'], 6.2)
assert isnan(stats['profit_all_coin'])
# Test that rpc_trade_statistics can handle trades that lacks # Test that rpc_trade_statistics can handle trades that lacks
# trade.open_rate (it is set to None) # trade.open_rate (it is set to None)