def test_get_ms_timestamp(self, time_mock): time_mock.return_value = 1234567891.23456 timestamp = utils.get_ms_timestamp() self.assertEqual(1234567891234, timestamp) # If requested two times at the same moment it should return the same value (writen this way to ensure # we are not using the nonce any more timestamp = utils.get_ms_timestamp() self.assertEqual(1234567891234, timestamp)
def test_get_ms_timestamp(self): with mock.patch( 'hummingbot.connector.exchange.ascend_ex.ascend_ex_utils.get_tracking_nonce_low_res' ) as get_tracking_nonce_low_res_mock: get_tracking_nonce_low_res_mock.return_value = 123456789 timestamp = utils.get_ms_timestamp() self.assertEqual(123456789, timestamp)
def get_auth_headers(self, path_url: str, data: Dict[str, Any] = None): """ Generates authentication signature and return it in a dictionary along with other inputs :return: a dictionary of request info including the request signature """ timestamp = str(get_ms_timestamp()) message = timestamp + path_url signature = hmac.new(self.secret_key.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest() return { "x-auth-key": self.api_key, "x-auth-signature": signature, "x-auth-timestamp": timestamp, }
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 api_params = { "symbol": ascend_ex_utils.convert_to_exchange_trading_pair(trading_pair), "orderId": ex_order_id, "time": ascend_ex_utils.get_ms_timestamp() } await self._api_request(method="delete", path_url=CONSTANTS.ORDER_PATH_URL, data=api_params, is_auth_required=True, force_auth_path_url="order") return order_id except asyncio.CancelledError: raise except Exception as e: if str(e).find("Order not found") != -1: self.stop_tracking_order(order_id) return 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 AscendEx. " f"Check API key and network connection.")
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 """ try: tracked_order = self._in_flight_order_tracker.fetch_tracked_order(order_id) if tracked_order is None: non_tracked_order = self._in_flight_order_tracker.fetch_cached_order(order_id) if non_tracked_order is None: raise ValueError(f"Failed to cancel order - {order_id}. Order not found.") else: self.logger().info(f"The order {order_id} was finished before being cancelled") else: ex_order_id = await tracked_order.get_exchange_order_id() api_params = { "symbol": ascend_ex_utils.convert_to_exchange_trading_pair(trading_pair), "orderId": ex_order_id, "time": ascend_ex_utils.get_ms_timestamp(), } await self._api_request( method="delete", path_url=CONSTANTS.ORDER_PATH_URL, data=api_params, is_auth_required=True, force_auth_path_url="order", ) return order_id except asyncio.CancelledError: raise except asyncio.TimeoutError: self._stop_tracking_order_exceed_no_exchange_id_limit(tracked_order=tracked_order) except Exception as e: self.logger().error( f"Failed to cancel order {order_id}: {str(e)}", exc_info=True, )
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 (aka 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}") amount = self.quantize_order_amount(trading_pair, amount) price = self.quantize_order_price(trading_pair, price) if amount <= s_decimal_0: raise ValueError("Order amount must be greater than zero.") try: timestamp = ascend_ex_utils.get_ms_timestamp() api_params = { "id": order_id, "time": timestamp, "symbol": ascend_ex_utils.convert_to_exchange_trading_pair(trading_pair), "orderPrice": f"{price:f}", "orderQty": f"{amount:f}", "orderType": "limit", "side": "buy" if trade_type == TradeType.BUY else "sell", "respInst": "ACCEPT", } self.start_tracking_order(order_id, None, trading_pair, trade_type, price, amount, order_type) resp = await self._api_request(method="post", path_url=CONSTANTS.ORDER_PATH_URL, data=api_params, is_auth_required=True, force_auth_path_url="order") exchange_order_id = str(resp["data"]["info"]["orderId"]) tracked_order: AscendExInFlightOrder = self._in_flight_orders.get( order_id) tracked_order.update_exchange_order_id(exchange_order_id) if resp["data"]["status"] == "Ack": # Ack status means the server has received the request return tracked_order.update_status(resp["data"]["info"]["status"]) if tracked_order.is_failure: raise Exception( f'Failed to create an order, reason: {resp["data"]["info"]["errorCode"]}' ) self.logger().info( f"Created {order_type.name} {trade_type.name} order {order_id} for " f"{amount} {trading_pair}.") self.trigger_order_created_event(tracked_order) except asyncio.CancelledError: raise except Exception: self.stop_tracking_order(order_id) msg = f"Error submitting {trade_type.name} {order_type.name} order to AscendEx for " \ f"{amount} {trading_pair} " \ f"{price}." self.logger().network(msg, exc_info=True, app_warning_msg=msg) self.trigger_event( MarketEvent.OrderFailure, MarketOrderFailureEvent(self.current_timestamp, order_id, order_type))
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 (aka 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}") amount = self.quantize_order_amount(trading_pair, amount) price = self.quantize_order_price(trading_pair, price) if amount <= s_decimal_0: raise ValueError("Order amount must be greater than zero.") try: timestamp = ascend_ex_utils.get_ms_timestamp() # Order UUID is strictly used to enable AscendEx to construct a unique(still questionable) exchange_order_id order_uuid = f"{ascend_ex_utils.HBOT_BROKER_ID}-{ascend_ex_utils.uuid32()}"[:32] api_params = { "id": order_uuid, "time": timestamp, "symbol": ascend_ex_utils.convert_to_exchange_trading_pair(trading_pair), "orderPrice": f"{price:f}", "orderQty": f"{amount:f}", "orderType": "limit", "side": "buy" if trade_type == TradeType.BUY else "sell", "respInst": "ACCEPT", } self.start_tracking_order( order_id=order_id, trading_pair=trading_pair, trade_type=trade_type, price=price, amount=amount, order_type=order_type, ) try: resp = await self._api_request( method="post", path_url=CONSTANTS.ORDER_PATH_URL, data=api_params, is_auth_required=True, force_auth_path_url="order", ) resp_status = resp["data"]["status"].upper() order_data = resp["data"]["info"] if resp_status == "ACK": # Ack request status means the server has received the request return order_update = None if resp_status == "ACCEPT": order_update: OrderUpdate = OrderUpdate( client_order_id=order_id, exchange_order_id=str(order_data["orderId"]), trading_pair=trading_pair, update_timestamp=order_data["lastExecTime"] * 1e-3, new_state=OrderState.OPEN, ) elif resp_status == "DONE": order_update: OrderUpdate = OrderUpdate( client_order_id=order_id, exchange_order_id=str(order_data["orderId"]), trading_pair=trading_pair, update_timestamp=order_data["lastExecTime"] * 1e-3, new_state=CONSTANTS.ORDER_STATE[order_data["status"]], ) elif resp_status == "ERR": order_update: OrderUpdate = OrderUpdate( client_order_id=order_id, exchange_order_id=str(order_data["orderId"]), trading_pair=trading_pair, update_timestamp=order_data["lastExecTime"] * 1e-3, new_state=OrderState.FAILED, ) self._in_flight_order_tracker.process_order_update(order_update) except IOError: self.logger().exception(f"The request to create the order {order_id} failed") self.stop_tracking_order(order_id) except asyncio.CancelledError: raise except Exception: msg = (f"Error submitting {trade_type.name} {order_type.name} order to AscendEx for " f"{amount} {trading_pair} {price}.") self.logger().exception(msg)