Fix cancel order deleting trade
if one order was successfully filled, the trade cannot be deleted. closes #6907
This commit is contained in:
parent
eee337c764
commit
88845f6d88
@ -1203,15 +1203,15 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
current_order_rate=order_obj.price, entry_tag=trade.enter_tag,
|
current_order_rate=order_obj.price, entry_tag=trade.enter_tag,
|
||||||
side=trade.entry_side)
|
side=trade.entry_side)
|
||||||
|
|
||||||
full_cancel = False
|
replacing = True
|
||||||
cancel_reason = constants.CANCEL_REASON['REPLACE']
|
cancel_reason = constants.CANCEL_REASON['REPLACE']
|
||||||
if not adjusted_entry_price:
|
if not adjusted_entry_price:
|
||||||
full_cancel = True if trade.nr_of_successful_entries == 0 else False
|
replacing = False
|
||||||
cancel_reason = constants.CANCEL_REASON['USER_CANCEL']
|
cancel_reason = constants.CANCEL_REASON['USER_CANCEL']
|
||||||
if order_obj.price != adjusted_entry_price:
|
if order_obj.price != adjusted_entry_price:
|
||||||
# cancel existing order if new price is supplied or None
|
# cancel existing order if new price is supplied or None
|
||||||
self.handle_cancel_enter(trade, order, cancel_reason,
|
self.handle_cancel_enter(trade, order, cancel_reason,
|
||||||
allow_full_cancel=full_cancel)
|
replacing=replacing)
|
||||||
if adjusted_entry_price:
|
if adjusted_entry_price:
|
||||||
# place new order only if new price is supplied
|
# place new order only if new price is supplied
|
||||||
self.execute_entry(
|
self.execute_entry(
|
||||||
@ -1245,10 +1245,11 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
|
|
||||||
def handle_cancel_enter(
|
def handle_cancel_enter(
|
||||||
self, trade: Trade, order: Dict, reason: str,
|
self, trade: Trade, order: Dict, reason: str,
|
||||||
allow_full_cancel: Optional[bool] = True
|
replacing: Optional[bool] = False
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Buy cancel - cancel order
|
Buy cancel - cancel order
|
||||||
|
:param replacing: Replacing order - prevent trade deletion.
|
||||||
:return: True if order was fully cancelled
|
:return: True if order was fully cancelled
|
||||||
"""
|
"""
|
||||||
was_trade_fully_canceled = False
|
was_trade_fully_canceled = False
|
||||||
@ -1286,7 +1287,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
if isclose(filled_amount, 0.0, abs_tol=constants.MATH_CLOSE_PREC):
|
if isclose(filled_amount, 0.0, abs_tol=constants.MATH_CLOSE_PREC):
|
||||||
# if trade is not partially completed and it's the only order, just delete the trade
|
# if trade is not partially completed and it's the only order, just delete the trade
|
||||||
open_order_count = len([order for order in trade.orders if order.status == 'open'])
|
open_order_count = len([order for order in trade.orders if order.status == 'open'])
|
||||||
if open_order_count <= 1 and allow_full_cancel:
|
if open_order_count <= 1 and trade.nr_of_successful_entries == 0 and not replacing:
|
||||||
logger.info(f'{side} order fully cancelled. Removing {trade} from database.')
|
logger.info(f'{side} order fully cancelled. Removing {trade} from database.')
|
||||||
trade.delete()
|
trade.delete()
|
||||||
was_trade_fully_canceled = True
|
was_trade_fully_canceled = True
|
||||||
@ -1295,7 +1296,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
# FIXME TODO: This could possibly reworked to not duplicate the code 15 lines below.
|
# FIXME TODO: This could possibly reworked to not duplicate the code 15 lines below.
|
||||||
self.update_trade_state(trade, trade.open_order_id, corder)
|
self.update_trade_state(trade, trade.open_order_id, corder)
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
logger.info(f'Partial {side} order timeout for {trade}.')
|
logger.info(f'{side} Order timeout for {trade}.')
|
||||||
else:
|
else:
|
||||||
# if trade is partially complete, edit the stake details for the trade
|
# if trade is partially complete, edit the stake details for the trade
|
||||||
# and close the order
|
# and close the order
|
||||||
|
@ -2572,6 +2572,7 @@ def test_check_handle_cancelled_buy(
|
|||||||
get_fee=fee
|
get_fee=fee
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
|
open_trade.orders = []
|
||||||
open_trade.is_short = is_short
|
open_trade.is_short = is_short
|
||||||
Trade.query.session.add(open_trade)
|
Trade.query.session.add(open_trade)
|
||||||
|
|
||||||
@ -2954,6 +2955,7 @@ def test_handle_cancel_enter(mocker, caplog, default_conf_usdt, limit_order, is_
|
|||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
freqtrade._notify_enter_cancel = MagicMock()
|
freqtrade._notify_enter_cancel = MagicMock()
|
||||||
|
|
||||||
|
# TODO: Convert to real trade
|
||||||
trade = MagicMock()
|
trade = MagicMock()
|
||||||
trade.pair = 'LTC/USDT'
|
trade.pair = 'LTC/USDT'
|
||||||
trade.open_rate = 200
|
trade.open_rate = 200
|
||||||
@ -2961,6 +2963,7 @@ def test_handle_cancel_enter(mocker, caplog, default_conf_usdt, limit_order, is_
|
|||||||
trade.entry_side = "buy"
|
trade.entry_side = "buy"
|
||||||
l_order['filled'] = 0.0
|
l_order['filled'] = 0.0
|
||||||
l_order['status'] = 'open'
|
l_order['status'] = 'open'
|
||||||
|
trade.nr_of_successful_entries = 0
|
||||||
reason = CANCEL_REASON['TIMEOUT']
|
reason = CANCEL_REASON['TIMEOUT']
|
||||||
assert freqtrade.handle_cancel_enter(trade, l_order, reason)
|
assert freqtrade.handle_cancel_enter(trade, l_order, reason)
|
||||||
assert cancel_order_mock.call_count == 1
|
assert cancel_order_mock.call_count == 1
|
||||||
@ -3003,7 +3006,9 @@ def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf_usdt, is_sho
|
|||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
|
|
||||||
reason = CANCEL_REASON['TIMEOUT']
|
reason = CANCEL_REASON['TIMEOUT']
|
||||||
|
# TODO: Convert to real trade
|
||||||
trade = MagicMock()
|
trade = MagicMock()
|
||||||
|
trade.nr_of_successful_entries = 0
|
||||||
trade.pair = 'LTC/ETH'
|
trade.pair = 'LTC/ETH'
|
||||||
trade.entry_side = "sell" if is_short else "buy"
|
trade.entry_side = "sell" if is_short else "buy"
|
||||||
assert freqtrade.handle_cancel_enter(trade, limit_buy_order_canceled_empty, reason)
|
assert freqtrade.handle_cancel_enter(trade, limit_buy_order_canceled_empty, reason)
|
||||||
@ -3036,13 +3041,14 @@ def test_handle_cancel_enter_corder_empty(mocker, default_conf_usdt, limit_order
|
|||||||
|
|
||||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||||
freqtrade._notify_enter_cancel = MagicMock()
|
freqtrade._notify_enter_cancel = MagicMock()
|
||||||
|
# TODO: Convert to real trade
|
||||||
trade = MagicMock()
|
trade = MagicMock()
|
||||||
trade.pair = 'LTC/USDT'
|
trade.pair = 'LTC/USDT'
|
||||||
trade.entry_side = "buy"
|
trade.entry_side = "buy"
|
||||||
trade.open_rate = 200
|
trade.open_rate = 200
|
||||||
trade.entry_side = "buy"
|
trade.entry_side = "buy"
|
||||||
trade.open_order_id = "open_order_noop"
|
trade.open_order_id = "open_order_noop"
|
||||||
|
trade.nr_of_successful_entries = 0
|
||||||
l_order['filled'] = 0.0
|
l_order['filled'] = 0.0
|
||||||
l_order['status'] = 'open'
|
l_order['status'] = 'open'
|
||||||
reason = CANCEL_REASON['TIMEOUT']
|
reason = CANCEL_REASON['TIMEOUT']
|
||||||
|
Loading…
Reference in New Issue
Block a user