Skip to content

Commit

Permalink
Merge pull request hummingbot#6966 from supervik/feat/add_kucoin_hft
Browse files Browse the repository at this point in the history
feat/add Kucoin HFT connector
  • Loading branch information
cardosofede authored Apr 22, 2024
2 parents 5d61b6c + 37b0ad6 commit dc35f2c
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 9 deletions.
5 changes: 5 additions & 0 deletions hummingbot/connector/exchange/kucoin/kucoin_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"

Expand Down Expand Up @@ -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),
]
31 changes: 22 additions & 9 deletions hummingbot/connector/exchange/kucoin/kucoin_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = {
Expand All @@ -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,
Expand All @@ -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

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions hummingbot/connector/exchange/kucoin/kucoin_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()}

0 comments on commit dc35f2c

Please sign in to comment.