From 9aac080414c5d7b96870b1aebf8f15cbe5c232f3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 20 Nov 2019 20:08:12 +0100 Subject: [PATCH 1/3] Fix 'remaining' bug when handling buy timeout --- freqtrade/freqtradebot.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 58f676aef..702e8483f 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -787,7 +787,7 @@ class FreqtradeBot: continue # Check if trade is still actually open - if float(order['remaining']) == 0.0: + if float(order.get('remaining', 0.0)) == 0.0: self.wallets.update() continue @@ -813,7 +813,8 @@ class FreqtradeBot: }) def handle_timedout_limit_buy(self, trade: Trade, order: Dict) -> bool: - """Buy timeout - cancel order + """ + Buy timeout - cancel order :return: True if order was fully cancelled """ reason = "cancelled due to timeout" @@ -824,18 +825,21 @@ class FreqtradeBot: corder = order reason = "canceled on Exchange" - if corder['remaining'] == corder['amount']: + if corder.get('remaining', order['remaining']) == order['amount']: # if trade is not partially completed, just delete the trade self.handle_buy_order_full_cancel(trade, reason) return True # if trade is partially complete, edit the stake details for the trade # and close the order - trade.amount = corder['amount'] - corder['remaining'] + # cancel_order may not contain the full order dict, so we need to fallback + # to the order dict aquired before cancelling. + # we need to fall back to the values from order if corder does not contain these keys. + trade.amount = order['amount'] - corder.get('remaining', order['remaining']) trade.stake_amount = trade.amount * trade.open_rate # verify if fees were taken from amount to avoid problems during selling try: - new_amount = self.get_real_amount(trade, corder, trade.amount) + new_amount = self.get_real_amount(trade, order, trade.amount) if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC): trade.amount = new_amount # Fee was applied, so set to 0 From a5bd4e329a9da98191e20b46e15fc496d55ec2c9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 20 Nov 2019 20:33:05 +0100 Subject: [PATCH 2/3] improve cancel_order handling --- freqtrade/freqtradebot.py | 3 ++- tests/test_freqtradebot.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 702e8483f..358c63f90 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -839,7 +839,8 @@ class FreqtradeBot: trade.stake_amount = trade.amount * trade.open_rate # verify if fees were taken from amount to avoid problems during selling try: - new_amount = self.get_real_amount(trade, order, trade.amount) + new_amount = self.get_real_amount(trade, corder if 'fee' in corder else order, + trade.amount) if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC): trade.amount = new_amount # Fee was applied, so set to 0 diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index f3baff7ce..c195ce39b 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1804,7 +1804,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, open_trade, fee, mocker) -> None: rpc_mock = patch_RPCManager(mocker) - cancel_order_mock = MagicMock() + cancel_order_mock = MagicMock(return_value=limit_buy_order_old) patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', From eac01960a79e2031630e90169e23259194275fa2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 20 Nov 2019 20:37:46 +0100 Subject: [PATCH 3/3] Add testcase for empty-order case --- tests/test_freqtradebot.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index c195ce39b..b01c8e247 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2089,6 +2089,29 @@ def test_handle_timedout_limit_buy(mocker, default_conf, limit_buy_order) -> Non assert cancel_order_mock.call_count == 1 +def test_handle_timedout_limit_buy_corder_empty(mocker, default_conf, limit_buy_order) -> None: + patch_RPCManager(mocker) + patch_exchange(mocker) + cancel_order_mock = MagicMock(return_value={}) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + cancel_order=cancel_order_mock + ) + + freqtrade = FreqtradeBot(default_conf) + + Trade.session = MagicMock() + trade = MagicMock() + limit_buy_order['remaining'] = limit_buy_order['amount'] + assert freqtrade.handle_timedout_limit_buy(trade, limit_buy_order) + assert cancel_order_mock.call_count == 1 + + cancel_order_mock.reset_mock() + limit_buy_order['amount'] = 2 + assert not freqtrade.handle_timedout_limit_buy(trade, limit_buy_order) + assert cancel_order_mock.call_count == 1 + + def test_handle_timedout_limit_sell(mocker, default_conf) -> None: patch_RPCManager(mocker) patch_exchange(mocker)