From 45e2621505fbfe748b5507dccdb697bebd5fbe34 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 21 May 2021 19:19:38 +0200 Subject: [PATCH 1/2] Add minimum-filled protection for buy cancels --- freqtrade/freqtradebot.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index fa372e6e8..1c3a759f4 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1045,6 +1045,16 @@ class FreqtradeBot(LoggingMixin): # Cancelled orders may have the status of 'canceled' or 'closed' if order['status'] not in ('cancelled', 'canceled', 'closed'): + filled_val = order.get('filled', 0.0) or 0.0 + filled_stake = filled_val * trade.open_rate + minstake = self.exchange.get_min_pair_stake_amount( + trade.pair, trade.open_rate, self.strategy.stoploss) + + if filled_val > 0 and filled_stake < minstake: + logger.warning( + f"Order {trade.open_order_id} for {trade.pair} not cancelled, " + f"as the filled amount of {filled_val} would result in an unsellable trade.") + return False corder = self.exchange.cancel_order_with_result(trade.open_order_id, trade.pair, trade.amount) # Avoid race condition where the order could not be cancelled coz its already filled. From 4e94d3d3e5bb84425a875d298dc66dbba81ed9b4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 21 May 2021 19:32:26 +0200 Subject: [PATCH 2/2] Add test for too small buy check --- tests/test_freqtradebot.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index df0715111..4d9284a2f 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2431,13 +2431,22 @@ def test_handle_cancel_buy(mocker, caplog, default_conf, limit_buy_order) -> Non freqtrade._notify_buy_cancel = MagicMock() trade = MagicMock() - trade.pair = 'LTC/ETH' + trade.pair = 'LTC/USDT' + trade.open_rate = 200 limit_buy_order['filled'] = 0.0 limit_buy_order['status'] = 'open' reason = CANCEL_REASON['TIMEOUT'] assert freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) assert cancel_order_mock.call_count == 1 + cancel_order_mock.reset_mock() + caplog.clear() + limit_buy_order['filled'] = 0.01 + assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) + assert cancel_order_mock.call_count == 0 + assert log_has_re("Order .* for .* not cancelled, as the filled amount.* unsellable.*", caplog) + + caplog.clear() cancel_order_mock.reset_mock() limit_buy_order['filled'] = 2 assert not freqtrade.handle_cancel_buy(trade, limit_buy_order, reason) @@ -2492,7 +2501,8 @@ def test_handle_cancel_buy_corder_empty(mocker, default_conf, limit_buy_order, freqtrade._notify_buy_cancel = MagicMock() trade = MagicMock() - trade.pair = 'LTC/ETH' + trade.pair = 'LTC/USDT' + trade.open_rate = 200 limit_buy_order['filled'] = 0.0 limit_buy_order['status'] = 'open' reason = CANCEL_REASON['TIMEOUT']