Skip to content

Commit

Permalink
Don't emergencysell partial sold exit
Browse files Browse the repository at this point in the history
  • Loading branch information
xmatthias committed Feb 25, 2022
1 parent 3c88b4c commit 5826698
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 13 deletions.
15 changes: 9 additions & 6 deletions freqtrade/freqtradebot.py
Original file line number Diff line number Diff line change
Expand Up @@ -979,10 +979,10 @@ def check_handle_timedout(self) -> None:
or (order_obj and self.strategy.ft_check_timed_out(
'sell', trade, order_obj, datetime.now(timezone.utc))
))):
self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['TIMEOUT'])
canceled = self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['TIMEOUT'])
canceled_count = trade.get_exit_order_count()
max_timeouts = self.config.get('unfilledtimeout', {}).get('exit_timeout_count', 0)
if max_timeouts > 0 and canceled_count >= max_timeouts:
if canceled and max_timeouts > 0 and canceled_count >= max_timeouts:
logger.warning(f'Emergencyselling trade {trade}, as the sell order '
f'timed out {max_timeouts} times.')
try:
Expand Down Expand Up @@ -1079,11 +1079,12 @@ def handle_cancel_enter(self, trade: Trade, order: Dict, reason: str) -> bool:
reason=reason)
return was_trade_fully_canceled

def handle_cancel_exit(self, trade: Trade, order: Dict, reason: str) -> str:
def handle_cancel_exit(self, trade: Trade, order: Dict, reason: str) -> bool:
"""
Sell cancel - cancel order and update trade
:return: Reason for cancel
:return: True if exit order was cancelled, false otherwise
"""
cancelled = False
# if trade is not partially completed, just cancel the order
if order['remaining'] == order['amount'] or order.get('filled') == 0.0:
if not self.exchange.check_order_canceled_empty(order):
Expand All @@ -1094,7 +1095,7 @@ def handle_cancel_exit(self, trade: Trade, order: Dict, reason: str) -> str:
trade.update_order(co)
except InvalidOrderException:
logger.exception(f"Could not cancel sell order {trade.open_order_id}")
return 'error cancelling order'
return False
logger.info('Sell order %s for %s.', reason, trade)
else:
reason = constants.CANCEL_REASON['CANCELLED_ON_EXCHANGE']
Expand All @@ -1108,17 +1109,19 @@ def handle_cancel_exit(self, trade: Trade, order: Dict, reason: str) -> str:
trade.close_date = None
trade.is_open = True
trade.open_order_id = None
cancelled = True
else:
# TODO: figure out how to handle partially complete sell orders
reason = constants.CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN']
cancelled = False

self.wallets.update()
self._notify_exit_cancel(
trade,
order_type=self.strategy.order_types['sell'],
reason=reason
)
return reason
return cancelled

def _safe_exit_amount(self, pair: str, amount: float) -> float:
"""
Expand Down
23 changes: 16 additions & 7 deletions tests/test_freqtradebot.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from copy import deepcopy
from math import isclose
from typing import List
from unittest.mock import ANY, MagicMock, PropertyMock
from unittest.mock import ANY, MagicMock, PropertyMock, patch

import arrow
import pytest
Expand Down Expand Up @@ -2220,9 +2220,14 @@ def test_check_handle_timedout_sell_usercustom(default_conf_usdt, ticker_usdt, l

et_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.execute_trade_exit')
caplog.clear()

# 2nd canceled trade ...
open_trade.open_order_id = limit_sell_order_old['id']

# If cancelling fails - no emergency sell!
with patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_exit', return_value=False):
freqtrade.check_handle_timedout()
assert et_mock.call_count == 0

freqtrade.check_handle_timedout()
assert log_has_re('Emergencyselling trade.*', caplog)
assert et_mock.call_count == 1
Expand Down Expand Up @@ -2564,13 +2569,17 @@ def test_handle_cancel_exit_limit(mocker, default_conf_usdt, fee) -> None:
send_msg_mock.reset_mock()

order['amount'] = 2
assert freqtrade.handle_cancel_exit(trade, order, reason
) == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN']
assert not freqtrade.handle_cancel_exit(trade, order, reason)
# Assert cancel_order was not called (callcount remains unchanged)
assert cancel_order_mock.call_count == 1
assert send_msg_mock.call_count == 1
assert freqtrade.handle_cancel_exit(trade, order, reason
) == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN']
assert (send_msg_mock.call_args_list[0][0][0]['reason']
== CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN'])

assert not freqtrade.handle_cancel_exit(trade, order, reason)

send_msg_mock.call_args_list[0][0][0]['reason'] = CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN']

# Message should not be iterated again
assert trade.sell_order_status == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN']
assert send_msg_mock.call_count == 1
Expand All @@ -2589,7 +2598,7 @@ def test_handle_cancel_exit_cancel_exception(mocker, default_conf_usdt) -> None:
order = {'remaining': 1,
'amount': 1,
'status': "open"}
assert freqtrade.handle_cancel_exit(trade, order, reason) == 'error cancelling order'
assert not freqtrade.handle_cancel_exit(trade, order, reason)


def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, mocker
Expand Down

0 comments on commit 5826698

Please sign in to comment.