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 :param order_id: The internal order id order.last_state to change to CANCELED """ 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( "post", "private/cancel-order", { "instrument_name": crypto_com_utils.convert_to_exchange_trading_pair( trading_pair), "order_id": ex_order_id }, True) return order_id except asyncio.CancelledError: raise except Exception as e: self.logger().network( f"Failed to cancel order {order_id}: {str(e)}", exc_info=True, app_warning_msg= f"Failed to cancel the order {order_id} on CryptoCom. " f"Check API key and network connection.")
async def get_order_book_data( trading_pair: str, throttler: Optional[AsyncThrottler] = None) -> Dict[str, any]: """ Retrieves the JSON order book data of the specified trading pair using the exchange's REST API. :param trading_pair: Specified trading pair. :param throttler: Optional AsyncThrottler used to throttle the API request. """ throttler = throttler or CryptoComAPIOrderBookDataSource._get_throttler_instance( ) async with aiohttp.ClientSession() as client: async with throttler.execute_task( CONSTANTS.GET_ORDER_BOOK_PATH_URL): url = crypto_com_utils.get_rest_url( CONSTANTS.GET_ORDER_BOOK_PATH_URL) params = { "depth": 150, "instrument_name": crypto_com_utils.convert_to_exchange_trading_pair( trading_pair), } orderbook_response = await client.get(url=url, params=params) if orderbook_response.status != 200: raise IOError( f"Error fetching OrderBook for {trading_pair} at {CONSTANTS.EXCHANGE_NAME}. " f"HTTP status is {orderbook_response.status}.") orderbook_data: List[Dict[str, Any]] = await safe_gather( orderbook_response.json()) orderbook_data = orderbook_data[0]["result"]["data"][0] return orderbook_data
def setUpClass(cls) -> None: super().setUpClass() cls.ev_loop = asyncio.get_event_loop() cls.base_asset = "COINALPHA" cls.quote_asset = "HBOT" cls.trading_pair = f"{cls.base_asset}-{cls.quote_asset}" cls.ex_trading_pair = crypto_com_utils.convert_to_exchange_trading_pair( cls.trading_pair)
async def cancel_all(self, timeout_seconds: float): """ Cancels all in-flight orders and waits for cancellation results. Used by bot's top level stop and exit commands (cancelling outstanding orders on exit) :param timeout_seconds: The timeout at which the operation will be canceled. :returns List of CancellationResult which indicates whether each order is successfully cancelled. """ if self._trading_pairs is None: raise Exception( "cancel_all can only be used when trading_pairs are specified." ) tracked_orders: Dict[ str, CryptoComInFlightOrder] = self._in_flight_orders.copy().items() cancellation_results = [] try: tasks = [] for _, order in tracked_orders: api_params = { "instrument_name": crypto_com_utils.convert_to_exchange_trading_pair( order.trading_pair), "order_id": order.exchange_order_id, } tasks.append( self._api_request(method="post", path_url=CONSTANTS.CANCEL_ORDER_PATH_URL, params=api_params, is_auth_required=True)) await safe_gather(*tasks) open_orders = await self.get_open_orders() for cl_order_id, tracked_order in tracked_orders: open_order = [ o for o in open_orders if o.client_order_id == cl_order_id ] if not open_order: cancellation_results.append( CancellationResult(cl_order_id, True)) self.trigger_event( MarketEvent.OrderCancelled, OrderCancelledEvent(self.current_timestamp, cl_order_id)) self.stop_tracking_order(cl_order_id) else: cancellation_results.append( CancellationResult(cl_order_id, False)) except Exception: self.logger().network( "Failed to cancel all orders.", exc_info=True, app_warning_msg= "Failed to cancel all orders on Crypto.com. Check API key and network connection." ) return cancellation_results
async def cancel_all(self, timeout_seconds: float): """ Cancels all in-flight orders and waits for cancellation results. Used by bot's top level stop and exit commands (cancelling outstanding orders on exit) :param timeout_seconds: The timeout at which the operation will be canceled. :returns List of CancellationResult which indicates whether each order is successfully cancelled. """ if self._trading_pairs is None: raise Exception( "cancel_all can only be used when trading_pairs are specified." ) cancellation_results = [] try: for trading_pair in self._trading_pairs: await self._api_request( "post", "private/cancel-all-orders", { "instrument_name": crypto_com_utils.convert_to_exchange_trading_pair( trading_pair) }, True) open_orders = await self.get_open_orders() for cl_order_id, tracked_order in self._in_flight_orders.items(): open_order = [ o for o in open_orders if o.client_order_id == cl_order_id ] if not open_order: cancellation_results.append( CancellationResult(cl_order_id, True)) self.trigger_event( MarketEvent.OrderCancelled, OrderCancelledEvent(self.current_timestamp, cl_order_id)) else: cancellation_results.append( CancellationResult(cl_order_id, False)) except Exception: self.logger().network( "Failed to cancel all orders.", exc_info=True, app_warning_msg= "Failed to cancel all orders on Crypto.com. Check API key and network connection." ) return cancellation_results
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: raise ValueError( f"Buy order amount {amount} is lower than the minimum order size " f"{trading_rule.min_order_size}.") api_params = { "instrument_name": crypto_com_utils.convert_to_exchange_trading_pair(trading_pair), "side": trade_type.name, "type": "LIMIT", "price": f"{price:f}", "quantity": f"{amount:f}", "client_oid": order_id } if order_type is OrderType.LIMIT_MAKER: api_params["exec_inst"] = "POST_ONLY" self.start_tracking_order(order_id, None, trading_pair, trade_type, price, amount, order_type) try: order_result = await self._api_request("post", "private/create-order", api_params, True) exchange_order_id = str(order_result["result"]["order_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) event_tag = MarketEvent.BuyOrderCreated if trade_type is TradeType.BUY else MarketEvent.SellOrderCreated event_class = BuyOrderCreatedEvent if trade_type is TradeType.BUY else SellOrderCreatedEvent self.trigger_event( event_tag, event_class(self.current_timestamp, order_type, trading_pair, amount, price, order_id)) except asyncio.CancelledError: raise except Exception as e: self.stop_tracking_order(order_id) self.logger().network( f"Error submitting {trade_type.name} {order_type.name} order to Crypto.com for " f"{amount} {trading_pair} " f"{price}.", exc_info=True, app_warning_msg=str(e)) self.trigger_event( MarketEvent.OrderFailure, MarketOrderFailureEvent(self.current_timestamp, order_id, order_type))