From 01e10014bb07d53d4c19b1406cb9f3bce6508909 Mon Sep 17 00:00:00 2001 From: Stephen Dade Date: Tue, 16 Jan 2018 21:55:15 +1100 Subject: [PATCH 1/2] Order timeouts - added exception catching and rpc messaging --- freqtrade/main.py | 15 ++++++++++++++- freqtrade/tests/test_main.py | 12 +++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/freqtrade/main.py b/freqtrade/main.py index c404d6c11..796fa8515 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -129,9 +129,16 @@ def check_handle_timedout(timeoutvalue: int) -> None: timeoutthreashold = arrow.utcnow().shift(minutes=-timeoutvalue).datetime for trade in Trade.query.filter(Trade.open_order_id.isnot(None)).all(): - order = exchange.get_order(trade.open_order_id) + try: + order = exchange.get_order(trade.open_order_id) + except OperationalException: + continue ordertime = arrow.get(order['opened']) + # Check if trade is still actually open + if int(order['remaining']) == 0: + continue + if order['type'] == "LIMIT_BUY" and ordertime < timeoutthreashold: # Buy timeout - cancel order exchange.cancel_order(trade.open_order_id) @@ -140,6 +147,8 @@ def check_handle_timedout(timeoutvalue: int) -> None: Trade.session.delete(trade) Trade.session.flush() logger.info('Buy order timeout for %s.', trade) + rpc.send_msg('*Timeout:* Unfilled buy order for {} cancelled'.format( + trade.pair.replace('_', '/'))) else: # if trade is partially complete, edit the stake details for the trade # and close the order @@ -147,6 +156,8 @@ def check_handle_timedout(timeoutvalue: int) -> None: trade.stake_amount = trade.amount * trade.open_rate trade.open_order_id = None logger.info('Partial buy order timeout for %s.', trade) + rpc.send_msg('*Timeout:* Remaining buy order for {} cancelled'.format( + trade.pair.replace('_', '/'))) elif order['type'] == "LIMIT_SELL" and ordertime < timeoutthreashold: # Sell timeout - cancel order and update trade if order['remaining'] == order['amount']: @@ -157,6 +168,8 @@ def check_handle_timedout(timeoutvalue: int) -> None: trade.close_date = None trade.is_open = True trade.open_order_id = None + rpc.send_msg('*Timeout:* Unfilled sell order for {} cancelled'.format( + trade.pair.replace('_', '/'))) logger.info('Sell order timeout for %s.', trade) return True else: diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py index 97bef2257..95eb0ad82 100644 --- a/freqtrade/tests/test_main.py +++ b/freqtrade/tests/test_main.py @@ -355,7 +355,8 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, mo def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, mocker): mocker.patch.dict('freqtrade.main._CONF', default_conf) cancel_order_mock = MagicMock() - mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock()) + mocker.patch('freqtrade.rpc.init', MagicMock()) + rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock()) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), get_ticker=ticker, @@ -380,6 +381,7 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, mo # check it does cancel buy orders over the time limit check_handle_timedout(600) assert cancel_order_mock.call_count == 1 + assert rpc_mock.call_count == 1 trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all() assert len(trades) == 0 @@ -387,7 +389,8 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, mo def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old, mocker): mocker.patch.dict('freqtrade.main._CONF', default_conf) cancel_order_mock = MagicMock() - mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock()) + mocker.patch('freqtrade.rpc.init', MagicMock()) + rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock()) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), get_ticker=ticker, @@ -413,6 +416,7 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old, # check it does cancel sell orders over the time limit check_handle_timedout(600) assert cancel_order_mock.call_count == 1 + assert rpc_mock.call_count == 1 assert trade_sell.is_open is True @@ -420,7 +424,8 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old mocker): mocker.patch.dict('freqtrade.main._CONF', default_conf) cancel_order_mock = MagicMock() - mocker.patch.multiple('freqtrade.rpc', init=MagicMock(), send_msg=MagicMock()) + mocker.patch('freqtrade.rpc.init', MagicMock()) + rpc_mock = mocker.patch('freqtrade.main.rpc.send_msg', MagicMock()) mocker.patch.multiple('freqtrade.main.exchange', validate_pairs=MagicMock(), get_ticker=ticker, @@ -446,6 +451,7 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old # note this is for a partially-complete buy order check_handle_timedout(600) assert cancel_order_mock.call_count == 1 + assert rpc_mock.call_count == 1 trades = Trade.query.filter(Trade.open_order_id.is_(trade_buy.open_order_id)).all() assert len(trades) == 1 assert trades[0].amount == 23.0 From 04be438b352232b21377fe3c244fd66b0f2b7999 Mon Sep 17 00:00:00 2001 From: Stephen Dade Date: Wed, 17 Jan 2018 19:51:27 +1100 Subject: [PATCH 2/2] Better exception handling for check_handle_timedout --- freqtrade/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/main.py b/freqtrade/main.py index 796fa8515..01f3d6e16 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -131,7 +131,8 @@ def check_handle_timedout(timeoutvalue: int) -> None: for trade in Trade.query.filter(Trade.open_order_id.isnot(None)).all(): try: order = exchange.get_order(trade.open_order_id) - except OperationalException: + except requests.exceptions.RequestException: + logger.info('Cannot query order for %s due to %s', trade, traceback.format_exc()) continue ordertime = arrow.get(order['opened'])