diff --git a/docs/developer.md b/docs/developer.md index 036109d5b..f09ae2c76 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -85,6 +85,35 @@ docker-compose exec freqtrade_develop /bin/bash ![image](https://user-images.githubusercontent.com/419355/65456522-ba671a80-de06-11e9-9598-df9ca0d8dcac.png) +## ErrorHandling + +Freqtrade Exceptions all inherit from `FreqtradeException`. +This general class of error should however not be used directly. Instead, multiple specialized sub-Exceptions exist. + +Below is an outline of exception inheritance hierarchy: + +``` ++ FreqtradeException +| ++---+ OperationalException +| ++---+ DependencyException +| | +| +---+ PricingError +| | +| +---+ ExchangeError +| | +| +---+ TemporaryError +| | +| +---+ DDosProtection +| | +| +---+ InvalidOrderException +| | +| +---+ RetryableOrderError +| ++---+ StrategyError +``` + ## Modules ### Dynamic Pairlist diff --git a/freqtrade/exceptions.py b/freqtrade/exceptions.py index c85fccc4b..e2bc969a9 100644 --- a/freqtrade/exceptions.py +++ b/freqtrade/exceptions.py @@ -29,7 +29,14 @@ class PricingError(DependencyException): """ -class InvalidOrderException(FreqtradeException): +class ExchangeError(DependencyException): + """ + Error raised out of the exchange. + Has multiple Errors to determine the appropriate error. + """ + + +class InvalidOrderException(ExchangeError): """ This is returned when the order is not valid. Example: If stoploss on exchange order is hit, then trying to cancel the order @@ -44,13 +51,6 @@ class RetryableOrderError(InvalidOrderException): """ -class ExchangeError(DependencyException): - """ - Error raised out of the exchange. - Has multiple Errors to determine the appropriate error. - """ - - class TemporaryError(ExchangeError): """ Temporary network or exchange related error. diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index df47e7b10..d32f79a3f 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -975,7 +975,7 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e - # Assign method to fetch_stoploss_order to allow easy overriding in other classes + # Assign method to cancel_stoploss_order to allow easy overriding in other classes cancel_stoploss_order = cancel_order def is_cancel_order_result_suitable(self, corder) -> bool: @@ -1041,10 +1041,10 @@ class Exchange: @retrier def fetch_l2_order_book(self, pair: str, limit: int = 100) -> dict: """ - get order book level 2 from exchange - - Notes: - 20180619: bittrex doesnt support limits -.- + Get L2 order book from exchange. + Can be limited to a certain amount (if supported). + Returns a dict in the format + {'asks': [price, volume], 'bids': [price, volume]} """ try: diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 816d24e18..2a95f58fc 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -770,7 +770,7 @@ class FreqtradeBot: logger.debug('Found no sell signal for %s.', trade) return False - def create_stoploss_order(self, trade: Trade, stop_price: float, rate: float) -> bool: + def create_stoploss_order(self, trade: Trade, stop_price: float) -> bool: """ Abstracts creating stoploss orders from the logic. Handles errors and updates the trade database object. @@ -833,14 +833,13 @@ class FreqtradeBot: stoploss = self.edge.stoploss(pair=trade.pair) if self.edge else self.strategy.stoploss stop_price = trade.open_rate * (1 + stoploss) - if self.create_stoploss_order(trade=trade, stop_price=stop_price, rate=stop_price): + if self.create_stoploss_order(trade=trade, stop_price=stop_price): trade.stoploss_last_update = datetime.now() return False # If stoploss order is canceled for some reason we add it if stoploss_order and stoploss_order['status'] in ('canceled', 'cancelled'): - if self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss, - rate=trade.stop_loss): + if self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss): return False else: trade.stoploss_order_id = None @@ -877,8 +876,7 @@ class FreqtradeBot: f"for pair {trade.pair}") # Create new stoploss order - if not self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss, - rate=trade.stop_loss): + if not self.create_stoploss_order(trade=trade, stop_price=trade.stop_loss): logger.warning(f"Could not create trailing stoploss order " f"for pair {trade.pair}.") @@ -923,7 +921,7 @@ class FreqtradeBot: if not trade.open_order_id: continue order = self.exchange.fetch_order(trade.open_order_id, trade.pair) - except (ExchangeError, InvalidOrderException): + except (ExchangeError): logger.info('Cannot query order for %s due to %s', trade, traceback.format_exc()) continue @@ -956,7 +954,7 @@ class FreqtradeBot: for trade in Trade.get_open_order_trades(): try: order = self.exchange.fetch_order(trade.open_order_id, trade.pair) - except (DependencyException, InvalidOrderException): + except (ExchangeError): logger.info('Cannot query order for %s due to %s', trade, traceback.format_exc()) continue diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 8a1ff7e96..f4e20c16f 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -11,7 +11,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union import arrow from numpy import NAN, mean -from freqtrade.exceptions import (ExchangeError, InvalidOrderException, +from freqtrade.exceptions import (ExchangeError, PricingError) from freqtrade.exchange import timeframe_to_minutes, timeframe_to_msecs from freqtrade.misc import shorten_date @@ -555,7 +555,7 @@ class RPC: try: self._freqtrade.exchange.cancel_order(trade.open_order_id, trade.pair) c_count += 1 - except (ExchangeError, InvalidOrderException): + except (ExchangeError): pass # cancel stoploss on exchange ... @@ -565,7 +565,7 @@ class RPC: self._freqtrade.exchange.cancel_stoploss_order(trade.stoploss_order_id, trade.pair) c_count += 1 - except (ExchangeError, InvalidOrderException): + except (ExchangeError): pass Trade.session.delete(trade) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index b8b4a0f7a..3f42aa889 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1301,7 +1301,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, freqtrade.enter_positions() trade = Trade.query.first() caplog.clear() - freqtrade.create_stoploss_order(trade, 200, 199) + freqtrade.create_stoploss_order(trade, 200) assert trade.stoploss_order_id is None assert trade.sell_reason == SellType.EMERGENCY_SELL.value assert log_has("Unable to place a stoploss order on exchange. ", caplog) @@ -4107,7 +4107,7 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order, def test_cancel_all_open_orders(mocker, default_conf, fee, limit_buy_order, limit_sell_order): default_conf['cancel_open_orders_on_exit'] = True mocker.patch('freqtrade.exchange.Exchange.fetch_order', - side_effect=[DependencyException(), limit_sell_order, limit_buy_order]) + side_effect=[ExchangeError(), limit_sell_order, limit_buy_order]) buy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_buy') sell_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_sell')