Merge pull request #3181 from freqtrade/fix/cancel_problems

Fix several cancel order problems
This commit is contained in:
hroff-1902
2020-04-18 11:16:59 +03:00
committed by GitHub
4 changed files with 94 additions and 23 deletions

View File

@@ -922,9 +922,9 @@ class Exchange:
return order.get('status') in ('closed', 'canceled') and order.get('filled') == 0.0
@retrier
def cancel_order(self, order_id: str, pair: str) -> None:
def cancel_order(self, order_id: str, pair: str) -> Dict:
if self._config['dry_run']:
return
return {}
try:
return self._api.cancel_order(order_id, pair)
@@ -937,6 +937,37 @@ class Exchange:
except ccxt.BaseError as e:
raise OperationalException(e) from e
def is_cancel_order_result_suitable(self, corder) -> bool:
if not isinstance(corder, dict):
return False
required = ('fee', 'status', 'amount')
return all(k in corder for k in required)
def cancel_order_with_result(self, order_id: str, pair: str, amount: float) -> Dict:
"""
Cancel order returning a result.
Creates a fake result if cancel order returns a non-usable result
and get_order does not work (certain exchanges don't return cancelled orders)
:param order_id: Orderid to cancel
:param pair: Pair corresponding to order_id
:param amount: Amount to use for fake response
:return: Result from either cancel_order if usable, or fetch_order
"""
try:
corder = self.cancel_order(order_id, pair)
if self.is_cancel_order_result_suitable(corder):
return corder
except InvalidOrderException:
logger.warning(f"Could not cancel order {order_id}.")
try:
order = self.get_order(order_id, pair)
except InvalidOrderException:
logger.warning(f"Could not fetch cancelled order {order_id}.")
order = {'fee': {}, 'status': 'canceled', 'amount': amount, 'info': {}}
return order
@retrier
def get_order(self, order_id: str, pair: str) -> Dict:
if self._config['dry_run']:

View File

@@ -890,21 +890,14 @@ class FreqtradeBot:
"""
if order['status'] != 'canceled':
reason = "cancelled due to timeout"
try:
corder = self.exchange.cancel_order(trade.open_order_id, trade.pair)
# Some exchanges don't return a dict here.
if not isinstance(corder, dict):
corder = {}
logger.info('Buy order %s for %s.', reason, trade)
except InvalidOrderException:
corder = {}
logger.exception(
f"Could not cancel buy order {trade.open_order_id} for pair {trade.pair}")
corder = self.exchange.cancel_order_with_result(trade.open_order_id, trade.pair,
trade.amount)
else:
# Order was cancelled already, so we can reuse the existing dict
corder = order
reason = "cancelled on exchange"
logger.info('Buy order %s for %s.', reason, trade)
logger.info('Buy order %s for %s.', reason, trade)
if safe_value_fallback(corder, order, 'remaining', 'remaining') == order['amount']:
logger.info('Buy order fully cancelled. Removing %s from database.', trade)
@@ -921,7 +914,7 @@ class FreqtradeBot:
trade.amount = order['amount'] - safe_value_fallback(corder, order,
'remaining', 'remaining')
trade.stake_amount = trade.amount * trade.open_rate
self.update_trade_state(trade, corder if 'fee' in corder else order, trade.amount)
self.update_trade_state(trade, corder, trade.amount)
trade.open_order_id = None
logger.info('Partial buy order timeout for %s.', trade)
@@ -1140,7 +1133,7 @@ class FreqtradeBot:
new_amount = self.get_real_amount(trade, order, order_amount)
if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC):
order['amount'] = new_amount
del order['filled']
order.pop('filled', None)
# Fee was applied, so set to 0
trade.fee_open = 0
trade.recalc_open_trade_price()