From 05b185494650c1cab2a94f08b7fb7780de2b9db6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 25 Jul 2019 19:56:59 +0200 Subject: [PATCH 1/3] Gracefully handle InvalidOrderException. --- freqtrade/freqtradebot.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index b103d73a7..d52165e0a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -524,7 +524,11 @@ class FreqtradeBot(object): if trade.open_order_id: # Update trade with order values logger.info('Found open order for %s', trade) - order = action_order or self.exchange.get_order(trade.open_order_id, trade.pair) + try: + order = action_order or self.exchange.get_order(trade.open_order_id, trade.pair) + except InvalidOrderException as exception: + logger.warning('Unable to fetch order %s: %s', trade.open_order_id, exception) + return # Try update amount (binance-fix) try: new_amount = self.get_real_amount(trade, order) @@ -749,7 +753,7 @@ class FreqtradeBot(object): if not trade.open_order_id: continue order = self.exchange.get_order(trade.open_order_id, trade.pair) - except (RequestException, DependencyException): + except (RequestException, DependencyException, InvalidOrderException): logger.info( 'Cannot query order for %s due to %s', trade, From e1b8ff798fb007ea75be49d69ce69cd2971d5d44 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 25 Jul 2019 20:05:48 +0200 Subject: [PATCH 2/3] Add test to verify that get_order was successfully cought --- freqtrade/tests/test_freqtradebot.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 9a22a4f94..1a4c5159c 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1460,6 +1460,22 @@ def test_update_trade_state_exception(mocker, default_conf, assert log_has('Could not update trade amount: ', caplog.record_tuples) +def test_update_trade_state_orderexception(mocker, default_conf, caplog) -> None: + freqtrade = get_patched_freqtradebot(mocker, default_conf) + mocker.patch('freqtrade.exchange.Exchange.get_order', + MagicMock(side_effect=InvalidOrderException)) + + trade = MagicMock() + trade.open_order_id = '123' + trade.open_fee = 0.001 + + # Test raise of OperationalException exception + grm_mock = mocker.patch("freqtrade.freqtradebot.FreqtradeBot.get_real_amount", MagicMock()) + freqtrade.update_trade_state(trade) + assert grm_mock.call_count == 0 + assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog.record_tuples) + + def test_update_trade_state_sell(default_conf, trades_for_order, limit_sell_order, mocker): mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order) # get_order should not be called!! From 4b8b2f7c5bcde38c234eca6f8d2b9f073ead3df8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 25 Jul 2019 20:06:20 +0200 Subject: [PATCH 3/3] Use raise xxx from e to have a nicer traceback --- freqtrade/exchange/exchange.py | 62 +++++++++++++++++----------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 65f013a03..a7c76e635 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -174,10 +174,10 @@ class Exchange(object): try: api = getattr(ccxt_module, name.lower())(ex_config) - except (KeyError, AttributeError): - raise OperationalException(f'Exchange {name} is not supported') + except (KeyError, AttributeError) as e: + raise OperationalException(f'Exchange {name} is not supported') from e except ccxt.BaseError as e: - raise OperationalException(f"Initialization of ccxt failed. Reason: {e}") + raise OperationalException(f"Initialization of ccxt failed. Reason: {e}") from e self.set_sandbox(api, exchange_config, name) @@ -398,17 +398,17 @@ class Exchange(object): raise DependencyException( f'Insufficient funds to create {ordertype} {side} order on market {pair}.' f'Tried to {side} amount {amount} at rate {rate} (total {rate * amount}).' - f'Message: {e}') + f'Message: {e}') from e except ccxt.InvalidOrder as e: raise DependencyException( f'Could not create {ordertype} {side} order on market {pair}.' f'Tried to {side} amount {amount} at rate {rate} (total {rate * amount}).' - f'Message: {e}') + f'Message: {e}') from e except (ccxt.NetworkError, ccxt.ExchangeError) as e: raise TemporaryError( - f'Could not place {side} order due to {e.__class__.__name__}. Message: {e}') + f'Could not place {side} order due to {e.__class__.__name__}. Message: {e}') from e except ccxt.BaseError as e: - raise OperationalException(e) + raise OperationalException(e) from e def buy(self, pair: str, ordertype: str, amount: float, rate: float, time_in_force) -> Dict: @@ -493,9 +493,9 @@ class Exchange(object): return balances except (ccxt.NetworkError, ccxt.ExchangeError) as e: raise TemporaryError( - f'Could not get balance due to {e.__class__.__name__}. Message: {e}') + f'Could not get balance due to {e.__class__.__name__}. Message: {e}') from e except ccxt.BaseError as e: - raise OperationalException(e) + raise OperationalException(e) from e @retrier def get_tickers(self) -> Dict: @@ -504,12 +504,12 @@ class Exchange(object): except ccxt.NotSupported as e: raise OperationalException( f'Exchange {self._api.name} does not support fetching tickers in batch.' - f'Message: {e}') + f'Message: {e}') from e except (ccxt.NetworkError, ccxt.ExchangeError) as e: raise TemporaryError( - f'Could not load tickers due to {e.__class__.__name__}. Message: {e}') + f'Could not load tickers due to {e.__class__.__name__}. Message: {e}') from e except ccxt.BaseError as e: - raise OperationalException(e) + raise OperationalException(e) from e @retrier def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict: @@ -528,9 +528,9 @@ class Exchange(object): return data except (ccxt.NetworkError, ccxt.ExchangeError) as e: raise TemporaryError( - f'Could not load ticker due to {e.__class__.__name__}. Message: {e}') + f'Could not load ticker due to {e.__class__.__name__}. Message: {e}') from e except ccxt.BaseError as e: - raise OperationalException(e) + raise OperationalException(e) from e else: logger.info("returning cached ticker-data for %s", pair) return self._cached_ticker[pair] @@ -651,12 +651,12 @@ class Exchange(object): except ccxt.NotSupported as e: raise OperationalException( f'Exchange {self._api.name} does not support fetching historical candlestick data.' - f'Message: {e}') + f'Message: {e}') from e except (ccxt.NetworkError, ccxt.ExchangeError) as e: - raise TemporaryError( - f'Could not load ticker history due to {e.__class__.__name__}. Message: {e}') + raise TemporaryError(f'Could not load ticker history due to {e.__class__.__name__}. ' + f'Message: {e}') from e except ccxt.BaseError as e: - raise OperationalException(f'Could not fetch ticker data. Msg: {e}') + raise OperationalException(f'Could not fetch ticker data. Msg: {e}') from e @retrier def cancel_order(self, order_id: str, pair: str) -> None: @@ -667,12 +667,12 @@ class Exchange(object): return self._api.cancel_order(order_id, pair) except ccxt.InvalidOrder as e: raise InvalidOrderException( - f'Could not cancel order. Message: {e}') + f'Could not cancel order. Message: {e}') from e except (ccxt.NetworkError, ccxt.ExchangeError) as e: raise TemporaryError( - f'Could not cancel order due to {e.__class__.__name__}. Message: {e}') + f'Could not cancel order due to {e.__class__.__name__}. Message: {e}') from e except ccxt.BaseError as e: - raise OperationalException(e) + raise OperationalException(e) from e @retrier def get_order(self, order_id: str, pair: str) -> Dict: @@ -683,12 +683,12 @@ class Exchange(object): return self._api.fetch_order(order_id, pair) except ccxt.InvalidOrder as e: raise InvalidOrderException( - f'Tried to get an invalid order (id: {order_id}). Message: {e}') + f'Tried to get an invalid order (id: {order_id}). Message: {e}') from e except (ccxt.NetworkError, ccxt.ExchangeError) as e: raise TemporaryError( - f'Could not get order due to {e.__class__.__name__}. Message: {e}') + f'Could not get order due to {e.__class__.__name__}. Message: {e}') from e except ccxt.BaseError as e: - raise OperationalException(e) + raise OperationalException(e) from e @retrier def get_order_book(self, pair: str, limit: int = 100) -> dict: @@ -704,12 +704,12 @@ class Exchange(object): except ccxt.NotSupported as e: raise OperationalException( f'Exchange {self._api.name} does not support fetching order book.' - f'Message: {e}') + f'Message: {e}') from e except (ccxt.NetworkError, ccxt.ExchangeError) as e: raise TemporaryError( - f'Could not get order book due to {e.__class__.__name__}. Message: {e}') + f'Could not get order book due to {e.__class__.__name__}. Message: {e}') from e except ccxt.BaseError as e: - raise OperationalException(e) + raise OperationalException(e) from e @retrier def get_trades_for_order(self, order_id: str, pair: str, since: datetime) -> List: @@ -726,9 +726,9 @@ class Exchange(object): except ccxt.NetworkError as e: raise TemporaryError( - f'Could not get trades due to networking error. Message: {e}') + f'Could not get trades due to networking error. Message: {e}') from e except ccxt.BaseError as e: - raise OperationalException(e) + raise OperationalException(e) from e @retrier def get_fee(self, symbol='ETH/BTC', type='', side='', amount=1, @@ -742,9 +742,9 @@ class Exchange(object): price=price, takerOrMaker=taker_or_maker)['rate'] except (ccxt.NetworkError, ccxt.ExchangeError) as e: raise TemporaryError( - f'Could not get fee info due to {e.__class__.__name__}. Message: {e}') + f'Could not get fee info due to {e.__class__.__name__}. Message: {e}') from e except ccxt.BaseError as e: - raise OperationalException(e) + raise OperationalException(e) from e def is_exchange_bad(exchange: str) -> bool: