From 0e7470b873b464b3c352c872c8f54c7f51e2a743 Mon Sep 17 00:00:00 2001 From: yuanhaijiang Date: Mon, 30 Mar 2026 08:32:56 +0800 Subject: [PATCH 1/2] sdk update --- alpha/.DS_Store | Bin 0 -> 6148 bytes alpha/platforms/.DS_Store | Bin 0 -> 6148 bytes .../restapi/rest_algo_order_sync.py | 40 +++ .../restapi/rest_algo_order_usdt_swap.py | 283 ++++++++++++++++++ .../restapi/rest_copytrading_trade_sync.py | 63 ++++ .../rest_copytrading_trade_usdt_swap.py | 186 ++++++++++++ examples/.DS_Store | Bin 0 -> 8196 bytes setup.py | 2 +- tests/.DS_Store | Bin 0 -> 6148 bytes tests/huobi_coin_future_test/.DS_Store | Bin 0 -> 6148 bytes tests/huobi_coin_swap_test/.DS_Store | Bin 0 -> 6148 bytes tests/huobi_usdt_swap_test/.DS_Store | Bin 0 -> 6148 bytes 12 files changed, 573 insertions(+), 1 deletion(-) create mode 100644 alpha/.DS_Store create mode 100644 alpha/platforms/.DS_Store create mode 100644 alpha/platforms/huobi_usdt_swap/restapi/rest_algo_order_sync.py create mode 100644 alpha/platforms/huobi_usdt_swap/restapi/rest_algo_order_usdt_swap.py create mode 100644 alpha/platforms/huobi_usdt_swap/restapi/rest_copytrading_trade_sync.py create mode 100644 alpha/platforms/huobi_usdt_swap/restapi/rest_copytrading_trade_usdt_swap.py create mode 100644 examples/.DS_Store create mode 100644 tests/.DS_Store create mode 100644 tests/huobi_coin_future_test/.DS_Store create mode 100644 tests/huobi_coin_swap_test/.DS_Store create mode 100644 tests/huobi_usdt_swap_test/.DS_Store diff --git a/alpha/.DS_Store b/alpha/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f46ac4c0c7a7dd38d782fcc5a18e6a0b4057651f GIT binary patch literal 6148 zcmeHKJ5Iwu5SyjdNZf>y8zAv! zW(jNAky0TP%}BFvJsrm;Xg9K?=Gd93Ob`Zir(LJeqNQel$<_1_eYggo-c}O0dF#Tq|dL{_rpz*%Pk}8 zt7zr16tGV#)OL*;@wMf}#$AJJOh@&Cfa7%&Emft_Oj zJ)0#NDcWcZ7z4(@ngRYkcqn5Quu+sx2L?F;0EaM(U@m(mpl1Ux3)m>a0&$uO)KnLa z7*5k+_aQC|*eGf`IawL^2`jsBLiyRTIWOVlGDRDW0b`)ez@A=Cxc?t*KL59q?8+E0 z26l=8H_Yex4389gYvc0=wr4Fbmiy!UD04fY4xr JG4Q7hd;`SwQdj^0 literal 0 HcmV?d00001 diff --git a/alpha/platforms/.DS_Store b/alpha/platforms/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..bdc64473016f3932200a5b5afaed35e2c20afeae GIT binary patch literal 6148 zcmeHKK}y3w6n#^J6fIbHE^`EXgIMayWe-qotG3WK!CJ(PWal+pi!MBX3qkM};>Mj< zu=xJWXcN=gAR<;~VCE&0|1$I5XEGT8syb>{fHHt0tDv{SW=fIgQZuDsdxWg?JGy9~ zg%Di~GSRNXIp7@lYYxcTT}KBauG%Ko?PUEnaKPSU)+6lq4lDIm*pKw|)%Y|Ys~^^C z&0g4Kbn)r-vUGp`u$7nD{w6c(BxYK;;BZ)*p>K#2>>)~PdwHGDNb0)7)w`}avxKIdVCA=;dIgP6^O zck?`-k<>8Wyq=zi%s9zB<-29yb&Ve6yK)w$?t6*R;HmE-x zZ1fR;=&@TDUdu0qaD0n_#n>R9uqZ<%8mh8K3}xu-_ibEYF*a!EQ1 z@Aq{$RAA7x&H?8@+JPl=TbBKQd-nN1O>%e60q4NKazJ^tdaX)Hc5lsylfBkw-DQ;` paj`*7VPm&reIZ-%2CFW7=F0^!uoxR;4~zQ|&^EZnIq<6vyaBf4$=Uz_ literal 0 HcmV?d00001 diff --git a/alpha/platforms/huobi_usdt_swap/restapi/rest_algo_order_sync.py b/alpha/platforms/huobi_usdt_swap/restapi/rest_algo_order_sync.py new file mode 100644 index 0000000..454b84c --- /dev/null +++ b/alpha/platforms/huobi_usdt_swap/restapi/rest_algo_order_sync.py @@ -0,0 +1,40 @@ +import json + +from alpha.utils.http_utils import post, get_url_suffix, get + +class RestAlgoOrderUsdtSwap: + + def __init__(self, access_key: str, secret_key: str, host: str = None): + self.access_key = access_key + self.secret_key = secret_key + if host is None: + host = "api.hbdm.com" + self.host = host + + def algo_order(self, data: dict = None) -> dict: + """创建策略订单""" + path = "/v5/algo/order" + return post(self.access_key, self.secret_key, self.host, path, data) + + def cancel_algo_orders(self, data: dict = None) -> dict: + """取消策略订单""" + path = "/v5/algo/cancel-orders" + return post(self.access_key, self.secret_key, self.host, path, data) + + def query_algo_order(self, params: dict = None) -> dict: + """查询策略订单""" + path = "/v5/algo/order" + path = "{}?{}".format(path, get_url_suffix('get', self.access_key, self.secret_key, self.host, path)) + return get(self.host, path, params) + + def query_open_algo_orders(self, params: dict = None) -> dict: + """查询未触发策略订单""" + path = "/v5/algo/order/opens" + path = "{}?{}".format(path, get_url_suffix('get', self.access_key, self.secret_key, self.host, path)) + return get(self.host, path, params) + + def query_algo_order_history(self, params: dict = None) -> dict: + """查询历史策略订单""" + path = "/v5/algo/order/history" + path = "{}?{}".format(path, get_url_suffix('get', self.access_key, self.secret_key, self.host, path)) + return get(self.host, path, params) \ No newline at end of file diff --git a/alpha/platforms/huobi_usdt_swap/restapi/rest_algo_order_usdt_swap.py b/alpha/platforms/huobi_usdt_swap/restapi/rest_algo_order_usdt_swap.py new file mode 100644 index 0000000..a6732c2 --- /dev/null +++ b/alpha/platforms/huobi_usdt_swap/restapi/rest_algo_order_usdt_swap.py @@ -0,0 +1,283 @@ +# -*- coding:utf-8 -*- + +""" +Huobi USDT Swap Api Module. + +Author: QiaoXiaofeng +Date: 2020/09/10 +Email: andyjoe318@gmail.com +""" + +import gzip +import json +import copy +import hmac +import base64 +import urllib +import hashlib +import datetime +import time +from urllib.parse import urljoin +from alpha.utils.request import AsyncHttpRequests +from alpha.const import USER_AGENT + +__all__ = ("HuobiUsdtSwapRestAlgoOrderAPI",) + + +class HuobiUsdtSwapRestAlgoOrderAPI: + """Huobi USDT Swap 策略订单REST API Client""" + + def __init__(self, host, access_key, secret_key): + self._host = host + self._access_key = access_key + self._secret_key = secret_key + + async def algo_order(self, + contract_code: str, + type: str, + position_side: str, + side: str, + margin_mode: str, + algo_client_order_id: str = None, + volume: str = None, + tp_trigger_price: str = None, + tp_order_price: str = None, + tp_type: str = None, + tp_trigger_price_type: str = None, + sl_trigger_price: str = None, + sl_order_price: str = None, + sl_type: str = None, + sl_trigger_price_type: str = None, + price: str = None, + price_match: str = None, + trigger_price: str = None, + trigger_price_type: str = None, + active_price: str = None, + order_price_type: str = None, + callback_rate: str = None, + reduce_only: bool = None): + """创建策略订单""" + uri = "/v5/algo/order" + body = { + "contract_code": contract_code, + "type": type, + "position_side": position_side, + "side": side, + "margin_mode": margin_mode + } + + # 可选字段 + if algo_client_order_id: + body["algo_client_order_id"] = algo_client_order_id + if volume: + body["volume"] = volume + if tp_trigger_price: + body["tp_trigger_price"] = tp_trigger_price + if tp_order_price: + body["tp_order_price"] = tp_order_price + if tp_type: + body["tp_type"] = tp_type + if tp_trigger_price_type: + body["tp_trigger_price_type"] = tp_trigger_price_type + if sl_trigger_price: + body["sl_trigger_price"] = sl_trigger_price + if sl_order_price: + body["sl_order_price"] = sl_order_price + if sl_type: + body["sl_type"] = sl_type + if sl_trigger_price_type: + body["sl_trigger_price_type"] = sl_trigger_price_type + if price: + body["price"] = price + if price_match: + body["price_match"] = price_match + if trigger_price: + body["trigger_price"] = trigger_price + if trigger_price_type: + body["trigger_price_type"] = trigger_price_type + if active_price: + body["active_price"] = active_price + if order_price_type: + body["order_price_type"] = order_price_type + if callback_rate: + body["callback_rate"] = callback_rate + if reduce_only is not None: + body["reduce_only"] = 1 if reduce_only else 0 + + success, error = await self.request("POST", uri, body=body, auth=True) + return success, error + + async def cancel_algo_orders(self, + algo_id: str = None, + algo_client_order_id: str = None, + contract_code: str = None): + """取消策略订单""" + uri = "/v5/algo/cancel-orders" + body = {} + + if algo_id: + body["algo_id"] = algo_id + if algo_client_order_id: + body["algo_client_order_id"] = algo_client_order_id + if contract_code: + body["contract_code"] = contract_code + + success, error = await self.request("POST", uri, body=body, auth=True) + return success, error + + async def query_algo_order(self, + type: str, + algo_id: str = None, + algo_client_order_id: str = None, + contract_code: str = None): + """查询策略订单""" + uri = "/v5/algo/order" + params = { + "type": type + } + + if algo_id: + params["algo_id"] = algo_id + if algo_client_order_id: + params["algo_client_order_id"] = algo_client_order_id + if contract_code: + params["contract_code"] = contract_code + + success, error = await self.request("GET", uri, params=params, auth=True) + return success, error + + async def query_open_algo_orders(self, + type: str, + contract_code: str = None, + algo_id: str = None, + algo_client_order_id: str = None, + from_page: int = None, + limit: int = None, + direct: str = None): + """查询未触发策略订单""" + uri = "/v5/algo/order/opens" + params = { + "type": type + } + + if contract_code: + params["contract_code"] = contract_code + if algo_id: + params["algo_id"] = algo_id + if algo_client_order_id: + params["algo_client_order_id"] = algo_client_order_id + if from_page is not None: + params["from"] = from_page + if limit is not None: + params["limit"] = limit + if direct: + params["direct"] = direct + + success, error = await self.request("GET", uri, params=params, auth=True) + return success, error + + async def query_algo_order_history(self, + type: str, + contract_code: str = None, + margin_mode: str = None, + states: list = None, + start_time: int = None, + end_time: int = None, + from_page: int = None, + limit: int = None, + direct: str = None): + """查询历史策略订单""" + uri = "/v5/algo/order/history" + params = { + "type": type + } + + if contract_code: + params["contract_code"] = contract_code + if margin_mode: + params["margin_mode"] = margin_mode + if states: + params["states"] = ",".join(states) + if start_time: + params["start_time"] = start_time + if end_time: + params["end_time"] = end_time + if from_page is not None: + params["from"] = from_page + if limit is not None: + params["limit"] = limit + if direct: + params["direct"] = direct + + success, error = await self.request("GET", uri, params=params, auth=True) + return success, error + + + async def request(self, method, uri, params=None, body=None, headers=None, auth=False): + """ Do HTTP request. + + Args: + method: HTTP request method. `GET` / `POST` / `DELETE` / `PUT`. + uri: HTTP request uri. + params: HTTP query params. + body: HTTP request body. + headers: HTTP request headers. + auth: If this request requires authentication. + + Returns: + success: Success results, otherwise it's None. + error: Error information, otherwise it's None. + """ + if uri.startswith("http://") or uri.startswith("https://"): + url = uri + else: + url = self._host + uri + + if auth: + timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S") + params = params if params else {} + params.update({"AccessKeyId": self._access_key, + "SignatureMethod": "HmacSHA256", + "SignatureVersion": "2", + "Timestamp": timestamp}) + + params["Signature"] = self.generate_signature(method, params, uri) + + if not headers: + headers = {} + if method == "GET": + headers["Content-type"] = "application/x-www-form-urlencoded" + headers["User-Agent"] = USER_AGENT + _, success, error = await AsyncHttpRequests.fetch("GET", url, params=params, headers=headers, timeout=10) + else: + headers["Accept"] = "application/json" + headers["Content-type"] = "application/json" + headers["User-Agent"] = USER_AGENT + _, success, error = await AsyncHttpRequests.fetch("POST", url, params=params, data=body, headers=headers, + timeout=10) + if error: + return None, error + if not isinstance(success, dict): + result = json.loads(success) + else: + result = success + if result.get("status") != "ok": + return None, result + return result, None + + def generate_signature(self, method, params, request_path): + if request_path.startswith("http://") or request_path.startswith("https://"): + host_url = urllib.parse.urlparse(request_path).hostname.lower() + request_path = '/' + '/'.join(request_path.split('/')[3:]) + else: + host_url = urllib.parse.urlparse(self._host).hostname.lower() + sorted_params = sorted(params.items(), key=lambda d: d[0], reverse=False) + encode_params = urllib.parse.urlencode(sorted_params) + payload = [method, host_url, request_path, encode_params] + payload = "\n".join(payload) + payload = payload.encode(encoding="UTF8") + secret_key = self._secret_key.encode(encoding="utf8") + digest = hmac.new(secret_key, payload, digestmod=hashlib.sha256).digest() + signature = base64.b64encode(digest) + signature = signature.decode() + return signature \ No newline at end of file diff --git a/alpha/platforms/huobi_usdt_swap/restapi/rest_copytrading_trade_sync.py b/alpha/platforms/huobi_usdt_swap/restapi/rest_copytrading_trade_sync.py new file mode 100644 index 0000000..7115071 --- /dev/null +++ b/alpha/platforms/huobi_usdt_swap/restapi/rest_copytrading_trade_sync.py @@ -0,0 +1,63 @@ +import json + +from alpha.utils.http_utils import post, get_url_suffix, get + +class RestCopyTradingTradeSync: + def __init__(self, access_key: str, secret_key: str, host: str = None): + self.access_key = access_key + self.secret_key = secret_key + if host is None: + host = "api.hbdm.com" + self.host = host + + def contract_copytrading_trader_instruments(self, params: dict = None) -> json: + path = "/api/v6/copyTrading/trader/instruments" + path = "{}?{}".format(path, get_url_suffix('get', self.access_key, self.secret_key, self.host, path)) + return get(self.host, path, params) + + def contract_copytrading_trader_statistics(self, params: dict = None) -> json: + path = "/api/v6/copyTrading/trader/statistics" + path = "{}?{}".format(path, get_url_suffix('get', self.access_key, self.secret_key, self.host, path)) + return get(self.host, path, params) + + def contract_copytrading_trader_profit_sharing_history(self, params: dict = None) -> json: + path = "/api/v6/copyTrading/trader/profit-sharing-history" + path = "{}?{}".format(path, get_url_suffix('get', self.access_key, self.secret_key, self.host, path)) + return get(self.host, path, params) + + def contract_copytrading_trader_profit_sharing_history_summary(self, params: dict = None) -> json: + path = "/api/v6/copyTrading/trader/profit-sharing-history-summary" + path = "{}?{}".format(path, get_url_suffix('get', self.access_key, self.secret_key, self.host, path)) + return get(self.host, path, params) + + def contract_copytrading_trader_unrealized_profit_sharing_summary(self, params: dict = None) -> json: + path = "/api/v6/copyTrading/trader/unrealized-profit-sharing-summary" + path = "{}?{}".format(path, get_url_suffix('get', self.access_key, self.secret_key, self.host, path)) + return get(self.host, path, params) + + def contract_copytrading_trader_followers(self, params: dict = None) -> json: + path = "/api/v6/copyTrading/trader/followers" + path = "{}?{}".format(path, get_url_suffix('get', self.access_key, self.secret_key, self.host, path)) + return get(self.host, path, params) + + def contract_copytrading_trader_follower(self, data: dict = None) -> json: + path = "/api/v6/copyTrading/trader/follower" + return post(self.access_key, self.secret_key, self.host, path, data) + + def contract_copytrading_trader_transfer(self, data: dict = None) -> json: + path = "/api/v6/copyTrading/trader/transfer" + return post(self.access_key, self.secret_key, self.host, path, data) + + def contract_copytrading_trader_follower_settings(self, data: dict = None) -> json: + path = "/api/v6/copyTrading/trader/follower-settings" + return post(self.access_key, self.secret_key, self.host, path, data) + + def contract_copytrading_trader_config(self, params: dict = None) -> json: + path = "/api/v6/copyTrading/trader/config" + path = "{}?{}".format(path, get_url_suffix('get', self.access_key, self.secret_key, self.host, path)) + return get(self.host, path, params) + + def contract_copytrading_trader_apikey(self, data: dict = None) -> json: + path = "/api/v6/copyTrading/trader/apikey" + return post(self.access_key, self.secret_key, self.host, path, data) + diff --git a/alpha/platforms/huobi_usdt_swap/restapi/rest_copytrading_trade_usdt_swap.py b/alpha/platforms/huobi_usdt_swap/restapi/rest_copytrading_trade_usdt_swap.py new file mode 100644 index 0000000..b3abcf5 --- /dev/null +++ b/alpha/platforms/huobi_usdt_swap/restapi/rest_copytrading_trade_usdt_swap.py @@ -0,0 +1,186 @@ +# -*- coding:utf-8 -*- + +import gzip +import json +import copy +import hmac +import base64 +import urllib +import hashlib +import datetime +import time +from urllib.parse import urljoin +from alpha.utils.request import AsyncHttpRequests +from alpha.const import USER_AGENT + +__all__ = ("HuobiUsdtSwapRestCopytradingTradeAPI",) + +class HuobiUsdtSwapRestCopytradingTradeAPI: + """ Huobi USDT Swap REST API Client. """ + + def __init__(self, host: str, access_key: str, secret_key: str): + """初始化跟单API客户端 + + Args: + host: API主机地址,如 "https://api.hbdm.com" + access_key: API访问密钥 + secret_key: API秘密密钥 + """ + self._host = host + self._access_key = access_key + self._secret_key = secret_key + self._success_code = "0" + + async def query_trader_instruments(self, inst_type: str) -> tuple: + uri = "/api/v6/copyTrading/trader/instruments" + params = {"instType": inst_type} + return await self._request("GET", uri, params=params, auth=True) + + async def query_trader_statistics(self, inst_type: str) -> tuple: + uri = "/api/v6/copyTrading/trader/statistics" + params = {"instType": inst_type} + return await self._request("GET", uri, params=params, auth=True) + + async def query_trader_profit_sharing_history(self, inst_id: str, inst_type: str, begin: str=None, end: str=None, after: str=None, before: str=None, limit: str=None) -> tuple: + uri = "/api/v6/copyTrading/trader/profit-sharing-history" + params = {"instId": inst_id, "instType": inst_type} + if begin: + params["begin"] = begin + if end: + params["end"] = end + if after: + params["after"] = after + if before: + params["before"] = before + if limit: + params["limit"] = limit + return await self._request("GET", uri, params=params, auth=True) + + async def query_trader_profit_sharing_history_summary(self, inst_type: str) -> tuple: + uri = "/api/v6/copyTrading/trader/profit-sharing-history-summary" + params = {"instType": inst_type} + return await self._request("GET", uri, params=params, auth=True) + + async def query_trader_unrealized_profit_sharing_summary(self, inst_type: str, ccy: str=None) -> tuple: + uri = "/api/v6/copyTrading/trader/unrealized-profit-sharing-summary" + params = {"instType": inst_type} + if ccy: + params["ccy"] = ccy + return await self._request("GET", uri, params=params, auth=True) + + async def query_trader_followers(self, inst_type: str, begin: str=None, end: str=None, after: str=None, before: str=None, limit: str=None) -> tuple: + uri = "/api/v6/copyTrading/trader/followers" + params = {"instType": inst_type} + if begin: + params["begin"] = begin + if end: + params["end"] = end + if after: + params["after"] = after + if before: + params["before"] = before + if limit: + params["limit"] = limit + return await self._request("GET", uri, params=params, auth=True) + + async def delete_trader_follower(self, inst_type: str, follower_uids: str) -> tuple: + uri = "/api/v6/copyTrading/trader/follower" + body = {"instType": inst_type, "followerUids": follower_uids} + return await self.request("POST", uri, body=body, auth=True) + + async def trader_transfer(self, amt: str, from_: str, to: str, ccy: str=None) -> tuple: + uri = "/api/v6/copyTrading/trader/transfer" + body = {"amt": amt, "from": from_, "to": to} + if ccy: + body["ccy"] = ccy + return await self.request("POST", uri, body=body, auth=True) + + async def trader_follower_settings(self, inst_type: str, enable: str=None, profit_sharing_ratio: str=None, max_followers: str=None) -> tuple: + uri = "/api/v6/copyTrading/trader/follower-settings" + body = {"instType": inst_type} + if enable: + body["enable"] = enable + if profit_sharing_ratio: + body["profitSharingRatio"] = profit_sharing_ratio + if max_followers: + body["maxFollowers"] = max_followers + return await self.request("POST", uri, body=body, auth=True) + + async def trader_config(self, inst_type: str) -> tuple: + uri = "/api/v6/copyTrading/trader/config" + params = {"instType": inst_type} + return await self._request("GET", uri, params=params, auth=True) + + async def trader_apikey(self, inst_type: str) -> tuple: + uri = "/api/v6/copyTrading/trader/apikey" + body = {"instType": inst_type} + return await self.request("POST", uri, body=body, auth=True) + + async def request(self, method, uri, params=None, body=None, headers=None, auth=False): + """ Do HTTP request. + + Args: + method: HTTP request method. `GET` / `POST` / `DELETE` / `PUT`. + uri: HTTP request uri. + params: HTTP query params. + body: HTTP request body. + headers: HTTP request headers. + auth: If this request requires authentication. + + Returns: + success: Success results, otherwise it's None. + error: Error information, otherwise it's None. + """ + if uri.startswith("http://") or uri.startswith("https://"): + url = uri + else: + url = self._host + uri + + if auth: + timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S") + params = params if params else {} + params.update({"AccessKeyId": self._access_key, + "SignatureMethod": "HmacSHA256", + "SignatureVersion": "2", + "Timestamp": timestamp}) + + params["Signature"] = self.generate_signature(method, params, uri) + + if not headers: + headers = {} + if method == "GET": + headers["Content-type"] = "application/x-www-form-urlencoded" + headers["User-Agent"] = USER_AGENT + _, success, error = await AsyncHttpRequests.fetch("GET", url, params=params, headers=headers, timeout=10) + else: + headers["Accept"] = "application/json" + headers["Content-type"] = "application/json" + headers["User-Agent"] = USER_AGENT + _, success, error = await AsyncHttpRequests.fetch("POST", url, params=params, data=body, headers=headers, + timeout=10) + if error: + return None, error + if not isinstance(success, dict): + result = json.loads(success) + else: + result = success + if result.get("status") != "ok": + return None, result + return result, None + + def generate_signature(self, method, params, request_path): + if request_path.startswith("http://") or request_path.startswith("https://"): + host_url = urllib.parse.urlparse(request_path).hostname.lower() + request_path = '/' + '/'.join(request_path.split('/')[3:]) + else: + host_url = urllib.parse.urlparse(self._host).hostname.lower() + sorted_params = sorted(params.items(), key=lambda d: d[0], reverse=False) + encode_params = urllib.parse.urlencode(sorted_params) + payload = [method, host_url, request_path, encode_params] + payload = "\n".join(payload) + payload = payload.encode(encoding="UTF8") + secret_key = self._secret_key.encode(encoding="utf8") + digest = hmac.new(secret_key, payload, digestmod=hashlib.sha256).digest() + signature = base64.b64encode(digest) + signature = signature.decode() + return signature \ No newline at end of file diff --git a/examples/.DS_Store b/examples/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..15386e65641c6a35e24f82bc50e601c26de580a6 GIT binary patch literal 8196 zcmeHMy>8P`7(Ew?6hQ?nNU6l&sX`!x%D`-@Fm*u+E2N|eZA4s0bs}K%)|D6F6=2~J zDj_klBk>08Oq~0JeXr~!4r);(zAN2(9Q!->p6}SQ{W$>C?rg9Fv;nl(HQv0&p{D8M zx>wpIx92udkq?Z}!z;`%WtS!nhy&t)I3Ny)1MBDjpV_*#<~;Z9Q5nSnap1pnK;IuC zc8#&a+@iU4a4;nRaf7=}<2LI6!-*Wm4s(l!$~kTIpd3}X5<^)!k4HM37(2`@S~`@a zL%Efet5B3#oxI3|L&X+l6bHnCssp-q-^3B?^%VV}em}u*e7Ms+NRx@B-8)+po1~pi zf1LIi)B5r3vk#rF!s*S`0y&>s3;!z#0A&IncMQo{jAntFiq&fBh`- zTw#6x%>CUAHTmCZoBRs;jy;Tdg-*GBOo!g_x8BY$4PCnboi3gG8orlr+n@g2 zZ!Tb+@+!N?b!yJbb-bp-Bf2{@-liNAbALNT8hYHmLXQ{5`+j|RSopPsQ|8{36Xx-h zvZSr41DCC?wyyu5%>Vwsrs_qOI3Nz3hXZP()9vhXCRbZm=G3)z#Jzswy(#jst`7yebP>WLP z(siM?70dy1;IBErbN7HcG{&rLV&3NSH=#>ZN5~txKz)RENTc3WteknnpZey8y!BMs-CPSy zxjA4ClsLfOhY-maDQ1HH(}Bis0e}I@K5)!?3Fd@~kzyu@D+ni4pirG|F`Up*54B&U zm-XBuD}QYA>e;U-KXUupvz=ey{7j4iW55{r>kOb~ zvm|?hHW~xQfHBZ8z~2WCWeke7VElAoh%Eqc2y+z7xtHJ=uNV|-L99TWgaRehX^Y_` z9CojML9rH;aB|vwI6c{EhvMSt*xyHYazW5WW55__Gq59rA3JCL%Y!pN)uyM3g`odjkv|!t1Op>8XVmUEJe>O1fTLjV9Bg zl7yG%{ny90<1Vk+FTBb*zb&iN zp0EuK=mGg;$f%$x-C(|ycWUKzaMbO!p4Z2FomaX8(z@{^R zn$42z3EF537z4&Y%>aKNJd`mgR)X=Dpvl0FTn@SZpM2l{H-qfT7%&F5 ziUHS8C+Qfsq_wqmb6jgZ^c>2_;HdV1qI6 Hrwn`o0Mu0V literal 0 HcmV?d00001 diff --git a/tests/huobi_usdt_swap_test/.DS_Store b/tests/huobi_usdt_swap_test/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..930710199cf48f3f3ac3c0fb855dfea895b4e29f GIT binary patch literal 6148 zcmeHKu};J=40Vo^=n007Oenu_0}E3)l`pt~6?%sNaaAig#F~v?;4={4fW&X{9Gh}g z3t~cmY{_}4V<+xwR3}8_W_QaeQBFh|lyNjcGax+9+LMu5c+jOe&ZwcQ)x~tasC(XK z_>Bzkx7(&0I;UG&)5iC!8cFx@uw4iJB*RrSHbIK2fy*BeY32j~j^HudnZeM$L@}t1{nHU4cfHCmb89>cu z$#w*-H3p0UW1wY#{|_F@7!;#m`gNd5R{-D`<|OE+GaS&f0T>jcAS@6kp+E_Bdc|-O z4!c*spcn-uoLpKN^XSS>Zzyttgxx21azW5qW55{bGO#6&IoJQ=&+q@;AbT Date: Mon, 30 Mar 2026 17:42:14 +0800 Subject: [PATCH 2/2] rm ds store --- .DS_Store | Bin 6148 -> 0 bytes alpha/.DS_Store | Bin 6148 -> 0 bytes alpha/platforms/.DS_Store | Bin 6148 -> 0 bytes examples/.DS_Store | Bin 8196 -> 0 bytes tests/.DS_Store | Bin 6148 -> 0 bytes tests/huobi_coin_future_test/.DS_Store | Bin 6148 -> 0 bytes tests/huobi_coin_swap_test/.DS_Store | Bin 6148 -> 0 bytes tests/huobi_usdt_swap_test/.DS_Store | Bin 6148 -> 0 bytes 8 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store delete mode 100644 alpha/.DS_Store delete mode 100644 alpha/platforms/.DS_Store delete mode 100644 examples/.DS_Store delete mode 100644 tests/.DS_Store delete mode 100644 tests/huobi_coin_future_test/.DS_Store delete mode 100644 tests/huobi_coin_swap_test/.DS_Store delete mode 100644 tests/huobi_usdt_swap_test/.DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 6b7abf941054a1c689c176499e81f2f8e16b3b2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKJ8Hu~5S?*c2;8VlxmWNF7NML#7jPg(jd8$s0_jxcTsfNGd#w z?7S6vg+?PH+P%GOMOqPQ;D+*PVQF@5zOYqh6bQ#17um}wAMeBIxTwCJFm5AfFi-OQ zlYe+Uj&bj8BC}M03Qz$mKn1A4pA@j(3u`ukj8uRMP=Rj+?E6sQhBa{t^iKzZj{v|m z(r#G$ECDQ*0M^7Q5E+;T6&O^_5krHHe95|+I0Xh>G=~q(Cu>e9>QBf0#mhx&AR`r^ z0&@i(V%u8(e}exq|IbO>Q2{D&R|@F5ANmfjl)ZKFa@K1Ld#>1^!!sE7tcF#Q*>R diff --git a/alpha/.DS_Store b/alpha/.DS_Store deleted file mode 100644 index f46ac4c0c7a7dd38d782fcc5a18e6a0b4057651f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKJ5Iwu5SyjdNZf>y8zAv! zW(jNAky0TP%}BFvJsrm;Xg9K?=Gd93Ob`Zir(LJeqNQel$<_1_eYggo-c}O0dF#Tq|dL{_rpz*%Pk}8 zt7zr16tGV#)OL*;@wMf}#$AJJOh@&Cfa7%&Emft_Oj zJ)0#NDcWcZ7z4(@ngRYkcqn5Quu+sx2L?F;0EaM(U@m(mpl1Ux3)m>a0&$uO)KnLa z7*5k+_aQC|*eGf`IawL^2`jsBLiyRTIWOVlGDRDW0b`)ez@A=Cxc?t*KL59q?8+E0 z26l=8H_Yex4389gYvc0=wr4Fbmiy!UD04fY4xr JG4Q7hd;`SwQdj^0 diff --git a/alpha/platforms/.DS_Store b/alpha/platforms/.DS_Store deleted file mode 100644 index bdc64473016f3932200a5b5afaed35e2c20afeae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKK}y3w6n#^J6fIbHE^`EXgIMayWe-qotG3WK!CJ(PWal+pi!MBX3qkM};>Mj< zu=xJWXcN=gAR<;~VCE&0|1$I5XEGT8syb>{fHHt0tDv{SW=fIgQZuDsdxWg?JGy9~ zg%Di~GSRNXIp7@lYYxcTT}KBauG%Ko?PUEnaKPSU)+6lq4lDIm*pKw|)%Y|Ys~^^C z&0g4Kbn)r-vUGp`u$7nD{w6c(BxYK;;BZ)*p>K#2>>)~PdwHGDNb0)7)w`}avxKIdVCA=;dIgP6^O zck?`-k<>8Wyq=zi%s9zB<-29yb&Ve6yK)w$?t6*R;HmE-x zZ1fR;=&@TDUdu0qaD0n_#n>R9uqZ<%8mh8K3}xu-_ibEYF*a!EQ1 z@Aq{$RAA7x&H?8@+JPl=TbBKQd-nN1O>%e60q4NKazJ^tdaX)Hc5lsylfBkw-DQ;` paj`*7VPm&reIZ-%2CFW7=F0^!uoxR;4~zQ|&^EZnIq<6vyaBf4$=Uz_ diff --git a/examples/.DS_Store b/examples/.DS_Store deleted file mode 100644 index 15386e65641c6a35e24f82bc50e601c26de580a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHMy>8P`7(Ew?6hQ?nNU6l&sX`!x%D`-@Fm*u+E2N|eZA4s0bs}K%)|D6F6=2~J zDj_klBk>08Oq~0JeXr~!4r);(zAN2(9Q!->p6}SQ{W$>C?rg9Fv;nl(HQv0&p{D8M zx>wpIx92udkq?Z}!z;`%WtS!nhy&t)I3Ny)1MBDjpV_*#<~;Z9Q5nSnap1pnK;IuC zc8#&a+@iU4a4;nRaf7=}<2LI6!-*Wm4s(l!$~kTIpd3}X5<^)!k4HM37(2`@S~`@a zL%Efet5B3#oxI3|L&X+l6bHnCssp-q-^3B?^%VV}em}u*e7Ms+NRx@B-8)+po1~pi zf1LIi)B5r3vk#rF!s*S`0y&>s3;!z#0A&IncMQo{jAntFiq&fBh`- zTw#6x%>CUAHTmCZoBRs;jy;Tdg-*GBOo!g_x8BY$4PCnboi3gG8orlr+n@g2 zZ!Tb+@+!N?b!yJbb-bp-Bf2{@-liNAbALNT8hYHmLXQ{5`+j|RSopPsQ|8{36Xx-h zvZSr41DCC?wyyu5%>Vwsrs_qOI3Nz3hXZP()9vhXCRbZm=G3)z#Jzswy(#jst`7yebP>WLP z(siM?70dy1;IBErbN7HcG{&rLV&3NSH=#>ZN5~txKz)RENTc3WteknnpZey8y!BMs-CPSy zxjA4ClsLfOhY-maDQ1HH(}Bis0e}I@K5)!?3Fd@~kzyu@D+ni4pirG|F`Up*54B&U zm-XBuD}QYA>e;U-KXUupvz=ey{7j4iW55{r>kOb~ zvm|?hHW~xQfHBZ8z~2WCWeke7VElAoh%Eqc2y+z7xtHJ=uNV|-L99TWgaRehX^Y_` z9CojML9rH;aB|vwI6c{EhvMSt*xyHYazW5WW55__Gq59rA3JCL%Y!pN)uyM3g`odjkv|!t1Op>8XVmUEJe>O1fTLjV9Bg zl7yG%{ny90<1Vk+FTBb*zb&iN zp0EuK=mGg;$f%$x-C(|ycWUKzaMbO!p4Z2FomaX8(z@{^R zn$42z3EF537z4&Y%>aKNJd`mgR)X=Dpvl0FTn@SZpM2l{H-qfT7%&F5 ziUHS8C+Qfsq_wqmb6jgZ^c>2_;HdV1qI6 Hrwn`o0Mu0V diff --git a/tests/huobi_usdt_swap_test/.DS_Store b/tests/huobi_usdt_swap_test/.DS_Store deleted file mode 100644 index 930710199cf48f3f3ac3c0fb855dfea895b4e29f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKu};J=40Vo^=n007Oenu_0}E3)l`pt~6?%sNaaAig#F~v?;4={4fW&X{9Gh}g z3t~cmY{_}4V<+xwR3}8_W_QaeQBFh|lyNjcGax+9+LMu5c+jOe&ZwcQ)x~tasC(XK z_>Bzkx7(&0I;UG&)5iC!8cFx@uw4iJB*RrSHbIK2fy*BeY32j~j^HudnZeM$L@}t1{nHU4cfHCmb89>cu z$#w*-H3p0UW1wY#{|_F@7!;#m`gNd5R{-D`<|OE+GaS&f0T>jcAS@6kp+E_Bdc|-O z4!c*spcn-uoLpKN^XSS>Zzyttgxx21azW5qW55{bGO#6&IoJQ=&+q@;AbT