unicex 0.13.17__py3-none-any.whl → 0.16.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unicex/__init__.py +36 -2
- unicex/_abc/exchange_info.py +1 -1
- unicex/_abc/uni_client.py +31 -1
- unicex/_abc/uni_websocket_manager.py +1 -1
- unicex/_base/client.py +7 -0
- unicex/_base/websocket.py +31 -9
- unicex/binance/adapter.py +7 -16
- unicex/binance/client.py +25 -25
- unicex/binance/uni_websocket_manager.py +6 -2
- unicex/bingx/__init__.py +27 -0
- unicex/bingx/adapter.py +284 -0
- unicex/bingx/client.py +521 -0
- unicex/bingx/exchange_info.py +22 -0
- unicex/bingx/uni_client.py +191 -0
- unicex/bingx/uni_websocket_manager.py +283 -0
- unicex/bingx/user_websocket.py +7 -0
- unicex/bingx/websocket_manager.py +118 -0
- unicex/bitget/adapter.py +2 -2
- unicex/bitget/client.py +64 -64
- unicex/bitget/uni_websocket_manager.py +27 -6
- unicex/bitget/websocket_manager.py +2 -4
- unicex/bybit/adapter.py +1 -0
- unicex/bybit/client.py +4 -4
- unicex/bybit/exchange_info.py +1 -1
- unicex/bybit/uni_websocket_manager.py +33 -4
- unicex/bybit/websocket_manager.py +8 -24
- unicex/enums.py +19 -3
- unicex/extra.py +37 -35
- unicex/gate/adapter.py +113 -0
- unicex/gate/client.py +9 -9
- unicex/gate/uni_client.py +1 -3
- unicex/gate/uni_websocket_manager.py +47 -9
- unicex/hyperliquid/adapter.py +1 -0
- unicex/hyperliquid/client.py +12 -12
- unicex/hyperliquid/uni_client.py +4 -7
- unicex/hyperliquid/uni_websocket_manager.py +6 -2
- unicex/kucoin/__init__.py +27 -0
- unicex/kucoin/adapter.py +181 -0
- unicex/kucoin/client.py +135 -0
- unicex/kucoin/exchange_info.py +50 -0
- unicex/kucoin/uni_client.py +208 -0
- unicex/kucoin/uni_websocket_manager.py +273 -0
- unicex/kucoin/user_websocket.py +7 -0
- unicex/kucoin/websocket_manager.py +11 -0
- unicex/mapper.py +62 -21
- unicex/mexc/adapter.py +104 -0
- unicex/mexc/client.py +7 -7
- unicex/mexc/uni_client.py +1 -3
- unicex/mexc/uni_websocket_manager.py +31 -9
- unicex/mexc/websocket_manager.py +27 -6
- unicex/okx/adapter.py +51 -0
- unicex/okx/client.py +15 -15
- unicex/okx/exchange_info.py +2 -2
- unicex/okx/uni_websocket_manager.py +50 -9
- unicex/okx/websocket_manager.py +119 -166
- unicex/types.py +14 -10
- unicex/utils.py +44 -1
- {unicex-0.13.17.dist-info → unicex-0.16.5.dist-info}/METADATA +7 -5
- unicex-0.16.5.dist-info/RECORD +109 -0
- unicex-0.13.17.dist-info/RECORD +0 -93
- {unicex-0.13.17.dist-info → unicex-0.16.5.dist-info}/WHEEL +0 -0
- {unicex-0.13.17.dist-info → unicex-0.16.5.dist-info}/licenses/LICENSE +0 -0
- {unicex-0.13.17.dist-info → unicex-0.16.5.dist-info}/top_level.txt +0 -0
unicex/extra.py
CHANGED
|
@@ -125,6 +125,10 @@ def normalize_ticker(raw_ticker: str) -> str:
|
|
|
125
125
|
.removesuffix("SWAP")
|
|
126
126
|
)
|
|
127
127
|
|
|
128
|
+
# Меняем постфикс USDTM на USDT
|
|
129
|
+
if ticker.endswith("USDTM"):
|
|
130
|
+
ticker = ticker.removesuffix("USDTM") + "USDT"
|
|
131
|
+
|
|
128
132
|
# Удаляем разделители
|
|
129
133
|
ticker = ticker.translate(str.maketrans("", "", "-_."))
|
|
130
134
|
|
|
@@ -149,6 +153,7 @@ def normalize_symbol(raw_ticker: str, quote: Literal["USDT", "USDC"] = "USDT") -
|
|
|
149
153
|
normalize_symbol("BTC") # "BTCUSDT"
|
|
150
154
|
normalize_symbol("btc_usdt_swap") # "BTCUSDT"
|
|
151
155
|
normalize_symbol("ETH", "USDC") # "ETHUSDC"
|
|
156
|
+
normalize_symbol("BTCUSDTM") # "BTCUSDT"
|
|
152
157
|
```
|
|
153
158
|
|
|
154
159
|
Параметры:
|
|
@@ -174,23 +179,22 @@ def generate_ex_link(exchange: Exchange, market_type: MarketType, symbol: str):
|
|
|
174
179
|
Возвращает:
|
|
175
180
|
`str`: Ссылка на биржу.
|
|
176
181
|
"""
|
|
177
|
-
symbol = normalize_symbol(symbol)
|
|
178
182
|
ticker = normalize_ticker(symbol)
|
|
179
183
|
if exchange == Exchange.BINANCE:
|
|
180
184
|
if market_type == MarketType.FUTURES:
|
|
181
|
-
return f"https://www.binance.com/en/futures/{
|
|
185
|
+
return f"https://www.binance.com/en/futures/{ticker}USDT"
|
|
182
186
|
else:
|
|
183
187
|
return f"https://www.binance.com/en/trade/{ticker}_USDT?type=spot"
|
|
184
188
|
elif exchange == Exchange.BYBIT:
|
|
185
189
|
if market_type == MarketType.FUTURES:
|
|
186
|
-
return f"https://www.bybit.com/trade/usdt/{
|
|
190
|
+
return f"https://www.bybit.com/trade/usdt/{ticker}USDT"
|
|
187
191
|
else:
|
|
188
192
|
return f"https://www.bybit.com/en/trade/spot/{ticker}/USDT"
|
|
189
193
|
elif exchange == Exchange.BITGET:
|
|
190
194
|
if market_type == MarketType.FUTURES:
|
|
191
|
-
return f"https://www.bitget.com/ru/futures/usdt/{
|
|
195
|
+
return f"https://www.bitget.com/ru/futures/usdt/{ticker}USDT"
|
|
192
196
|
else:
|
|
193
|
-
return f"https://www.bitget.com/ru/spot/{
|
|
197
|
+
return f"https://www.bitget.com/ru/spot/{ticker}USDT"
|
|
194
198
|
elif exchange == Exchange.OKX:
|
|
195
199
|
if market_type == MarketType.FUTURES:
|
|
196
200
|
return f"https://www.okx.com/ru/trade-swap/{ticker.lower()}-usdt-swap"
|
|
@@ -206,26 +210,21 @@ def generate_ex_link(exchange: Exchange, market_type: MarketType, symbol: str):
|
|
|
206
210
|
return f"https://www.gate.com/ru/futures/USDT/{ticker}_USDT"
|
|
207
211
|
else:
|
|
208
212
|
return f"https://www.gate.com/ru/trade/{ticker}_USDT"
|
|
209
|
-
elif exchange == Exchange.
|
|
210
|
-
if market_type == MarketType.FUTURES:
|
|
211
|
-
return f"https://www.xt.com/ru/futures/trade/{ticker.lower()}_usdt"
|
|
212
|
-
else:
|
|
213
|
-
return f"https://www.xt.com/ru/trade/{ticker.lower()}_usdt"
|
|
214
|
-
elif exchange == Exchange.BITUNIX:
|
|
213
|
+
elif exchange == Exchange.HYPERLIQUID:
|
|
215
214
|
if market_type == MarketType.FUTURES:
|
|
216
|
-
return f"https://
|
|
215
|
+
return f"https://app.hyperliquid.xyz/trade/{ticker}"
|
|
217
216
|
else:
|
|
218
|
-
return f"https://
|
|
219
|
-
elif exchange == Exchange.
|
|
217
|
+
return f"https://app.hyperliquid.xyz/trade/{ticker}/USDC"
|
|
218
|
+
elif exchange == Exchange.KUCOIN:
|
|
220
219
|
if market_type == MarketType.FUTURES:
|
|
221
|
-
return f"https://www.
|
|
220
|
+
return f"https://www.kucoin.com/trade/futures/{ticker}USDTM"
|
|
222
221
|
else:
|
|
223
|
-
return f"https://www.
|
|
224
|
-
elif exchange == Exchange.
|
|
222
|
+
return f"https://www.kucoin.com/trade/{ticker}-USDT"
|
|
223
|
+
elif exchange == Exchange.BINGX:
|
|
225
224
|
if market_type == MarketType.FUTURES:
|
|
226
|
-
return f"https://
|
|
225
|
+
return f"https://bingx.com/en/perpetual/{ticker}-USDT"
|
|
227
226
|
else:
|
|
228
|
-
return f"https://
|
|
227
|
+
return f"https://bingx.com/en/spot/{ticker}USDT"
|
|
229
228
|
else:
|
|
230
229
|
raise NotSupported(f"Exchange {exchange} is not supported")
|
|
231
230
|
|
|
@@ -260,32 +259,35 @@ def generate_cg_link(exchange: Exchange, market_type: MarketType, symbol: str) -
|
|
|
260
259
|
Возвращает:
|
|
261
260
|
`str`: Ссылка для CoinGlass.
|
|
262
261
|
"""
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
symbol = normalize_symbol(symbol)
|
|
262
|
+
ticker = normalize_ticker(symbol)
|
|
266
263
|
|
|
264
|
+
base_url = "https://www.coinglass.com/tv/ru"
|
|
267
265
|
if market_type == MarketType.FUTURES:
|
|
268
266
|
match exchange:
|
|
269
267
|
case Exchange.OKX:
|
|
270
|
-
return f"{base_url}/OKX_{
|
|
268
|
+
return f"{base_url}/OKX_{ticker}-USDT-SWAP"
|
|
271
269
|
case Exchange.MEXC:
|
|
272
|
-
return f"{base_url}/MEXC_{
|
|
270
|
+
return f"{base_url}/MEXC_{ticker}_USDT"
|
|
273
271
|
case Exchange.BITGET:
|
|
274
|
-
return f"{base_url}/Bitget_{
|
|
272
|
+
return f"{base_url}/Bitget_{ticker}USDT_UMCBL"
|
|
275
273
|
case Exchange.GATE:
|
|
276
|
-
return f"{base_url}/Gate_{
|
|
277
|
-
case Exchange.BITUNIX:
|
|
278
|
-
return f"{base_url}/Bitunix_{symbol}"
|
|
274
|
+
return f"{base_url}/Gate_{ticker}_USDT"
|
|
279
275
|
case Exchange.HYPERLIQUID:
|
|
280
|
-
return f"{base_url}/Hyperliquid_{
|
|
276
|
+
return f"{base_url}/Hyperliquid_{ticker}-USD"
|
|
277
|
+
case Exchange.KUCOIN:
|
|
278
|
+
return f"https://www.coinglass.com/tv/ru/KuCoin_{ticker}USDTM"
|
|
279
|
+
case Exchange.BINGX:
|
|
280
|
+
return f"https://www.coinglass.com/tv/ru/BingX_{ticker}-USDT"
|
|
281
281
|
case _:
|
|
282
|
-
return f"{base_url}/{exchange.capitalize()}_{
|
|
282
|
+
return f"{base_url}/{exchange.capitalize()}_{ticker}USDT"
|
|
283
283
|
else:
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
284
|
+
match exchange:
|
|
285
|
+
case Exchange.OKX:
|
|
286
|
+
return f"{base_url}/SPOT_{exchange.upper()}_{ticker}-USDT"
|
|
287
|
+
case Exchange.GATE:
|
|
288
|
+
return f"{base_url}/SPOT_{exchange.capitalize()}_{ticker}_USDT"
|
|
289
|
+
case _:
|
|
290
|
+
return f"{base_url}/SPOT_{exchange.capitalize()}_{ticker}USDT"
|
|
289
291
|
|
|
290
292
|
|
|
291
293
|
def make_humanreadable(value: float, locale: Literal["ru", "en"] = "ru") -> str:
|
unicex/gate/adapter.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import time
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
__all__ = ["Adapter"]
|
|
6
7
|
|
|
@@ -10,9 +11,12 @@ from unicex.types import (
|
|
|
10
11
|
OpenInterestItem,
|
|
11
12
|
TickerDailyDict,
|
|
12
13
|
TickerDailyItem,
|
|
14
|
+
TradeDict,
|
|
13
15
|
)
|
|
14
16
|
from unicex.utils import catch_adapter_errors, decorate_all_methods
|
|
15
17
|
|
|
18
|
+
from .exchange_info import ExchangeInfo
|
|
19
|
+
|
|
16
20
|
|
|
17
21
|
@decorate_all_methods(catch_adapter_errors)
|
|
18
22
|
class Adapter:
|
|
@@ -173,6 +177,115 @@ class Adapter:
|
|
|
173
177
|
item["contract"]: OpenInterestItem(
|
|
174
178
|
t=int(time.time() * 1000),
|
|
175
179
|
v=float(item["total_size"]) * float(item["quanto_multiplier"]),
|
|
180
|
+
u="coins",
|
|
176
181
|
)
|
|
177
182
|
for item in raw_data
|
|
178
183
|
}
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def klines_message(raw_msg: Any) -> list[KlineDict]:
|
|
187
|
+
"""Преобразует вебсокет-сообщение со свечами в унифицированный формат.
|
|
188
|
+
|
|
189
|
+
Параметры:
|
|
190
|
+
raw_msg (Any): Сырое сообщение с вебсокета.
|
|
191
|
+
|
|
192
|
+
Возвращает:
|
|
193
|
+
list[KlineDict]: Список свечей в унифицированном формате.
|
|
194
|
+
"""
|
|
195
|
+
data = raw_msg["result"]
|
|
196
|
+
return [
|
|
197
|
+
KlineDict(
|
|
198
|
+
s=data["n"].split("_", 1)[1], # XRP_USDT
|
|
199
|
+
t=int(data["t"]) * 1000,
|
|
200
|
+
o=float(data["o"]),
|
|
201
|
+
h=float(data["h"]),
|
|
202
|
+
l=float(data["l"]),
|
|
203
|
+
c=float(data["c"]),
|
|
204
|
+
v=float(data["a"]),
|
|
205
|
+
q=float(data["v"]),
|
|
206
|
+
T=None,
|
|
207
|
+
x=not data["w"], # w=False → свеча закрыта
|
|
208
|
+
)
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
@staticmethod
|
|
212
|
+
def futures_klines_message(raw_msg: Any) -> list[KlineDict]:
|
|
213
|
+
"""Преобразует вебсокет-сообщение со свечами в унифицированный формат.
|
|
214
|
+
|
|
215
|
+
Параметры:
|
|
216
|
+
raw_msg (Any): Сырое сообщение с вебсокета.
|
|
217
|
+
|
|
218
|
+
Возвращает:
|
|
219
|
+
list[KlineDict]: Список свечей в унифицированном формате.
|
|
220
|
+
"""
|
|
221
|
+
return [
|
|
222
|
+
KlineDict(
|
|
223
|
+
s=item["n"].split("_", 1)[1], # XRP_USDT
|
|
224
|
+
t=int(item["t"]) * 1000,
|
|
225
|
+
o=float(item["o"]),
|
|
226
|
+
h=float(item["h"]),
|
|
227
|
+
l=float(item["l"]),
|
|
228
|
+
c=float(item["c"]),
|
|
229
|
+
v=float(item["a"]),
|
|
230
|
+
q=float(item["v"]),
|
|
231
|
+
T=None,
|
|
232
|
+
x=not item["w"], # w=False → свеча закрыта
|
|
233
|
+
)
|
|
234
|
+
for item in sorted(
|
|
235
|
+
raw_msg["result"],
|
|
236
|
+
key=lambda x: int(x["t"]),
|
|
237
|
+
)
|
|
238
|
+
]
|
|
239
|
+
|
|
240
|
+
@staticmethod
|
|
241
|
+
def trades_message(raw_msg: Any) -> list[TradeDict]:
|
|
242
|
+
"""Преобразует вебсокет-сообщение со сделками в унифицированный формат.
|
|
243
|
+
|
|
244
|
+
Параметры:
|
|
245
|
+
raw_msg (Any): Сырое сообщение с вебсокета.
|
|
246
|
+
|
|
247
|
+
Возвращает:
|
|
248
|
+
list[TradeDict]: Список сделок в унифицированном формате.
|
|
249
|
+
"""
|
|
250
|
+
trade = raw_msg["result"]
|
|
251
|
+
return [
|
|
252
|
+
TradeDict(
|
|
253
|
+
t=trade["create_time_ms"],
|
|
254
|
+
s=trade["currency_pair"],
|
|
255
|
+
S=trade["side"].upper(),
|
|
256
|
+
p=float(trade["price"]),
|
|
257
|
+
v=float(trade["amount"]),
|
|
258
|
+
)
|
|
259
|
+
]
|
|
260
|
+
|
|
261
|
+
@staticmethod
|
|
262
|
+
def futures_trades_message(raw_msg: Any) -> list[TradeDict]:
|
|
263
|
+
"""Преобразует вебсокет-сообщение со сделками в унифицированный формат.
|
|
264
|
+
|
|
265
|
+
Параметры:
|
|
266
|
+
raw_msg (Any): Сырое сообщение с вебсокета.
|
|
267
|
+
|
|
268
|
+
Возвращает:
|
|
269
|
+
list[TradeDict]: Список сделок в унифицированном формате.
|
|
270
|
+
"""
|
|
271
|
+
return [
|
|
272
|
+
TradeDict(
|
|
273
|
+
t=item["create_time_ms"],
|
|
274
|
+
s=item["contract"],
|
|
275
|
+
S="BUY" if float(item["size"]) > 0 else "SELL",
|
|
276
|
+
p=float(item["price"]),
|
|
277
|
+
v=abs(float(item["size"])) * Adapter._get_contract_size(item["contract"]),
|
|
278
|
+
)
|
|
279
|
+
for item in sorted(
|
|
280
|
+
raw_msg["result"],
|
|
281
|
+
key=lambda x: x["create_time_ms"],
|
|
282
|
+
)
|
|
283
|
+
]
|
|
284
|
+
|
|
285
|
+
@staticmethod
|
|
286
|
+
def _get_contract_size(symbol: str) -> float:
|
|
287
|
+
"""Возвращает размер контракта для указанного символа тикера."""
|
|
288
|
+
try:
|
|
289
|
+
return ExchangeInfo.get_futures_ticker_info(symbol)["contract_size"] or 1
|
|
290
|
+
except: # noqa
|
|
291
|
+
return 1
|
unicex/gate/client.py
CHANGED
|
@@ -8,7 +8,7 @@ from typing import Any, Literal
|
|
|
8
8
|
|
|
9
9
|
from unicex._base import BaseClient
|
|
10
10
|
from unicex.exceptions import NotAuthorized
|
|
11
|
-
from unicex.types import RequestMethod
|
|
11
|
+
from unicex.types import NumberLike, RequestMethod
|
|
12
12
|
from unicex.utils import dict_to_query_string, filter_params
|
|
13
13
|
|
|
14
14
|
|
|
@@ -328,8 +328,8 @@ class Client(BaseClient):
|
|
|
328
328
|
async def cross_liquidate_orders(
|
|
329
329
|
self,
|
|
330
330
|
currency_pair: str,
|
|
331
|
-
amount:
|
|
332
|
-
price:
|
|
331
|
+
amount: NumberLike,
|
|
332
|
+
price: NumberLike,
|
|
333
333
|
text: str | None = None,
|
|
334
334
|
action_mode: str | None = None,
|
|
335
335
|
) -> dict:
|
|
@@ -353,11 +353,11 @@ class Client(BaseClient):
|
|
|
353
353
|
self,
|
|
354
354
|
currency_pair: str,
|
|
355
355
|
side: str,
|
|
356
|
-
amount:
|
|
356
|
+
amount: NumberLike,
|
|
357
357
|
text: str | None = None,
|
|
358
358
|
type: str | None = None,
|
|
359
359
|
account: str | None = None,
|
|
360
|
-
price:
|
|
360
|
+
price: NumberLike | None = None,
|
|
361
361
|
time_in_force: str | None = None,
|
|
362
362
|
iceberg: str | None = None,
|
|
363
363
|
auto_borrow: bool | None = None,
|
|
@@ -479,8 +479,8 @@ class Client(BaseClient):
|
|
|
479
479
|
order_id: str,
|
|
480
480
|
currency_pair: str | None = None,
|
|
481
481
|
account: str | None = None,
|
|
482
|
-
amount:
|
|
483
|
-
price:
|
|
482
|
+
amount: NumberLike | None = None,
|
|
483
|
+
price: NumberLike | None = None,
|
|
484
484
|
amend_text: str | None = None,
|
|
485
485
|
action_mode: str | None = None,
|
|
486
486
|
) -> dict:
|
|
@@ -1344,8 +1344,8 @@ class Client(BaseClient):
|
|
|
1344
1344
|
self,
|
|
1345
1345
|
settle: str,
|
|
1346
1346
|
order_id: str,
|
|
1347
|
-
size:
|
|
1348
|
-
price:
|
|
1347
|
+
size: NumberLike | None = None,
|
|
1348
|
+
price: NumberLike | None = None,
|
|
1349
1349
|
amend_text: str | None = None,
|
|
1350
1350
|
text: str | None = None,
|
|
1351
1351
|
) -> dict:
|
unicex/gate/uni_client.py
CHANGED
|
@@ -173,9 +173,7 @@ class UniClient(IUniClient[Client]):
|
|
|
173
173
|
raw_data = await self._client.futures_tickers(settle="usdt", contract=symbol)
|
|
174
174
|
items = raw_data if isinstance(raw_data, list) else [raw_data]
|
|
175
175
|
adapted_data = Adapter.funding_rate(raw_data=items) # type: ignore[reportArgumentType]
|
|
176
|
-
if symbol
|
|
177
|
-
return adapted_data[symbol]
|
|
178
|
-
return adapted_data
|
|
176
|
+
return adapted_data[symbol] if symbol else adapted_data
|
|
179
177
|
|
|
180
178
|
@overload
|
|
181
179
|
async def open_interest(self, symbol: str) -> OpenInterestItem: ...
|
|
@@ -5,7 +5,7 @@ from typing import Any, overload
|
|
|
5
5
|
|
|
6
6
|
from unicex._abc import IUniWebsocketManager
|
|
7
7
|
from unicex._base import Websocket
|
|
8
|
-
from unicex.enums import Timeframe
|
|
8
|
+
from unicex.enums import Exchange, MarketType, Timeframe
|
|
9
9
|
from unicex.types import LoggerLike
|
|
10
10
|
|
|
11
11
|
from .adapter import Adapter
|
|
@@ -20,18 +20,36 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
20
20
|
"""Реализация менеджера асинхронных унифицированных вебсокетов."""
|
|
21
21
|
|
|
22
22
|
def __init__(
|
|
23
|
-
self,
|
|
23
|
+
self,
|
|
24
|
+
client: Client | UniClient | None = None,
|
|
25
|
+
logger: LoggerLike | None = None,
|
|
26
|
+
**ws_kwargs: Any,
|
|
24
27
|
) -> None:
|
|
25
28
|
"""Инициализирует унифицированный менеджер вебсокетов.
|
|
26
29
|
|
|
27
30
|
Параметры:
|
|
28
31
|
client (`Client | UniClient | None`): Клиент Gateio или унифицированный клиент. Нужен для подключения к приватным топикам.
|
|
29
32
|
logger (`LoggerLike | None`): Логгер для записи логов.
|
|
33
|
+
ws_kwargs (`dict[str, Any]`): Дополнительные параметры инициализации, которые будут переданы WebsocketManager/Websocket.
|
|
30
34
|
"""
|
|
31
35
|
super().__init__(client=client, logger=logger)
|
|
32
|
-
self._websocket_manager = WebsocketManager(self._client) # type: ignore
|
|
36
|
+
self._websocket_manager = WebsocketManager(self._client, **ws_kwargs) # type: ignore
|
|
33
37
|
self._adapter = Adapter()
|
|
34
38
|
|
|
39
|
+
def _normalize_symbols(
|
|
40
|
+
self,
|
|
41
|
+
symbol: str | None,
|
|
42
|
+
symbols: Sequence[str] | None,
|
|
43
|
+
) -> list[str]:
|
|
44
|
+
"""Преобразует параметры symbol/symbols в список тикеров."""
|
|
45
|
+
if symbol and symbols:
|
|
46
|
+
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
47
|
+
if symbol:
|
|
48
|
+
return [symbol]
|
|
49
|
+
if symbols:
|
|
50
|
+
return list(symbols)
|
|
51
|
+
raise ValueError("Either symbol or symbols must be provided")
|
|
52
|
+
|
|
35
53
|
@overload
|
|
36
54
|
def klines(
|
|
37
55
|
self,
|
|
@@ -72,7 +90,14 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
72
90
|
Возвращает:
|
|
73
91
|
`Websocket`: Экземпляр вебсокета для управления соединением.
|
|
74
92
|
"""
|
|
75
|
-
|
|
93
|
+
tickers = self._normalize_symbols(symbol, symbols)
|
|
94
|
+
|
|
95
|
+
wrapper = self._make_wrapper(self._adapter.klines_message, callback)
|
|
96
|
+
return self._websocket_manager.candlesticks(
|
|
97
|
+
callback=wrapper,
|
|
98
|
+
interval=timeframe.to_exchange_format(Exchange.GATE, MarketType.SPOT),
|
|
99
|
+
symbols=tickers,
|
|
100
|
+
)
|
|
76
101
|
|
|
77
102
|
@overload
|
|
78
103
|
def futures_klines(
|
|
@@ -114,7 +139,14 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
114
139
|
Возвращает:
|
|
115
140
|
`Websocket`: Экземпляр вебсокета.
|
|
116
141
|
"""
|
|
117
|
-
|
|
142
|
+
tickers = self._normalize_symbols(symbol, symbols)
|
|
143
|
+
|
|
144
|
+
wrapper = self._make_wrapper(self._adapter.futures_klines_message, callback)
|
|
145
|
+
return self._websocket_manager.futures_candlesticks(
|
|
146
|
+
callback=wrapper,
|
|
147
|
+
interval=timeframe.to_exchange_format(Exchange.GATE, MarketType.FUTURES),
|
|
148
|
+
symbols=tickers,
|
|
149
|
+
)
|
|
118
150
|
|
|
119
151
|
@overload
|
|
120
152
|
def trades(
|
|
@@ -152,7 +184,10 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
152
184
|
Возвращает:
|
|
153
185
|
`Websocket`: Экземпляр вебсокета.
|
|
154
186
|
"""
|
|
155
|
-
|
|
187
|
+
tickers = self._normalize_symbols(symbol, symbols)
|
|
188
|
+
|
|
189
|
+
wrapper = self._make_wrapper(self._adapter.trades_message, callback)
|
|
190
|
+
return self._websocket_manager.trades(callback=wrapper, symbols=tickers)
|
|
156
191
|
|
|
157
192
|
@overload
|
|
158
193
|
def aggtrades(
|
|
@@ -190,7 +225,7 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
190
225
|
Возвращает:
|
|
191
226
|
`Websocket`: Экземпляр вебсокета.
|
|
192
227
|
"""
|
|
193
|
-
|
|
228
|
+
return self.trades(callback=callback, symbol=symbol, symbols=symbols) # type: ignore[reportCallIssue]
|
|
194
229
|
|
|
195
230
|
@overload
|
|
196
231
|
def futures_trades(
|
|
@@ -228,7 +263,10 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
228
263
|
Возвращает:
|
|
229
264
|
`Websocket`: Экземпляр вебсокета.
|
|
230
265
|
"""
|
|
231
|
-
|
|
266
|
+
tickers = self._normalize_symbols(symbol, symbols)
|
|
267
|
+
|
|
268
|
+
wrapper = self._make_wrapper(self._adapter.futures_trades_message, callback)
|
|
269
|
+
return self._websocket_manager.futures_trades(callback=wrapper, symbols=tickers)
|
|
232
270
|
|
|
233
271
|
@overload
|
|
234
272
|
def futures_aggtrades(
|
|
@@ -266,4 +304,4 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
266
304
|
Возвращает:
|
|
267
305
|
`Websocket`: Экземпляр вебсокета.
|
|
268
306
|
"""
|
|
269
|
-
|
|
307
|
+
return self.futures_trades(callback=callback, symbol=symbol, symbols=symbols) # type: ignore[reportCallIssue]
|
unicex/hyperliquid/adapter.py
CHANGED
unicex/hyperliquid/client.py
CHANGED
|
@@ -13,7 +13,7 @@ from eth_utils.crypto import keccak
|
|
|
13
13
|
|
|
14
14
|
from unicex._base import BaseClient
|
|
15
15
|
from unicex.exceptions import NotAuthorized
|
|
16
|
-
from unicex.types import LoggerLike
|
|
16
|
+
from unicex.types import LoggerLike, NumberLike
|
|
17
17
|
from unicex.utils import filter_params
|
|
18
18
|
|
|
19
19
|
# Authentication
|
|
@@ -1184,11 +1184,11 @@ class Client(BaseClient):
|
|
|
1184
1184
|
self,
|
|
1185
1185
|
asset: int,
|
|
1186
1186
|
is_buy: bool,
|
|
1187
|
-
size:
|
|
1187
|
+
size: NumberLike,
|
|
1188
1188
|
reduce_only: bool,
|
|
1189
1189
|
order_type: Literal["limit", "trigger"],
|
|
1190
1190
|
order_body: dict,
|
|
1191
|
-
price:
|
|
1191
|
+
price: NumberLike | None = None,
|
|
1192
1192
|
client_order_id: str | None = None,
|
|
1193
1193
|
grouping: Literal["na", "normalTpsl", "positionTpsl"] = "na",
|
|
1194
1194
|
builder_address: str | None = None,
|
|
@@ -1478,8 +1478,8 @@ class Client(BaseClient):
|
|
|
1478
1478
|
order_id: int | str,
|
|
1479
1479
|
asset: int,
|
|
1480
1480
|
is_buy: bool,
|
|
1481
|
-
price:
|
|
1482
|
-
size:
|
|
1481
|
+
price: NumberLike,
|
|
1482
|
+
size: NumberLike,
|
|
1483
1483
|
reduce_only: bool,
|
|
1484
1484
|
order_type: Literal["limit", "trigger"],
|
|
1485
1485
|
order_body: dict[str, Any],
|
|
@@ -1668,7 +1668,7 @@ class Client(BaseClient):
|
|
|
1668
1668
|
hyperliquid_chain: Literal["Mainnet", "Testnet"],
|
|
1669
1669
|
signature_chain_id: str,
|
|
1670
1670
|
destination: str,
|
|
1671
|
-
amount:
|
|
1671
|
+
amount: NumberLike,
|
|
1672
1672
|
time_ms: int,
|
|
1673
1673
|
nonce: int | None = None,
|
|
1674
1674
|
) -> dict:
|
|
@@ -1711,7 +1711,7 @@ class Client(BaseClient):
|
|
|
1711
1711
|
signature_chain_id: str,
|
|
1712
1712
|
destination: str,
|
|
1713
1713
|
token: str,
|
|
1714
|
-
amount:
|
|
1714
|
+
amount: NumberLike,
|
|
1715
1715
|
time_ms: int,
|
|
1716
1716
|
nonce: int | None = None,
|
|
1717
1717
|
) -> dict:
|
|
@@ -1753,7 +1753,7 @@ class Client(BaseClient):
|
|
|
1753
1753
|
self,
|
|
1754
1754
|
hyperliquid_chain: Literal["Mainnet", "Testnet"],
|
|
1755
1755
|
signature_chain_id: str,
|
|
1756
|
-
amount:
|
|
1756
|
+
amount: NumberLike,
|
|
1757
1757
|
time_ms: int,
|
|
1758
1758
|
destination: str,
|
|
1759
1759
|
nonce: int | None = None,
|
|
@@ -1795,7 +1795,7 @@ class Client(BaseClient):
|
|
|
1795
1795
|
self,
|
|
1796
1796
|
hyperliquid_chain: Literal["Mainnet", "Testnet"],
|
|
1797
1797
|
signature_chain_id: str,
|
|
1798
|
-
amount:
|
|
1798
|
+
amount: NumberLike,
|
|
1799
1799
|
to_perp: bool,
|
|
1800
1800
|
subaccount: str | None = None,
|
|
1801
1801
|
) -> dict:
|
|
@@ -1845,7 +1845,7 @@ class Client(BaseClient):
|
|
|
1845
1845
|
source_dex: str,
|
|
1846
1846
|
destination_dex: str,
|
|
1847
1847
|
token: str,
|
|
1848
|
-
amount:
|
|
1848
|
+
amount: NumberLike,
|
|
1849
1849
|
from_subaccount: str,
|
|
1850
1850
|
nonce_value: int,
|
|
1851
1851
|
nonce: int | None = None,
|
|
@@ -2015,7 +2015,7 @@ class Client(BaseClient):
|
|
|
2015
2015
|
self,
|
|
2016
2016
|
vault_address: str,
|
|
2017
2017
|
is_deposit: bool,
|
|
2018
|
-
usd:
|
|
2018
|
+
usd: NumberLike,
|
|
2019
2019
|
nonce: int | None = None,
|
|
2020
2020
|
expires_after: int | None = None,
|
|
2021
2021
|
signing_vault_address: str | None = None,
|
|
@@ -2147,7 +2147,7 @@ class Client(BaseClient):
|
|
|
2147
2147
|
self,
|
|
2148
2148
|
asset: int,
|
|
2149
2149
|
is_buy: bool,
|
|
2150
|
-
size:
|
|
2150
|
+
size: NumberLike,
|
|
2151
2151
|
reduce_only: bool,
|
|
2152
2152
|
minutes: int,
|
|
2153
2153
|
randomize: bool,
|
unicex/hyperliquid/uni_client.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
__all__ = ["UniClient"]
|
|
2
2
|
|
|
3
|
-
import time
|
|
4
3
|
from typing import Self, overload
|
|
5
4
|
|
|
6
5
|
import aiohttp
|
|
@@ -217,15 +216,14 @@ class UniClient(IUniClient[Client]):
|
|
|
217
216
|
list[KlineDict]: Список свечей для тикера.
|
|
218
217
|
"""
|
|
219
218
|
if not limit and not all([start_time, end_time]):
|
|
220
|
-
raise ValueError("limit
|
|
219
|
+
raise ValueError("limit or (start_time and end_time) must be provided")
|
|
221
220
|
|
|
222
221
|
if limit: # Перезаписываем start_time и end_time если указан limit, т.к. по умолчанию HyperLiquid не принимают этот параметр
|
|
223
222
|
if not isinstance(interval, Timeframe):
|
|
224
223
|
raise ValueError("interval must be a Timeframe if limit param provided")
|
|
225
|
-
end_time =
|
|
226
|
-
start_time = end_time - (limit * interval.to_seconds * 1000) # type: ignore[reportOptionalOperand]
|
|
224
|
+
start_time, end_time = self.limit_to_start_and_end_time(interval, limit)
|
|
227
225
|
interval = (
|
|
228
|
-
interval.to_exchange_format(Exchange.HYPERLIQUID
|
|
226
|
+
interval.to_exchange_format(Exchange.HYPERLIQUID)
|
|
229
227
|
if isinstance(interval, Timeframe)
|
|
230
228
|
else interval
|
|
231
229
|
)
|
|
@@ -263,8 +261,7 @@ class UniClient(IUniClient[Client]):
|
|
|
263
261
|
if limit: # Перезаписываем start_time и end_time если указан limit, т.к. по умолчанию HyperLiquid не принимают этот параметр
|
|
264
262
|
if not isinstance(interval, Timeframe):
|
|
265
263
|
raise ValueError("interval must be a Timeframe if limit param provided")
|
|
266
|
-
end_time =
|
|
267
|
-
start_time = end_time - (limit * interval.to_seconds * 1000) # type: ignore[reportOptionalOperand]
|
|
264
|
+
start_time, end_time = self.limit_to_start_and_end_time(interval, limit)
|
|
268
265
|
interval = (
|
|
269
266
|
interval.to_exchange_format(Exchange.HYPERLIQUID, MarketType.FUTURES)
|
|
270
267
|
if isinstance(interval, Timeframe)
|
|
@@ -20,16 +20,20 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
20
20
|
"""Реализация менеджера асинхронных унифицированных вебсокетов."""
|
|
21
21
|
|
|
22
22
|
def __init__(
|
|
23
|
-
self,
|
|
23
|
+
self,
|
|
24
|
+
client: Client | UniClient | None = None,
|
|
25
|
+
logger: LoggerLike | None = None,
|
|
26
|
+
**ws_kwargs: Any,
|
|
24
27
|
) -> None:
|
|
25
28
|
"""Инициализирует унифицированный менеджер вебсокетов.
|
|
26
29
|
|
|
27
30
|
Параметры:
|
|
28
31
|
client (`Client | UniClient | None`): Клиент Hyperliquid или унифицированный клиент. Нужен для подключения к приватным топикам.
|
|
29
32
|
logger (`LoggerLike | None`): Логгер для записи логов.
|
|
33
|
+
ws_kwargs (`dict[str, Any]`): Дополнительные параметры инициализации, которые будут переданы WebsocketManager/Websocket.
|
|
30
34
|
"""
|
|
31
35
|
super().__init__(client=client, logger=logger)
|
|
32
|
-
self._websocket_manager = WebsocketManager(self._client) # type: ignore
|
|
36
|
+
self._websocket_manager = WebsocketManager(self._client, **ws_kwargs) # type: ignore
|
|
33
37
|
self._adapter = Adapter()
|
|
34
38
|
|
|
35
39
|
@overload
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Пакет, содержащий реализации клиентов и менеджеров для работы с биржей Kucoin."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"Client",
|
|
5
|
+
"UniClient",
|
|
6
|
+
"UserWebsocket",
|
|
7
|
+
"WebsocketManager",
|
|
8
|
+
"UniWebsocketManager",
|
|
9
|
+
"ExchangeInfo",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
from .client import Client
|
|
13
|
+
from .exchange_info import ExchangeInfo
|
|
14
|
+
from .uni_client import UniClient
|
|
15
|
+
from .uni_websocket_manager import UniWebsocketManager
|
|
16
|
+
from .user_websocket import UserWebsocket
|
|
17
|
+
from .websocket_manager import WebsocketManager
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def load_exchange_info() -> None:
|
|
21
|
+
"""Загружает информацию о бирже Kucoin."""
|
|
22
|
+
await ExchangeInfo.load_exchange_info()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def start_exchange_info(parse_interval_seconds: int = 60 * 60) -> None:
|
|
26
|
+
"""Запускает процесс обновления информации о бирже Kucoin."""
|
|
27
|
+
await ExchangeInfo.start(parse_interval_seconds)
|