diff --git a/hummingbot/connector/exchange/kucoin/kucoin_constants.py b/hummingbot/connector/exchange/kucoin/kucoin_constants.py index af2a62df6f..da3e3370ed 100644 --- a/hummingbot/connector/exchange/kucoin/kucoin_constants.py +++ b/hummingbot/connector/exchange/kucoin/kucoin_constants.py @@ -12,6 +12,7 @@ # REST endpoints BASE_PATH_URL = { "main": "https://api.kucoin.com", + "hft": "https://api.kucoin.com", } PUBLIC_WS_DATA_PATH_URL = "/api/v1/bullet-public" PRIVATE_WS_DATA_PATH_URL = "/api/v1/bullet-private" @@ -21,9 +22,11 @@ SERVER_TIME_PATH_URL = "/api/v1/timestamp" SYMBOLS_PATH_URL = "/api/v2/symbols" ORDERS_PATH_URL = "/api/v1/orders" +ORDERS_PATH_URL_HFT = "/api/v1/hf/orders" FEE_PATH_URL = "/api/v1/trade-fees" ALL_TICKERS_PATH_URL = "/api/v1/market/allTickers" FILLS_PATH_URL = "/api/v1/fills" +FILLS_PATH_URL_HFT = "/api/v1/hf/fills" LIMIT_FILLS_PATH_URL = "/api/v1/limit/fills" ORDER_CLIENT_ORDER_PATH_URL = "/api/v1/order/client-order" @@ -61,5 +64,7 @@ RateLimit(limit_id=POST_ORDER_LIMIT_ID, limit=45, time_interval=3), RateLimit(limit_id=DELETE_ORDER_LIMIT_ID, limit=60, time_interval=3), RateLimit(limit_id=ORDERS_PATH_URL, limit=45, time_interval=3), + RateLimit(limit_id=ORDERS_PATH_URL_HFT, limit=45, time_interval=3), RateLimit(limit_id=FILLS_PATH_URL, limit=9, time_interval=3), + RateLimit(limit_id=FILLS_PATH_URL_HFT, limit=9, time_interval=3), ] diff --git a/hummingbot/connector/exchange/kucoin/kucoin_exchange.py b/hummingbot/connector/exchange/kucoin/kucoin_exchange.py index bb0395f6d2..400c45f14c 100644 --- a/hummingbot/connector/exchange/kucoin/kucoin_exchange.py +++ b/hummingbot/connector/exchange/kucoin/kucoin_exchange.py @@ -89,6 +89,14 @@ def trading_pairs_request_path(self): def check_network_request_path(self): return CONSTANTS.SERVER_TIME_PATH_URL + @property + def orders_path_url(self): + return CONSTANTS.ORDERS_PATH_URL_HFT if self._domain == "hft" else CONSTANTS.ORDERS_PATH_URL + + @property + def fills_path_url(self): + return CONSTANTS.FILLS_PATH_URL_HFT if self.domain == "hft" else CONSTANTS.FILLS_PATH_URL + @property def trading_pairs(self): return self._trading_pairs @@ -193,7 +201,6 @@ async def _place_order(self, order_type: OrderType, price: Decimal, **kwargs) -> Tuple[str, float]: - path_url = CONSTANTS.ORDERS_PATH_URL side = trade_type.name.lower() order_type_str = "market" if order_type == OrderType.MARKET else "limit" data = { @@ -209,7 +216,7 @@ async def _place_order(self, data["price"] = str(price) data["postOnly"] = True exchange_order_id = await self._api_post( - path_url=path_url, + path_url=self.orders_path_url, data=data, is_auth_required=True, limit_id=CONSTANTS.POST_ORDER_LIMIT_ID, @@ -223,12 +230,15 @@ async def _place_cancel(self, order_id: str, tracked_order: InFlightOrder): This implementation specific function is called by _cancel, and returns True if successful """ exchange_order_id = await tracked_order.get_exchange_order_id() + params = {"symbol": tracked_order.trading_pair} if self.domain == "hft" else None cancel_result = await self._api_delete( - f"{CONSTANTS.ORDERS_PATH_URL}/{exchange_order_id}", + f"{self.orders_path_url}/{exchange_order_id}", + params=params, is_auth_required=True, limit_id=CONSTANTS.DELETE_ORDER_LIMIT_ID ) - if tracked_order.exchange_order_id in cancel_result["data"].get("cancelledOrderIds", []): + response_param = "orderId" if self.domain == "hft" else "cancelledOrderIds" + if tracked_order.exchange_order_id in cancel_result["data"].get(response_param, []): return True return False @@ -316,10 +326,11 @@ async def _user_stream_event_listener(self): async def _update_balances(self): local_asset_names = set(self._account_balances.keys()) remote_asset_names = set() + account_type = "trade_hf" if self.domain == "hft" else "trade" response = await self._api_get( path_url=CONSTANTS.ACCOUNTS_PATH_URL, - params={"type": "trade"}, + params={"type": account_type}, is_auth_required=True) if response: @@ -406,7 +417,7 @@ async def _all_trades_updates(self, orders: List[InFlightOrder]) -> List[TradeUp # "If you only specified the start time, the system will automatically # calculate the end time (end time = start time + 7 * 24 hours)" all_fills_response = await self._api_get( - path_url=CONSTANTS.FILLS_PATH_URL, + path_url=self.fills_path_url, params={ "pageSize": 500, "startAt": self._last_order_fill_ts_s * 1000, @@ -448,7 +459,7 @@ async def _all_trade_updates_for_order(self, order: InFlightOrder) -> List[Trade if order.exchange_order_id is not None: exchange_order_id = order.exchange_order_id all_fills_response = await self._api_get( - path_url=CONSTANTS.FILLS_PATH_URL, + path_url=self.fills_path_url, params={ "orderId": exchange_order_id, "pageSize": 500, @@ -479,13 +490,15 @@ async def _all_trade_updates_for_order(self, order: InFlightOrder) -> List[Trade async def _request_order_status(self, tracked_order: InFlightOrder) -> OrderUpdate: exchange_order_id = await tracked_order.get_exchange_order_id() + params = {"symbol": tracked_order.trading_pair} if self.domain == "hft" else None updated_order_data = await self._api_get( - path_url=f"{CONSTANTS.ORDERS_PATH_URL}/{exchange_order_id}", + path_url=f"{self.orders_path_url}/{exchange_order_id}", is_auth_required=True, + params=params, limit_id=CONSTANTS.GET_ORDER_LIMIT_ID) ordered_canceled = updated_order_data["data"]["cancelExist"] - is_active = updated_order_data["data"]["isActive"] + is_active = updated_order_data["data"]["active"] if self.domain == "hft" else updated_order_data["data"]["isActive"] op_type = updated_order_data["data"]["opType"] new_state = tracked_order.current_state diff --git a/hummingbot/connector/exchange/kucoin/kucoin_utils.py b/hummingbot/connector/exchange/kucoin/kucoin_utils.py index ba96c43ef5..069da94e81 100644 --- a/hummingbot/connector/exchange/kucoin/kucoin_utils.py +++ b/hummingbot/connector/exchange/kucoin/kucoin_utils.py @@ -62,3 +62,45 @@ class Config: KEYS = KuCoinConfigMap.construct() + +OTHER_DOMAINS = ["kucoin_hft"] +OTHER_DOMAINS_PARAMETER = {"kucoin_hft": "hft"} +OTHER_DOMAINS_EXAMPLE_PAIR = {"kucoin_hft": "ETH-USDT"} +OTHER_DOMAINS_DEFAULT_FEES = {"kucoin_hft": DEFAULT_FEES} + + +class KuCoinHFTConfigMap(BaseConnectorConfigMap): + connector: str = Field(default="kucoin_hft", client_data=None) + kucoin_hft_api_key: SecretStr = Field( + default=..., + client_data=ClientFieldData( + prompt=lambda cm: "Enter your KuCoin HFT API key", + is_secure=True, + is_connect_key=True, + prompt_on_new=True, + ) + ) + kucoin_hft_secret_key: SecretStr = Field( + default=..., + client_data=ClientFieldData( + prompt=lambda cm: "Enter your KuCoin HFT secret key", + is_secure=True, + is_connect_key=True, + prompt_on_new=True, + ) + ) + kucoin_hft_passphrase: SecretStr = Field( + default=..., + client_data=ClientFieldData( + prompt=lambda cm: "Enter your KuCoin HFT passphrase", + is_secure=True, + is_connect_key=True, + prompt_on_new=True, + ) + ) + + class Config: + title = "kucoin_hft" + + +OTHER_DOMAINS_KEYS = {"kucoin_hft": KuCoinHFTConfigMap.construct()}