Merge pull request #3531 from freqtrade/exchange_errorhandling

Improve exchange errorhandling and API backoff
This commit is contained in:
hroff-1902
2020-06-30 07:53:09 +03:00
committed by GitHub
18 changed files with 306 additions and 183 deletions

View File

@@ -1,11 +1,11 @@
from unittest.mock import MagicMock
from pandas import DataFrame
import pytest
from pandas import DataFrame
from freqtrade.data.dataprovider import DataProvider
from freqtrade.exceptions import ExchangeError, OperationalException
from freqtrade.pairlist.pairlistmanager import PairListManager
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.state import RunMode
from tests.conftest import get_patched_exchange
@@ -164,7 +164,7 @@ def test_ticker(mocker, default_conf, tickers):
assert 'symbol' in res
assert res['symbol'] == 'ETH/BTC'
ticker_mock = MagicMock(side_effect=DependencyException('Pair not found'))
ticker_mock = MagicMock(side_effect=ExchangeError('Pair not found'))
mocker.patch("freqtrade.exchange.Exchange.fetch_ticker", ticker_mock)
exchange = get_patched_exchange(mocker, default_conf)
dp = DataProvider(default_conf, exchange)

View File

@@ -5,8 +5,9 @@ import ccxt
import pytest
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError)
OperationalException)
from tests.conftest import get_patched_exchange
from tests.exchange.test_exchange import ccxt_exceptionhandlers
@pytest.mark.parametrize('limitratio,expected', [
@@ -62,15 +63,9 @@ def test_stoploss_order_binance(default_conf, mocker, limitratio, expected):
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance')
exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={})
with pytest.raises(TemporaryError):
api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("No connection"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance')
exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={})
with pytest.raises(OperationalException, match=r".*DeadBeef.*"):
api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("DeadBeef"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance')
exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={})
ccxt_exceptionhandlers(mocker, default_conf, api_mock, "binance",
"stoploss", "create_order", retries=1,
pair='ETH/BTC', amount=1, stop_price=220, order_types={})
def test_stoploss_order_dry_run_binance(default_conf, mocker):

View File

@@ -4,17 +4,17 @@ import copy
import logging
from datetime import datetime, timezone
from random import randint
from unittest.mock import MagicMock, Mock, PropertyMock
from unittest.mock import MagicMock, Mock, PropertyMock, patch
import arrow
import ccxt
import pytest
from pandas import DataFrame
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
from freqtrade.exceptions import (DependencyException, InvalidOrderException, DDosProtection,
OperationalException, TemporaryError)
from freqtrade.exchange import Binance, Exchange, Kraken
from freqtrade.exchange.common import API_RETRY_COUNT
from freqtrade.exchange.common import API_RETRY_COUNT, calculate_backoff
from freqtrade.exchange.exchange import (market_is_active, symbol_is_pair,
timeframe_to_minutes,
timeframe_to_msecs,
@@ -37,12 +37,20 @@ def get_mock_coro(return_value):
def ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
fun, mock_ccxt_fun, **kwargs):
fun, mock_ccxt_fun, retries=API_RETRY_COUNT + 1, **kwargs):
with patch('freqtrade.exchange.common.time.sleep'):
with pytest.raises(DDosProtection):
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.DDoSProtection("DDos"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
getattr(exchange, fun)(**kwargs)
assert api_mock.__dict__[mock_ccxt_fun].call_count == retries
with pytest.raises(TemporaryError):
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.NetworkError("DeaDBeef"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
getattr(exchange, fun)(**kwargs)
assert api_mock.__dict__[mock_ccxt_fun].call_count == API_RETRY_COUNT + 1
assert api_mock.__dict__[mock_ccxt_fun].call_count == retries
with pytest.raises(OperationalException):
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.BaseError("DeadBeef"))
@@ -51,12 +59,21 @@ def ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
assert api_mock.__dict__[mock_ccxt_fun].call_count == 1
async def async_ccxt_exception(mocker, default_conf, api_mock, fun, mock_ccxt_fun, **kwargs):
async def async_ccxt_exception(mocker, default_conf, api_mock, fun, mock_ccxt_fun,
retries=API_RETRY_COUNT + 1, **kwargs):
with patch('freqtrade.exchange.common.asyncio.sleep', get_mock_coro(None)):
with pytest.raises(DDosProtection):
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.DDoSProtection("Dooh"))
exchange = get_patched_exchange(mocker, default_conf, api_mock)
await getattr(exchange, fun)(**kwargs)
assert api_mock.__dict__[mock_ccxt_fun].call_count == retries
with pytest.raises(TemporaryError):
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.NetworkError("DeadBeef"))
exchange = get_patched_exchange(mocker, default_conf, api_mock)
await getattr(exchange, fun)(**kwargs)
assert api_mock.__dict__[mock_ccxt_fun].call_count == API_RETRY_COUNT + 1
assert api_mock.__dict__[mock_ccxt_fun].call_count == retries
with pytest.raises(OperationalException):
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.BaseError("DeadBeef"))
@@ -1127,9 +1144,10 @@ def test_get_balance_prod(default_conf, mocker, exchange_name):
exchange.get_balance(currency='BTC')
def test_get_balances_dry_run(default_conf, mocker):
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_get_balances_dry_run(default_conf, mocker, exchange_name):
default_conf['dry_run'] = True
exchange = get_patched_exchange(mocker, default_conf)
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
assert exchange.get_balances() == {}
@@ -1847,36 +1865,48 @@ def test_cancel_stoploss_order(default_conf, mocker, exchange_name):
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_get_order(default_conf, mocker, exchange_name):
def test_fetch_order(default_conf, mocker, exchange_name):
default_conf['dry_run'] = True
order = MagicMock()
order.myid = 123
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange._dry_run_open_orders['X'] = order
assert exchange.get_order('X', 'TKN/BTC').myid == 123
assert exchange.fetch_order('X', 'TKN/BTC').myid == 123
with pytest.raises(InvalidOrderException, match=r'Tried to get an invalid dry-run-order.*'):
exchange.get_order('Y', 'TKN/BTC')
exchange.fetch_order('Y', 'TKN/BTC')
default_conf['dry_run'] = False
api_mock = MagicMock()
api_mock.fetch_order = MagicMock(return_value=456)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
assert exchange.get_order('X', 'TKN/BTC') == 456
assert exchange.fetch_order('X', 'TKN/BTC') == 456
with pytest.raises(InvalidOrderException):
api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange.get_order(order_id='_', pair='TKN/BTC')
exchange.fetch_order(order_id='_', pair='TKN/BTC')
assert api_mock.fetch_order.call_count == 1
api_mock.fetch_order = MagicMock(side_effect=ccxt.OrderNotFound("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
with patch('freqtrade.exchange.common.time.sleep') as tm:
with pytest.raises(InvalidOrderException):
exchange.fetch_order(order_id='_', pair='TKN/BTC')
# Ensure backoff is called
assert tm.call_args_list[0][0][0] == 1
assert tm.call_args_list[1][0][0] == 2
assert tm.call_args_list[2][0][0] == 5
assert tm.call_args_list[3][0][0] == 10
assert api_mock.fetch_order.call_count == API_RETRY_COUNT + 1
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
'get_order', 'fetch_order',
'fetch_order', 'fetch_order',
order_id='_', pair='TKN/BTC')
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_get_stoploss_order(default_conf, mocker, exchange_name):
def test_fetch_stoploss_order(default_conf, mocker, exchange_name):
# Don't test FTX here - that needs a seperate test
if exchange_name == 'ftx':
return
@@ -1885,25 +1915,25 @@ def test_get_stoploss_order(default_conf, mocker, exchange_name):
order.myid = 123
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
exchange._dry_run_open_orders['X'] = order
assert exchange.get_stoploss_order('X', 'TKN/BTC').myid == 123
assert exchange.fetch_stoploss_order('X', 'TKN/BTC').myid == 123
with pytest.raises(InvalidOrderException, match=r'Tried to get an invalid dry-run-order.*'):
exchange.get_stoploss_order('Y', 'TKN/BTC')
exchange.fetch_stoploss_order('Y', 'TKN/BTC')
default_conf['dry_run'] = False
api_mock = MagicMock()
api_mock.fetch_order = MagicMock(return_value=456)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
assert exchange.get_stoploss_order('X', 'TKN/BTC') == 456
assert exchange.fetch_stoploss_order('X', 'TKN/BTC') == 456
with pytest.raises(InvalidOrderException):
api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange.get_stoploss_order(order_id='_', pair='TKN/BTC')
exchange.fetch_stoploss_order(order_id='_', pair='TKN/BTC')
assert api_mock.fetch_order.call_count == 1
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
'get_stoploss_order', 'fetch_order',
'fetch_stoploss_order', 'fetch_order',
order_id='_', pair='TKN/BTC')
@@ -2111,6 +2141,13 @@ def test_get_markets(default_conf, mocker, markets,
assert sorted(pairs.keys()) == sorted(expected_keys)
def test_get_markets_error(default_conf, mocker):
ex = get_patched_exchange(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=None))
with pytest.raises(OperationalException, match="Markets were not loaded."):
ex.get_markets('LTC', 'USDT', True, False)
def test_timeframe_to_minutes():
assert timeframe_to_minutes("5m") == 5
assert timeframe_to_minutes("10m") == 10
@@ -2271,3 +2308,15 @@ def test_calculate_fee_rate(mocker, default_conf, order, expected) -> None:
ex = get_patched_exchange(mocker, default_conf)
assert ex.calculate_fee_rate(order) == expected
@pytest.mark.parametrize('retrycount,max_retries,expected', [
(0, 3, 10),
(1, 3, 5),
(2, 3, 2),
(3, 3, 1),
(0, 1, 2),
(1, 1, 1),
])
def test_calculate_backoff(retrycount, max_retries, expected):
assert calculate_backoff(retrycount, max_retries) == expected

View File

@@ -6,9 +6,9 @@ from unittest.mock import MagicMock
import ccxt
import pytest
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError)
from freqtrade.exceptions import DependencyException, InvalidOrderException
from tests.conftest import get_patched_exchange
from .test_exchange import ccxt_exceptionhandlers
STOPLOSS_ORDERTYPE = 'stop'
@@ -85,15 +85,9 @@ def test_stoploss_order_ftx(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx')
exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={})
with pytest.raises(TemporaryError):
api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("No connection"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx')
exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={})
with pytest.raises(OperationalException, match=r".*DeadBeef.*"):
api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("DeadBeef"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx')
exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={})
ccxt_exceptionhandlers(mocker, default_conf, api_mock, "ftx",
"stoploss", "create_order", retries=1,
pair='ETH/BTC', amount=1, stop_price=220, order_types={})
def test_stoploss_order_dry_run_ftx(default_conf, mocker):
@@ -130,34 +124,34 @@ def test_stoploss_adjust_ftx(mocker, default_conf):
assert not exchange.stoploss_adjust(1501, order)
def test_get_stoploss_order(default_conf, mocker):
def test_fetch_stoploss_order(default_conf, mocker):
default_conf['dry_run'] = True
order = MagicMock()
order.myid = 123
exchange = get_patched_exchange(mocker, default_conf, id='ftx')
exchange._dry_run_open_orders['X'] = order
assert exchange.get_stoploss_order('X', 'TKN/BTC').myid == 123
assert exchange.fetch_stoploss_order('X', 'TKN/BTC').myid == 123
with pytest.raises(InvalidOrderException, match=r'Tried to get an invalid dry-run-order.*'):
exchange.get_stoploss_order('Y', 'TKN/BTC')
exchange.fetch_stoploss_order('Y', 'TKN/BTC')
default_conf['dry_run'] = False
api_mock = MagicMock()
api_mock.fetch_orders = MagicMock(return_value=[{'id': 'X', 'status': '456'}])
exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx')
assert exchange.get_stoploss_order('X', 'TKN/BTC')['status'] == '456'
assert exchange.fetch_stoploss_order('X', 'TKN/BTC')['status'] == '456'
api_mock.fetch_orders = MagicMock(return_value=[{'id': 'Y', 'status': '456'}])
exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx')
with pytest.raises(InvalidOrderException, match=r"Could not get stoploss order for id X"):
exchange.get_stoploss_order('X', 'TKN/BTC')['status']
exchange.fetch_stoploss_order('X', 'TKN/BTC')['status']
with pytest.raises(InvalidOrderException):
api_mock.fetch_orders = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx')
exchange.get_stoploss_order(order_id='_', pair='TKN/BTC')
exchange.fetch_stoploss_order(order_id='_', pair='TKN/BTC')
assert api_mock.fetch_orders.call_count == 1
ccxt_exceptionhandlers(mocker, default_conf, api_mock, 'ftx',
'get_stoploss_order', 'fetch_orders',
'fetch_stoploss_order', 'fetch_orders',
order_id='_', pair='TKN/BTC')

View File

@@ -6,8 +6,7 @@ from unittest.mock import MagicMock
import ccxt
import pytest
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
OperationalException, TemporaryError)
from freqtrade.exceptions import DependencyException, InvalidOrderException
from tests.conftest import get_patched_exchange
from tests.exchange.test_exchange import ccxt_exceptionhandlers
@@ -206,15 +205,9 @@ def test_stoploss_order_kraken(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kraken')
exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={})
with pytest.raises(TemporaryError):
api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("No connection"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kraken')
exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={})
with pytest.raises(OperationalException, match=r".*DeadBeef.*"):
api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("DeadBeef"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kraken')
exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={})
ccxt_exceptionhandlers(mocker, default_conf, api_mock, "kraken",
"stoploss", "create_order", retries=1,
pair='ETH/BTC', amount=1, stop_price=220, order_types={})
def test_stoploss_order_dry_run_kraken(default_conf, mocker):

View File

@@ -8,12 +8,13 @@ import pytest
from numpy import isnan
from freqtrade.edge import PairInfo
from freqtrade.exceptions import DependencyException, TemporaryError
from freqtrade.exceptions import ExchangeError, TemporaryError
from freqtrade.persistence import Trade
from freqtrade.rpc import RPC, RPCException
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
from freqtrade.state import State
from tests.conftest import get_patched_freqtradebot, patch_get_signal, create_mock_trades
from tests.conftest import (create_mock_trades, get_patched_freqtradebot,
patch_get_signal)
# Functions for recurrent object patching
@@ -106,7 +107,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
}
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
MagicMock(side_effect=DependencyException("Pair 'ETH/BTC' not available")))
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
results = rpc._rpc_trade_status()
assert isnan(results[0]['current_profit'])
assert isnan(results[0]['current_rate'])
@@ -209,7 +210,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
assert '-0.41% (-0.06)' == result[0][3]
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
MagicMock(side_effect=DependencyException("Pair 'ETH/BTC' not available")))
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
result, headers = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
assert 'instantly' == result[0][2]
assert 'ETH/BTC' in result[0][1]
@@ -365,7 +366,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
# Test non-available pair
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
MagicMock(side_effect=DependencyException("Pair 'ETH/BTC' not available")))
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
assert stats['trade_count'] == 2
assert stats['first_trade_date'] == 'just now'
@@ -606,7 +607,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
cancel_order=cancel_order_mock,
get_order=MagicMock(
fetch_order=MagicMock(
return_value={
'status': 'closed',
'type': 'limit',
@@ -652,7 +653,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
trade = Trade.query.filter(Trade.id == '1').first()
filled_amount = trade.amount / 2
mocker.patch(
'freqtrade.exchange.Exchange.get_order',
'freqtrade.exchange.Exchange.fetch_order',
return_value={
'status': 'open',
'type': 'limit',
@@ -671,7 +672,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
amount = trade.amount
# make an limit-buy open trade, if there is no 'filled', don't sell it
mocker.patch(
'freqtrade.exchange.Exchange.get_order',
'freqtrade.exchange.Exchange.fetch_order',
return_value={
'status': 'open',
'type': 'limit',
@@ -688,7 +689,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
freqtradebot.enter_positions()
# make an limit-sell open trade
mocker.patch(
'freqtrade.exchange.Exchange.get_order',
'freqtrade.exchange.Exchange.fetch_order',
return_value={
'status': 'open',
'type': 'limit',

View File

@@ -9,13 +9,12 @@ from unittest.mock import ANY, MagicMock, PropertyMock
import arrow
import pytest
import requests
from freqtrade.constants import (CANCEL_REASON, MATH_CLOSE_PREC,
UNLIMITED_STAKE_AMOUNT)
from freqtrade.exceptions import (DependencyException, InvalidOrderException,
OperationalException, PricingError,
TemporaryError)
from freqtrade.exceptions import (DependencyException, ExchangeError,
InvalidOrderException, OperationalException,
PricingError, TemporaryError)
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.persistence import Trade
from freqtrade.rpc import RPCMessageType
@@ -763,7 +762,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_order=MagicMock(return_value=limit_buy_order),
fetch_order=MagicMock(return_value=limit_buy_order),
get_fee=fee,
)
freqtrade = FreqtradeBot(default_conf)
@@ -832,7 +831,7 @@ def test_process_trade_handling(default_conf, ticker, limit_buy_order, fee, mock
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_order=MagicMock(return_value=limit_buy_order),
fetch_order=MagicMock(return_value=limit_buy_order),
get_fee=fee,
)
freqtrade = FreqtradeBot(default_conf)
@@ -859,7 +858,7 @@ def test_process_trade_no_whitelist_pair(default_conf, ticker, limit_buy_order,
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_order=MagicMock(return_value=limit_buy_order),
fetch_order=MagicMock(return_value=limit_buy_order),
get_fee=fee,
)
freqtrade = FreqtradeBot(default_conf)
@@ -1064,7 +1063,7 @@ def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order) -> None
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order)
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
return_value=limit_buy_order['amount'])
@@ -1126,7 +1125,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
trade.stoploss_order_id = 100
hanging_stoploss_order = MagicMock(return_value={'status': 'open'})
mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', hanging_stoploss_order)
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', hanging_stoploss_order)
assert freqtrade.handle_stoploss_on_exchange(trade) is False
assert trade.stoploss_order_id == 100
@@ -1139,7 +1138,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
trade.stoploss_order_id = 100
canceled_stoploss_order = MagicMock(return_value={'status': 'canceled'})
mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', canceled_stoploss_order)
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', canceled_stoploss_order)
stoploss.reset_mock()
assert freqtrade.handle_stoploss_on_exchange(trade) is False
@@ -1164,7 +1163,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
'average': 2,
'amount': limit_buy_order['amount'],
})
mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', stoploss_order_hit)
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', stoploss_order_hit)
assert freqtrade.handle_stoploss_on_exchange(trade) is True
assert log_has('STOP_LOSS_LIMIT is hit for {}.'.format(trade), caplog)
assert trade.stoploss_order_id is None
@@ -1172,18 +1171,18 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
mocker.patch(
'freqtrade.exchange.Exchange.stoploss',
side_effect=DependencyException()
side_effect=ExchangeError()
)
trade.is_open = True
freqtrade.handle_stoploss_on_exchange(trade)
assert log_has('Unable to place a stoploss order on exchange.', caplog)
assert trade.stoploss_order_id is None
# Fifth case: get_order returns InvalidOrder
# Fifth case: fetch_order returns InvalidOrder
# It should try to add stoploss order
trade.stoploss_order_id = 100
stoploss.reset_mock()
mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order',
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order',
side_effect=InvalidOrderException())
mocker.patch('freqtrade.exchange.Exchange.stoploss', stoploss)
freqtrade.handle_stoploss_on_exchange(trade)
@@ -1194,7 +1193,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
trade.stoploss_order_id = None
trade.is_open = False
stoploss.reset_mock()
mocker.patch('freqtrade.exchange.Exchange.get_order')
mocker.patch('freqtrade.exchange.Exchange.fetch_order')
mocker.patch('freqtrade.exchange.Exchange.stoploss', stoploss)
assert freqtrade.handle_stoploss_on_exchange(trade) is False
assert stoploss.call_count == 0
@@ -1215,8 +1214,8 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
get_fee=fee,
get_stoploss_order=MagicMock(return_value={'status': 'canceled'}),
stoploss=MagicMock(side_effect=DependencyException()),
fetch_stoploss_order=MagicMock(return_value={'status': 'canceled'}),
stoploss=MagicMock(side_effect=ExchangeError()),
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
@@ -1249,7 +1248,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
sell=sell_mock,
get_fee=fee,
get_order=MagicMock(return_value={'status': 'canceled'}),
fetch_order=MagicMock(return_value={'status': 'canceled'}),
stoploss=MagicMock(side_effect=InvalidOrderException()),
)
freqtrade = FreqtradeBot(default_conf)
@@ -1332,7 +1331,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
}
})
mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', stoploss_order_hanging)
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', stoploss_order_hanging)
# stoploss initially at 5%
assert freqtrade.handle_trade(trade) is False
@@ -1432,7 +1431,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c
}
mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order',
side_effect=InvalidOrderException())
mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', stoploss_order_hanging)
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', stoploss_order_hanging)
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/BTC.*", caplog)
@@ -1442,7 +1441,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c
# Fail creating stoploss order
caplog.clear()
cancel_mock = mocker.patch("freqtrade.exchange.Exchange.cancel_stoploss_order", MagicMock())
mocker.patch("freqtrade.exchange.Exchange.stoploss", side_effect=DependencyException())
mocker.patch("freqtrade.exchange.Exchange.stoploss", side_effect=ExchangeError())
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
assert cancel_mock.call_count == 1
assert log_has_re(r"Could not create trailing stoploss order for pair ETH/BTC\..*", caplog)
@@ -1512,7 +1511,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
}
})
mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', stoploss_order_hanging)
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', stoploss_order_hanging)
# stoploss initially at 20% as edge dictated it.
assert freqtrade.handle_trade(trade) is False
@@ -1589,7 +1588,7 @@ def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order)
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
return_value=limit_buy_order['amount'])
@@ -1613,7 +1612,7 @@ def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None:
def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order)
trade = MagicMock()
trade.open_order_id = None
@@ -1634,7 +1633,7 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order)
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
return_value=limit_buy_order['amount'])
@@ -1673,8 +1672,8 @@ def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> No
def test_update_trade_state_withorderdict(default_conf, trades_for_order, limit_buy_order, fee,
mocker):
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
# get_order should not be called!!
mocker.patch('freqtrade.exchange.Exchange.get_order', MagicMock(side_effect=ValueError))
# fetch_order should not be called!!
mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError))
patch_exchange(mocker)
Trade.session = MagicMock()
amount = sum(x['amount'] for x in trades_for_order)
@@ -1698,8 +1697,8 @@ def test_update_trade_state_withorderdict_rounding_fee(default_conf, trades_for_
limit_buy_order, mocker, caplog):
trades_for_order[0]['amount'] = limit_buy_order['amount'] + 1e-14
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
# get_order should not be called!!
mocker.patch('freqtrade.exchange.Exchange.get_order', MagicMock(side_effect=ValueError))
# fetch_order should not be called!!
mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError))
patch_exchange(mocker)
Trade.session = MagicMock()
amount = sum(x['amount'] for x in trades_for_order)
@@ -1724,7 +1723,7 @@ def test_update_trade_state_withorderdict_rounding_fee(default_conf, trades_for_
def test_update_trade_state_exception(mocker, default_conf,
limit_buy_order, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order)
mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order)
trade = MagicMock()
trade.open_order_id = '123'
@@ -1741,7 +1740,7 @@ def test_update_trade_state_exception(mocker, default_conf,
def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.get_order',
mocker.patch('freqtrade.exchange.Exchange.fetch_order',
MagicMock(side_effect=InvalidOrderException))
trade = MagicMock()
@@ -1757,8 +1756,8 @@ def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None
def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_order, mocker):
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
# get_order should not be called!!
mocker.patch('freqtrade.exchange.Exchange.get_order', MagicMock(side_effect=ValueError))
# fetch_order should not be called!!
mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError))
wallet_mock = MagicMock()
mocker.patch('freqtrade.wallets.Wallets.update', wallet_mock)
@@ -1973,7 +1972,7 @@ def test_check_handle_timedout_buy_usercustom(default_conf, ticker, limit_buy_or
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
get_order=MagicMock(return_value=limit_buy_order_old),
fetch_order=MagicMock(return_value=limit_buy_order_old),
cancel_order=cancel_order_mock,
get_fee=fee
)
@@ -2022,7 +2021,7 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, op
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
get_order=MagicMock(return_value=limit_buy_order_old),
fetch_order=MagicMock(return_value=limit_buy_order_old),
cancel_order_with_result=cancel_order_mock,
get_fee=fee
)
@@ -2052,7 +2051,7 @@ def test_check_handle_cancelled_buy(default_conf, ticker, limit_buy_order_old, o
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
get_order=MagicMock(return_value=limit_buy_order_old),
fetch_order=MagicMock(return_value=limit_buy_order_old),
cancel_order=cancel_order_mock,
get_fee=fee
)
@@ -2079,7 +2078,7 @@ def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_ord
'freqtrade.exchange.Exchange',
validate_pairs=MagicMock(),
fetch_ticker=ticker,
get_order=MagicMock(side_effect=DependencyException),
fetch_order=MagicMock(side_effect=ExchangeError),
cancel_order=cancel_order_mock,
get_fee=fee
)
@@ -2105,7 +2104,7 @@ def test_check_handle_timedout_sell_usercustom(default_conf, ticker, limit_sell_
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
get_order=MagicMock(return_value=limit_sell_order_old),
fetch_order=MagicMock(return_value=limit_sell_order_old),
cancel_order=cancel_order_mock
)
freqtrade = FreqtradeBot(default_conf)
@@ -2152,7 +2151,7 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old,
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
get_order=MagicMock(return_value=limit_sell_order_old),
fetch_order=MagicMock(return_value=limit_sell_order_old),
cancel_order=cancel_order_mock
)
freqtrade = FreqtradeBot(default_conf)
@@ -2183,7 +2182,7 @@ def test_check_handle_cancelled_sell(default_conf, ticker, limit_sell_order_old,
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
get_order=MagicMock(return_value=limit_sell_order_old),
fetch_order=MagicMock(return_value=limit_sell_order_old),
cancel_order_with_result=cancel_order_mock
)
freqtrade = FreqtradeBot(default_conf)
@@ -2210,7 +2209,7 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
get_order=MagicMock(return_value=limit_buy_order_old_partial),
fetch_order=MagicMock(return_value=limit_buy_order_old_partial),
cancel_order_with_result=cancel_order_mock
)
freqtrade = FreqtradeBot(default_conf)
@@ -2238,7 +2237,7 @@ def test_check_handle_timedout_partial_fee(default_conf, ticker, open_trade, cap
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
get_order=MagicMock(return_value=limit_buy_order_old_partial),
fetch_order=MagicMock(return_value=limit_buy_order_old_partial),
cancel_order_with_result=cancel_order_mock,
get_trades_for_order=MagicMock(return_value=trades_for_order),
)
@@ -2276,7 +2275,7 @@ def test_check_handle_timedout_partial_except(default_conf, ticker, open_trade,
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
get_order=MagicMock(return_value=limit_buy_order_old_partial),
fetch_order=MagicMock(return_value=limit_buy_order_old_partial),
cancel_order_with_result=cancel_order_mock,
get_trades_for_order=MagicMock(return_value=trades_for_order),
)
@@ -2320,7 +2319,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, open_trade, mocke
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=ticker,
get_order=MagicMock(side_effect=requests.exceptions.RequestException('Oh snap')),
fetch_order=MagicMock(side_effect=ExchangeError('Oh snap')),
cancel_order=cancel_order_mock
)
freqtrade = FreqtradeBot(default_conf)
@@ -2774,7 +2773,7 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f
"fee": None,
"trades": None
})
mocker.patch('freqtrade.exchange.Exchange.get_stoploss_order', stoploss_executed)
mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', stoploss_executed)
freqtrade.exit_positions(trades)
assert trade.stoploss_order_id is None
@@ -4017,7 +4016,7 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order,
@pytest.mark.usefixtures("init_persistence")
def test_cancel_all_open_orders(mocker, default_conf, fee, limit_buy_order, limit_sell_order):
default_conf['cancel_open_orders_on_exit'] = True
mocker.patch('freqtrade.exchange.Exchange.get_order',
mocker.patch('freqtrade.exchange.Exchange.fetch_order',
side_effect=[DependencyException(), limit_sell_order, limit_buy_order])
buy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_buy')
sell_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_sell')

View File

@@ -62,7 +62,7 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee,
get_fee=fee,
amount_to_precision=lambda s, x, y: y,
price_to_precision=lambda s, x, y: y,
get_stoploss_order=stoploss_order_mock,
fetch_stoploss_order=stoploss_order_mock,
cancel_stoploss_order=cancel_order_mock,
)