async def _update_order_status(self): """ Calls REST API to get status update for each in-flight order. """ last_tick = int(self._last_poll_timestamp / CONSTANTS.UPDATE_ORDER_STATUS_INTERVAL) current_tick = (0 if math.isnan(self.current_timestamp) else int( self.current_timestamp / CONSTANTS.UPDATE_ORDER_STATUS_INTERVAL)) if current_tick > last_tick and len(self._in_flight_orders) > 0: tracked_orders = list(self._in_flight_orders.values()) tasks = [] for tracked_order in tracked_orders: try: exchange_order_id = await tracked_order.get_exchange_order_id( ) except asyncio.TimeoutError: self.logger().network( f"Skipped order status update for {tracked_order.client_order_id} " "- waiting for exchange order id.") continue trading_pair = convert_to_exchange_trading_pair( tracked_order.trading_pair) tasks.append( self._api_request( "GET", CONSTANTS.ORDER_STATUS_PATH_URL.format( id=exchange_order_id), params={'currency_pair': trading_pair}, is_auth_required=True, limit_id=CONSTANTS.ORDER_STATUS_LIMIT_ID)) self.logger().debug( f"Polling for order status updates of {len(tasks)} orders.") responses = await safe_gather(*tasks, return_exceptions=True) for response, tracked_order in zip(responses, tracked_orders): client_order_id = tracked_order.client_order_id if isinstance(response, GateIoAPIError): if response.error_label == 'ORDER_NOT_FOUND': self._order_not_found_records[client_order_id] = \ self._order_not_found_records.get(client_order_id, 0) + 1 if self._order_not_found_records[ client_order_id] < self.ORDER_NOT_EXIST_CONFIRMATION_COUNT: # Wait until the order not found error have repeated a few times before actually treating # it as failed. See: https://github.com/CoinAlpha/hummingbot/issues/601 continue self.trigger_event( MarketEvent.OrderFailure, MarketOrderFailureEvent(self.current_timestamp, client_order_id, tracked_order.order_type)) self.stop_tracking_order(client_order_id) else: continue elif "id" not in response: self.logger().info( f"_update_order_status id not in resp: {response}") continue else: self._process_order_message(response)
async def _execute_cancel(self, trading_pair: str, order_id: str) -> str: """ Executes order cancellation process by first calling cancel-order API. The API result doesn't confirm whether the cancellation is successful, it simply states it receives the request. :param trading_pair: The market trading pair (Unused during cancel on Gate.io) :param order_id: The internal order id order.last_state to change to CANCELED """ order_was_cancelled = False try: tracked_order = self._in_flight_orders.get(order_id) if tracked_order is None: raise ValueError( f"Failed to cancel order - {order_id}. Order not found.") if tracked_order.exchange_order_id is None: await tracked_order.get_exchange_order_id() ex_order_id = tracked_order.exchange_order_id await self._api_request( "DELETE", Constants.ENDPOINT["ORDER_DELETE"].format(id=ex_order_id), params={ 'currency_pair': convert_to_exchange_trading_pair(trading_pair) }, is_auth_required=True) order_was_cancelled = True except asyncio.CancelledError: raise except (asyncio.TimeoutError, GateIoAPIError) as e: if isinstance(e, asyncio.TimeoutError): err_msg = 'Order not tracked.' err_lbl = 'ORDER_NOT_FOUND' else: err_msg = e.error_message err_lbl = e.error_label self._order_not_found_records[ order_id] = self._order_not_found_records.get(order_id, 0) + 1 if err_lbl == 'ORDER_NOT_FOUND' and \ self._order_not_found_records[order_id] >= self.ORDER_NOT_EXIST_CANCEL_COUNT: order_was_cancelled = True if order_was_cancelled: self.logger().info( f"Successfully cancelled order {order_id} on {Constants.EXCHANGE_NAME}." ) self.stop_tracking_order(order_id) self.trigger_event( MarketEvent.OrderCancelled, OrderCancelledEvent(self.current_timestamp, order_id)) tracked_order.cancelled_event.set() return CancellationResult(order_id, True) else: self.logger().network( f"Failed to cancel order {order_id}: {err_msg}", exc_info=True, app_warning_msg= f"Failed to cancel the order {order_id} on {Constants.EXCHANGE_NAME}. " f"Check API key and network connection.") return CancellationResult(order_id, False)
async def _create_order(self, trade_type: TradeType, order_id: str, trading_pair: str, amount: Decimal, order_type: OrderType, price: Decimal): """ Calls create-order API end point to place an order, starts tracking the order and triggers order created event. :param trade_type: BUY or SELL :param order_id: Internal order id (also called client_order_id) :param trading_pair: The market to place order :param amount: The order amount (in base token value) :param order_type: The order type :param price: The order price """ if not order_type.is_limit_type(): raise Exception(f"Unsupported order type: {order_type}") trading_rule = self._trading_rules[trading_pair] amount = self.quantize_order_amount(trading_pair, amount) price = self.quantize_order_price(trading_pair, price) if amount < trading_rule.min_order_size: self.logger().warning( f"{trade_type.name.title()} order amount {amount} is lower than the minimum order size " f"{trading_rule.min_order_size}.") else: order_type_str = order_type.name.lower().split("_")[0] api_params = { "text": order_id, "currency_pair": convert_to_exchange_trading_pair(trading_pair), "side": trade_type.name.lower(), "type": order_type_str, "price": f"{price:f}", "amount": f"{amount:f}", } self.start_tracking_order(order_id, None, trading_pair, trade_type, price, amount, order_type) try: order_result = await self._api_request( "POST", CONSTANTS.ORDER_CREATE_PATH_URL, api_params, True) if order_result.get('status') in { "cancelled", "expired", "failed" }: raise GateIoAPIError({ 'label': 'ORDER_REJECTED', 'message': 'Order rejected.' }) else: exchange_order_id = str(order_result["id"]) tracked_order = self._in_flight_orders.get(order_id) if tracked_order is not None: self.logger().info( f"Created {order_type.name} {trade_type.name} order {order_id} for " f"{amount} {trading_pair}.") tracked_order.update_exchange_order_id( exchange_order_id) if trade_type is TradeType.BUY: event_tag = MarketEvent.BuyOrderCreated event_cls = BuyOrderCreatedEvent else: event_tag = MarketEvent.SellOrderCreated event_cls = SellOrderCreatedEvent self.trigger_event( event_tag, event_cls(self.current_timestamp, order_type, trading_pair, amount, price, order_id, exchange_order_id)) except asyncio.CancelledError: raise except GateIoAPIError as e: error_reason = e.error_message self.stop_tracking_order(order_id) self.logger().network( f"Error submitting {trade_type.name} {order_type.name} order to {CONSTANTS.EXCHANGE_NAME} for " f"{amount} {trading_pair} {price} - {error_reason}.", exc_info=True, app_warning_msg= (f"Error submitting order to {CONSTANTS.EXCHANGE_NAME} - {error_reason}." )) self.trigger_event( MarketEvent.OrderFailure, MarketOrderFailureEvent(self.current_timestamp, order_id, order_type))
async def _update_order_status(self): """ Calls REST API to get status update for each in-flight order. """ tracked_orders: List[GateIoInFlightOrder] = list( self._in_flight_orders.values()) order_status_tasks = [] order_trade_tasks = [] for tracked_order in tracked_orders: try: exchange_order_id = await tracked_order.get_exchange_order_id() except asyncio.TimeoutError: self.logger().network( f"Skipped order status update for {tracked_order.client_order_id} " "- waiting for exchange order id.") continue trading_pair = convert_to_exchange_trading_pair( tracked_order.trading_pair) params = { "currency_pair": trading_pair, "order_id": exchange_order_id } order_trade_request = GateIORESTRequest( method=RESTMethod.GET, endpoint=CONSTANTS.MY_TRADES_PATH_URL, params=params, is_auth_required=True, throttler_limit_id=CONSTANTS.MY_TRADES_PATH_URL, ) params = {"currency_pair": trading_pair} order_status_request = GateIORESTRequest( method=RESTMethod.GET, endpoint=CONSTANTS.ORDER_STATUS_PATH_URL.format( id=exchange_order_id), params=params, is_auth_required=True, throttler_limit_id=CONSTANTS.ORDER_STATUS_LIMIT_ID, ) order_status_tasks.append( asyncio.create_task(self._api_request(order_status_request))) order_trade_tasks.append( asyncio.create_task(self._api_request(order_trade_request))) self.logger().debug( f"Polling for order updates of {len(tracked_orders)} orders.") # Process order trades first before processing order statuses trade_responses = await safe_gather(*order_trade_tasks, return_exceptions=True) for response, tracked_order in zip(trade_responses, tracked_orders): if not isinstance(response, GateIoAPIError): if len(response) > 0: for trade_fills in response: self._process_trade_message( trade_fills, tracked_order.client_order_id) else: self.logger().warning( f"Failed to fetch trade updates for order {tracked_order.client_order_id}. " f"Response: {response}") if response.error_label == 'ORDER_NOT_FOUND': self.stop_tracking_order_exceed_not_found_limit( tracked_order=tracked_order) status_responses = await safe_gather(*order_status_tasks, return_exceptions=True) for response, tracked_order in zip(status_responses, tracked_orders): if not isinstance(response, GateIoAPIError): self._process_order_message(response) else: self.logger().warning( f"Failed to fetch order status updates for order {tracked_order.client_order_id}. " f"Response: {response}") if response.error_label == 'ORDER_NOT_FOUND': self.stop_tracking_order_exceed_not_found_limit( tracked_order=tracked_order)