Merge pull request #6396 from freqtrade/order_refind_update

Improve "order refind" to also work for stoploss orders
This commit is contained in:
Matthias 2022-02-13 12:31:37 +01:00 committed by GitHub
commit 109440a6bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 23 additions and 82 deletions

View File

@ -298,28 +298,6 @@ class FreqtradeBot(LoggingMixin):
self.update_trade_state(trade, order.order_id, send_msg=False)
def handle_insufficient_funds(self, trade: Trade):
"""
Determine if we ever opened a sell order for this trade.
If not, try update buy fees - otherwise "refind" the open order we obviously lost.
"""
sell_order = trade.select_order('sell', None)
if sell_order:
self.refind_lost_order(trade)
else:
self.reupdate_enter_order_fees(trade)
def reupdate_enter_order_fees(self, trade: Trade):
"""
Get buy order from database, and try to reupdate.
Handles trades where the initial fee-update did not work.
"""
logger.info(f"Trying to reupdate buy fees for {trade}")
order = trade.select_order('buy', False)
if order:
logger.info(f"Updating buy-fee on trade {trade} for order {order.order_id}.")
self.update_trade_state(trade, order.order_id, send_msg=False)
def refind_lost_order(self, trade):
"""
Try refinding a lost trade.
Only used when InsufficientFunds appears on sell orders (stoploss or sell).
@ -332,9 +310,6 @@ class FreqtradeBot(LoggingMixin):
if not order.ft_is_open:
logger.debug(f"Order {order} is no longer open.")
continue
if order.ft_order_side == 'buy':
# Skip buy side - this is handled by reupdate_buy_order_fees
continue
try:
fo = self.exchange.fetch_order_or_stoploss_order(order.order_id, order.ft_pair,
order.ft_order_side == 'stoploss')
@ -346,6 +321,9 @@ class FreqtradeBot(LoggingMixin):
if fo and fo['status'] == 'open':
# Assume this as the open order
trade.open_order_id = order.order_id
elif order.ft_order_side == 'buy':
if fo and fo['status'] == 'open':
trade.open_order_id = order.order_id
if fo:
logger.info(f"Found {order} for trade {trade}.")
self.update_trade_state(trade, order.order_id, fo,

View File

@ -4110,15 +4110,17 @@ def test_reupdate_enter_order_fees(mocker, default_conf_usdt, fee, caplog):
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
mock_uts = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.update_trade_state')
mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
return_value={'status': 'open'})
create_mock_trades(fee)
trades = Trade.get_trades().all()
freqtrade.reupdate_enter_order_fees(trades[0])
assert log_has_re(r"Trying to reupdate buy fees for .*", caplog)
freqtrade.handle_insufficient_funds(trades[3])
# assert log_has_re(r"Trying to reupdate buy fees for .*", caplog)
assert mock_uts.call_count == 1
assert mock_uts.call_args_list[0][0][0] == trades[0]
assert mock_uts.call_args_list[0][0][1] == mock_order_1()['id']
assert log_has_re(r"Updating buy-fee on trade .* for order .*\.", caplog)
assert mock_uts.call_args_list[0][0][0] == trades[3]
assert mock_uts.call_args_list[0][0][1] == mock_order_4()['id']
assert log_has_re(r"Trying to refind lost order for .*", caplog)
mock_uts.reset_mock()
caplog.clear()
@ -4136,52 +4138,13 @@ def test_reupdate_enter_order_fees(mocker, default_conf_usdt, fee, caplog):
)
Trade.query.session.add(trade)
freqtrade.reupdate_enter_order_fees(trade)
assert log_has_re(r"Trying to reupdate buy fees for .*", caplog)
freqtrade.handle_insufficient_funds(trade)
# assert log_has_re(r"Trying to reupdate buy fees for .*", caplog)
assert mock_uts.call_count == 0
assert not log_has_re(r"Updating buy-fee on trade .* for order .*\.", caplog)
@pytest.mark.usefixtures("init_persistence")
def test_handle_insufficient_funds(mocker, default_conf_usdt, fee):
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
mock_rlo = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.refind_lost_order')
mock_bof = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.reupdate_enter_order_fees')
create_mock_trades(fee)
trades = Trade.get_trades().all()
# Trade 0 has only a open buy order, no closed order
freqtrade.handle_insufficient_funds(trades[0])
assert mock_rlo.call_count == 0
assert mock_bof.call_count == 1
mock_rlo.reset_mock()
mock_bof.reset_mock()
# Trade 1 has closed buy and sell orders
freqtrade.handle_insufficient_funds(trades[1])
assert mock_rlo.call_count == 1
assert mock_bof.call_count == 0
mock_rlo.reset_mock()
mock_bof.reset_mock()
# Trade 2 has closed buy and sell orders
freqtrade.handle_insufficient_funds(trades[2])
assert mock_rlo.call_count == 1
assert mock_bof.call_count == 0
mock_rlo.reset_mock()
mock_bof.reset_mock()
# Trade 3 has an opne buy order
freqtrade.handle_insufficient_funds(trades[3])
assert mock_rlo.call_count == 0
assert mock_bof.call_count == 1
@pytest.mark.usefixtures("init_persistence")
def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog):
def test_handle_insufficient_funds(mocker, default_conf_usdt, fee, caplog):
caplog.set_level(logging.DEBUG)
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
mock_uts = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.update_trade_state')
@ -4204,7 +4167,7 @@ def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog):
assert trade.open_order_id is None
assert trade.stoploss_order_id is None
freqtrade.refind_lost_order(trade)
freqtrade.handle_insufficient_funds(trade)
order = mock_order_1()
assert log_has_re(r"Order Order(.*order_id=" + order['id'] + ".*) is no longer open.", caplog)
assert mock_fo.call_count == 0
@ -4222,13 +4185,13 @@ def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog):
assert trade.open_order_id is None
assert trade.stoploss_order_id is None
freqtrade.refind_lost_order(trade)
freqtrade.handle_insufficient_funds(trade)
order = mock_order_4()
assert log_has_re(r"Trying to refind Order\(.*", caplog)
assert mock_fo.call_count == 0
assert mock_uts.call_count == 0
# No change to orderid - as update_trade_state is mocked
assert trade.open_order_id is None
assert mock_fo.call_count == 1
assert mock_uts.call_count == 1
# Found open buy order
assert trade.open_order_id is not None
assert trade.stoploss_order_id is None
caplog.clear()
@ -4240,11 +4203,11 @@ def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog):
assert trade.open_order_id is None
assert trade.stoploss_order_id is None
freqtrade.refind_lost_order(trade)
freqtrade.handle_insufficient_funds(trade)
order = mock_order_5_stoploss()
assert log_has_re(r"Trying to refind Order\(.*", caplog)
assert mock_fo.call_count == 1
assert mock_uts.call_count == 1
assert mock_uts.call_count == 2
# stoploss_order_id is "refound" and added to the trade
assert trade.open_order_id is None
assert trade.stoploss_order_id is not None
@ -4259,7 +4222,7 @@ def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog):
assert trade.open_order_id is None
assert trade.stoploss_order_id is None
freqtrade.refind_lost_order(trade)
freqtrade.handle_insufficient_funds(trade)
order = mock_order_6_sell()
assert log_has_re(r"Trying to refind Order\(.*", caplog)
assert mock_fo.call_count == 1
@ -4275,7 +4238,7 @@ def test_refind_lost_order(mocker, default_conf_usdt, fee, caplog):
side_effect=ExchangeError())
order = mock_order_5_stoploss()
freqtrade.refind_lost_order(trades[4])
freqtrade.handle_insufficient_funds(trades[4])
assert log_has(f"Error updating {order['id']}.", caplog)