diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json
index 83b8a27d0..228a08a02 100644
--- a/config_examples/config_full.example.json
+++ b/config_examples/config_full.example.json
@@ -28,6 +28,7 @@
"unfilledtimeout": {
"buy": 10,
"sell": 30,
+ "exit_timeout_count": 0,
"unit": "minutes"
},
"bid_strategy": {
diff --git a/docs/configuration.md b/docs/configuration.md
index 24198b44c..c566e33c2 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -101,6 +101,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `fee` | Fee used during backtesting / dry-runs. Should normally not be configured, which has freqtrade fall back to the exchange default fee. Set as ratio (e.g. 0.001 = 0.1%). Fee is applied twice for each trade, once when buying, once when selling.
**Datatype:** Float (as ratio)
| `unfilledtimeout.buy` | **Required.** How long (in minutes or seconds) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled and repeated at current (new) price, as long as there is a signal. [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Integer
| `unfilledtimeout.sell` | **Required.** How long (in minutes or seconds) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled and repeated at current (new) price, as long as there is a signal. [Strategy Override](#parameters-in-the-strategy).
**Datatype:** Integer
+| `unfilledtimeout.exit_timeout_count` | How many times can exit orders time out. 0 to disable and allow unlimited cancels. [Strategy Override](#parameters-in-the-strategy).
*Defaults to `0`.*
**Datatype:** Integer
| `unfilledtimeout.unit` | Unit to use in unfilledtimeout setting. Note: If you set unfilledtimeout.unit to "seconds", "internals.process_throttle_secs" must be inferior or equal to timeout [Strategy Override](#parameters-in-the-strategy).
*Defaults to `minutes`.*
**Datatype:** String
| `bid_strategy.price_side` | Select the side of the spread the bot should look at to get the buy rate. [More information below](#buy-price-side).
*Defaults to `bid`.*
**Datatype:** String (either `ask` or `bid`).
| `bid_strategy.ask_last_balance` | **Required.** Interpolate the bidding price. More information [below](#buy-price-without-orderbook-enabled).
diff --git a/freqtrade/constants.py b/freqtrade/constants.py
index 656893999..73830af09 100644
--- a/freqtrade/constants.py
+++ b/freqtrade/constants.py
@@ -156,6 +156,7 @@ CONF_SCHEMA = {
'properties': {
'buy': {'type': 'number', 'minimum': 1},
'sell': {'type': 'number', 'minimum': 1},
+ 'exit_timeout_count': {'type': 'number', 'minimum': 0, 'default': 0},
'unit': {'type': 'string', 'enum': TIMEOUT_UNITS, 'default': 'minutes'}
}
},
diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py
index a1e8cad4a..a797b2e70 100644
--- a/freqtrade/freqtradebot.py
+++ b/freqtrade/freqtradebot.py
@@ -920,6 +920,13 @@ class FreqtradeBot(LoggingMixin):
trade=trade,
order=order))):
self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['TIMEOUT'])
+ canceled_count = trade.get_exit_order_count()
+ max_timeouts = self.config.get('unfilledtimeout', {}).get('exit_timeout_count', 0)
+ if max_timeouts > 0 and canceled_count >= max_timeouts:
+ logger.warning(f'Emergencyselling trade {trade}, as the sell order '
+ f'timed out {max_timeouts} times.')
+ self.execute_trade_exit(trade, order.get('price'), sell_reason=SellCheckTuple(
+ sell_type=SellType.EMERGENCY_SELL))
def cancel_all_open_orders(self) -> None:
"""
@@ -1283,7 +1290,7 @@ class FreqtradeBot(LoggingMixin):
if self.exchange.check_order_canceled_empty(order):
# Trade has been cancelled on exchange
- # Handling of this will happen in check_handle_timeout.
+ # Handling of this will happen in check_handle_timedout.
return True
# Try update amount (binance-fix)
diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py
index b3518c228..bef04fd76 100644
--- a/freqtrade/persistence/models.py
+++ b/freqtrade/persistence/models.py
@@ -491,6 +491,14 @@ class LocalTrade():
def update_order(self, order: Dict) -> None:
Order.update_orders(self.orders, order)
+ def get_exit_order_count(self) -> int:
+ """
+ Get amount of failed exiting orders
+ assumes full exits.
+ """
+ orders = [o for o in self.orders if o.ft_order_side == 'sell']
+ return len(orders)
+
def _calc_open_trade_value(self) -> float:
"""
Calculate the open_rate including open_fee.
@@ -775,7 +783,7 @@ class Trade(_DECL_BASE, LocalTrade):
return Trade.query
@staticmethod
- def get_open_order_trades():
+ def get_open_order_trades() -> List['Trade']:
"""
Returns all open trades
NOTE: Not supported in Backtesting.