Merge pull request #8086 from freqtrade/feat/cancel_order
Cancel open orders through UI/telegram
This commit is contained in:
@@ -363,7 +363,7 @@ class FreqtradeBot(LoggingMixin):
|
||||
"Order is older than 5 days. Assuming order was fully cancelled.")
|
||||
fo = order.to_ccxt_object()
|
||||
fo['status'] = 'canceled'
|
||||
self.handle_timedout_order(fo, order.trade)
|
||||
self.handle_cancel_order(fo, order.trade, constants.CANCEL_REASON['TIMEOUT'])
|
||||
|
||||
except ExchangeError as e:
|
||||
|
||||
@@ -1263,11 +1263,11 @@ class FreqtradeBot(LoggingMixin):
|
||||
if not_closed:
|
||||
if fully_cancelled or (order_obj and self.strategy.ft_check_timed_out(
|
||||
trade, order_obj, datetime.now(timezone.utc))):
|
||||
self.handle_timedout_order(order, trade)
|
||||
self.handle_cancel_order(order, trade, constants.CANCEL_REASON['TIMEOUT'])
|
||||
else:
|
||||
self.replace_order(order, order_obj, trade)
|
||||
|
||||
def handle_timedout_order(self, order: Dict, trade: Trade) -> None:
|
||||
def handle_cancel_order(self, order: Dict, trade: Trade, reason: str) -> None:
|
||||
"""
|
||||
Check if current analyzed order timed out and cancel if necessary.
|
||||
:param order: Order dict grabbed with exchange.fetch_order()
|
||||
@@ -1275,10 +1275,10 @@ class FreqtradeBot(LoggingMixin):
|
||||
:return: None
|
||||
"""
|
||||
if order['side'] == trade.entry_side:
|
||||
self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['TIMEOUT'])
|
||||
self.handle_cancel_enter(trade, order, reason)
|
||||
else:
|
||||
canceled = self.handle_cancel_exit(
|
||||
trade, order, constants.CANCEL_REASON['TIMEOUT'])
|
||||
trade, order, reason)
|
||||
canceled_count = trade.get_exit_order_count()
|
||||
max_timeouts = self.config.get('unfilledtimeout', {}).get('exit_timeout_count', 0)
|
||||
if canceled and max_timeouts > 0 and canceled_count >= max_timeouts:
|
||||
|
@@ -41,7 +41,8 @@ logger = logging.getLogger(__name__)
|
||||
# 2.21: Add new_candle messagetype
|
||||
# 2.22: Add FreqAI to backtesting
|
||||
# 2.23: Allow plot config request in webserver mode
|
||||
API_VERSION = 2.23
|
||||
# 2.24: Add cancel_open_order endpoint
|
||||
API_VERSION = 2.24
|
||||
|
||||
# Public API, requires no auth.
|
||||
router_public = APIRouter()
|
||||
@@ -123,6 +124,12 @@ def trades_delete(tradeid: int, rpc: RPC = Depends(get_rpc)):
|
||||
return rpc._rpc_delete(tradeid)
|
||||
|
||||
|
||||
@router.delete('/trades/{tradeid}/open-order', response_model=OpenTradeSchema, tags=['trading'])
|
||||
def cancel_open_order(tradeid: int, rpc: RPC = Depends(get_rpc)):
|
||||
rpc._rpc_cancel_open_order(tradeid)
|
||||
return rpc._rpc_trade_status([tradeid])[0]
|
||||
|
||||
|
||||
# TODO: Missing response model
|
||||
@router.get('/edge', tags=['info'])
|
||||
def edge(rpc: RPC = Depends(get_rpc)):
|
||||
|
@@ -812,6 +812,29 @@ class RPC:
|
||||
else:
|
||||
raise RPCException(f'Failed to enter position for {pair}.')
|
||||
|
||||
def _rpc_cancel_open_order(self, trade_id: int):
|
||||
if self._freqtrade.state != State.RUNNING:
|
||||
raise RPCException('trader is not running')
|
||||
with self._freqtrade._exit_lock:
|
||||
# Query for trade
|
||||
trade = Trade.get_trades(
|
||||
trade_filter=[Trade.id == trade_id, Trade.is_open.is_(True), ]
|
||||
).first()
|
||||
if not trade:
|
||||
logger.warning('cancel_open_order: Invalid trade_id received.')
|
||||
raise RPCException('Invalid trade_id.')
|
||||
if not trade.open_order_id:
|
||||
logger.warning('cancel_open_order: No open order for trade_id.')
|
||||
raise RPCException('No open order for trade_id.')
|
||||
|
||||
try:
|
||||
order = self._freqtrade.exchange.fetch_order(trade.open_order_id, trade.pair)
|
||||
except ExchangeError as e:
|
||||
logger.info(f"Cannot query order for {trade} due to {e}.", exc_info=True)
|
||||
raise RPCException("Order not found.")
|
||||
self._freqtrade.handle_cancel_order(order, trade, CANCEL_REASON['USER_CANCEL'])
|
||||
Trade.commit()
|
||||
|
||||
def _rpc_delete(self, trade_id: int) -> Dict[str, Union[str, int]]:
|
||||
"""
|
||||
Handler for delete <id>.
|
||||
|
@@ -174,6 +174,7 @@ class Telegram(RPCHandler):
|
||||
self._force_enter, order_side=SignalDirection.SHORT)),
|
||||
CommandHandler('trades', self._trades),
|
||||
CommandHandler('delete', self._delete_trade),
|
||||
CommandHandler(['coo', 'cancel_open_order'], self._cancel_open_order),
|
||||
CommandHandler('performance', self._performance),
|
||||
CommandHandler(['buys', 'entries'], self._enter_tag_performance),
|
||||
CommandHandler(['sells', 'exits'], self._exit_reason_performance),
|
||||
@@ -1144,10 +1145,25 @@ class Telegram(RPCHandler):
|
||||
raise RPCException("Trade-id not set.")
|
||||
trade_id = int(context.args[0])
|
||||
msg = self._rpc._rpc_delete(trade_id)
|
||||
self._send_msg((
|
||||
self._send_msg(
|
||||
f"`{msg['result_msg']}`\n"
|
||||
'Please make sure to take care of this asset on the exchange manually.'
|
||||
))
|
||||
)
|
||||
|
||||
@authorized_only
|
||||
def _cancel_open_order(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /cancel_open_order <id>.
|
||||
Cancel open order for tradeid
|
||||
:param bot: telegram bot
|
||||
:param update: message update
|
||||
:return: None
|
||||
"""
|
||||
if not context.args or len(context.args) == 0:
|
||||
raise RPCException("Trade-id not set.")
|
||||
trade_id = int(context.args[0])
|
||||
self._rpc._rpc_cancel_open_order(trade_id)
|
||||
self._send_msg('Open order canceled.')
|
||||
|
||||
@authorized_only
|
||||
def _performance(self, update: Update, context: CallbackContext) -> None:
|
||||
@@ -1456,6 +1472,10 @@ class Telegram(RPCHandler):
|
||||
"*/fx <trade_id>|all:* `Alias to /forceexit`\n"
|
||||
f"{force_enter_text if self._config.get('force_entry_enable', False) else ''}"
|
||||
"*/delete <trade_id>:* `Instantly delete the given trade in the database`\n"
|
||||
"*/cancel_open_order <trade_id>:* `Cancels open orders for trade. "
|
||||
"Only valid when the trade has open orders.`\n"
|
||||
"*/coo <trade_id>|all:* `Alias to /cancel_open_order`\n"
|
||||
|
||||
"*/whitelist [sorted] [baseonly]:* `Show current whitelist. Optionally in "
|
||||
"order and/or only displaying the base currency of each pairing.`\n"
|
||||
"*/blacklist [pair]:* `Show current blacklist, or adds one or more pairs "
|
||||
|
Reference in New Issue
Block a user