unicex 0.15.2__py3-none-any.whl → 0.16.0__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/_abc/exchange_info.py +1 -1
- unicex/_abc/uni_client.py +1 -1
- unicex/_abc/uni_websocket_manager.py +1 -1
- unicex/_base/client.py +7 -0
- unicex/binance/adapter.py +5 -1
- unicex/bingx/adapter.py +1 -1
- unicex/bingx/uni_websocket_manager.py +2 -2
- unicex/bingx/websocket_manager.py +2 -4
- unicex/bitget/adapter.py +1 -0
- unicex/bitget/uni_websocket_manager.py +21 -4
- unicex/bitget/websocket_manager.py +2 -4
- unicex/bybit/adapter.py +1 -0
- unicex/bybit/websocket_manager.py +7 -24
- unicex/extra.py +23 -23
- unicex/gate/adapter.py +103 -0
- unicex/gate/uni_websocket_manager.py +41 -7
- unicex/hyperliquid/adapter.py +1 -0
- unicex/kucoin/adapter.py +4 -9
- unicex/kucoin/client.py +13 -0
- unicex/kucoin/uni_client.py +2 -14
- unicex/mexc/_spot_ws_proto/__init__.py +335 -335
- unicex/mexc/adapter.py +104 -0
- unicex/mexc/uni_websocket_manager.py +25 -7
- unicex/mexc/websocket_manager.py +26 -5
- unicex/okx/adapter.py +51 -0
- unicex/okx/uni_websocket_manager.py +44 -7
- unicex/types.py +6 -3
- unicex/utils.py +22 -1
- {unicex-0.15.2.dist-info → unicex-0.16.0.dist-info}/METADATA +5 -5
- {unicex-0.15.2.dist-info → unicex-0.16.0.dist-info}/RECORD +33 -33
- {unicex-0.15.2.dist-info → unicex-0.16.0.dist-info}/WHEEL +0 -0
- {unicex-0.15.2.dist-info → unicex-0.16.0.dist-info}/licenses/LICENSE +0 -0
- {unicex-0.15.2.dist-info → unicex-0.16.0.dist-info}/top_level.txt +0 -0
unicex/_abc/exchange_info.py
CHANGED
|
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class IExchangeInfo(ABC):
|
|
20
|
-
"""Интерфейс
|
|
20
|
+
"""Интерфейс класса, который обновляет информацию о правилах торговли на бирже."""
|
|
21
21
|
|
|
22
22
|
_loaded: bool
|
|
23
23
|
"""Флаг, указывающий, была ли информация о бирже загружена."""
|
unicex/_abc/uni_client.py
CHANGED
|
@@ -16,7 +16,7 @@ type CallbackType = Callable[[Any], Awaitable[None]]
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class IUniWebsocketManager(ABC):
|
|
19
|
-
"""Интерфейс менеджера
|
|
19
|
+
"""Интерфейс менеджера унифицированных вебсокетов."""
|
|
20
20
|
|
|
21
21
|
def __init__(
|
|
22
22
|
self, client: BaseClient | IUniClient | None = None, logger: LoggerLike | None = None
|
unicex/_base/client.py
CHANGED
|
@@ -203,6 +203,9 @@ class BaseClient:
|
|
|
203
203
|
response_json=response_json,
|
|
204
204
|
) from None
|
|
205
205
|
|
|
206
|
+
# Валидирование ответа в конерктной реализации клиента
|
|
207
|
+
self._validate_response(response_json)
|
|
208
|
+
|
|
206
209
|
# Логирование ответа
|
|
207
210
|
try:
|
|
208
211
|
self._logger.debug(
|
|
@@ -212,3 +215,7 @@ class BaseClient:
|
|
|
212
215
|
self._logger.error(f"Error while logging response: {e}")
|
|
213
216
|
|
|
214
217
|
return response_json
|
|
218
|
+
|
|
219
|
+
def _validate_response(self, response_json: dict[str, Any]) -> None:
|
|
220
|
+
"""Проверка ответа API на ошибки биржи. Переопределяется в клиентах конкретных бирж."""
|
|
221
|
+
return None
|
unicex/binance/adapter.py
CHANGED
|
@@ -115,7 +115,11 @@ class Adapter:
|
|
|
115
115
|
Возвращает:
|
|
116
116
|
OpenInterestItem: Словарь со временем и объемом открытого интереса в монетах.
|
|
117
117
|
"""
|
|
118
|
-
return OpenInterestItem(
|
|
118
|
+
return OpenInterestItem(
|
|
119
|
+
t=raw_data["time"],
|
|
120
|
+
v=float(raw_data["openInterest"]),
|
|
121
|
+
u="coins",
|
|
122
|
+
)
|
|
119
123
|
|
|
120
124
|
@staticmethod
|
|
121
125
|
def klines_message(raw_msg: dict) -> list[KlineDict]:
|
unicex/bingx/adapter.py
CHANGED
|
@@ -68,7 +68,7 @@ class Adapter:
|
|
|
68
68
|
OpenInterestItem: Словарь со временем и объемом открытого интереса в монетах.
|
|
69
69
|
"""
|
|
70
70
|
item = raw_data["data"]
|
|
71
|
-
return OpenInterestItem(t=int(item["time"]), v=float(item["openInterest"]))
|
|
71
|
+
return OpenInterestItem(t=int(item["time"]), v=float(item["openInterest"]), u="usd")
|
|
72
72
|
|
|
73
73
|
@staticmethod
|
|
74
74
|
def funding_rate(raw_data: dict) -> dict[str, float]:
|
|
@@ -199,7 +199,7 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
199
199
|
Возвращает:
|
|
200
200
|
`Websocket`: Экземпляр вебсокета.
|
|
201
201
|
"""
|
|
202
|
-
|
|
202
|
+
return self.trades(callback, symbol=symbol, symbols=symbols) # type: ignore
|
|
203
203
|
|
|
204
204
|
@overload
|
|
205
205
|
def futures_trades(
|
|
@@ -280,4 +280,4 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
280
280
|
Возвращает:
|
|
281
281
|
`Websocket`: Экземпляр вебсокета.
|
|
282
282
|
"""
|
|
283
|
-
|
|
283
|
+
return self.futures_trades(callback, symbol=symbol, symbols=symbols) # type: ignore
|
|
@@ -9,6 +9,7 @@ from typing import Any, Literal
|
|
|
9
9
|
import orjson
|
|
10
10
|
|
|
11
11
|
from unicex._base import Websocket
|
|
12
|
+
from unicex.utils import validate_single_symbol_args
|
|
12
13
|
|
|
13
14
|
from .client import Client
|
|
14
15
|
|
|
@@ -101,10 +102,7 @@ class WebsocketManager:
|
|
|
101
102
|
Возвращает:
|
|
102
103
|
`Websocket`: Объект для управления вебсокет соединением.
|
|
103
104
|
"""
|
|
104
|
-
|
|
105
|
-
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
106
|
-
if not (symbol or symbols):
|
|
107
|
-
raise ValueError("Either symbol or symbols must be provided")
|
|
105
|
+
validate_single_symbol_args(symbol, symbols)
|
|
108
106
|
|
|
109
107
|
tickers = [symbol] if symbol else symbols
|
|
110
108
|
data_types = [f"{ticker.upper()}@trade" for ticker in tickers] # type: ignore[arg-type]
|
unicex/bitget/adapter.py
CHANGED
|
@@ -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
|
|
@@ -76,7 +76,17 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
76
76
|
Возвращает:
|
|
77
77
|
`Websocket`: Экземпляр вебсокета для управления соединением.
|
|
78
78
|
"""
|
|
79
|
-
|
|
79
|
+
wrapper = self._make_wrapper(self._adapter.klines_message, callback)
|
|
80
|
+
return self._websocket_manager.candlestick(
|
|
81
|
+
callback=wrapper,
|
|
82
|
+
market_type="SPOT",
|
|
83
|
+
symbol=symbol,
|
|
84
|
+
symbols=symbols,
|
|
85
|
+
interval=timeframe.to_exchange_format(
|
|
86
|
+
Exchange.BITGET,
|
|
87
|
+
MarketType.FUTURES, # Тут пришлось поставить Futures, потому что:
|
|
88
|
+
), # кто бы мог подумать, что у Bitget на споте для вебсокетов и HTTP запросов совершенно разные перечисления. Тупые ублюдки.
|
|
89
|
+
)
|
|
80
90
|
|
|
81
91
|
@overload
|
|
82
92
|
def futures_klines(
|
|
@@ -118,7 +128,14 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
118
128
|
Возвращает:
|
|
119
129
|
`Websocket`: Экземпляр вебсокета.
|
|
120
130
|
"""
|
|
121
|
-
|
|
131
|
+
wrapper = self._make_wrapper(self._adapter.klines_message, callback)
|
|
132
|
+
return self._websocket_manager.candlestick(
|
|
133
|
+
callback=wrapper,
|
|
134
|
+
market_type="USDT-FUTURES",
|
|
135
|
+
symbol=symbol,
|
|
136
|
+
symbols=symbols,
|
|
137
|
+
interval=timeframe.to_exchange_format(Exchange.BITGET, MarketType.FUTURES),
|
|
138
|
+
)
|
|
122
139
|
|
|
123
140
|
@overload
|
|
124
141
|
def trades(
|
|
@@ -276,4 +293,4 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
276
293
|
Возвращает:
|
|
277
294
|
`Websocket`: Экземпляр вебсокета.
|
|
278
295
|
"""
|
|
279
|
-
|
|
296
|
+
return self.futures_trades(callback=callback, symbol=symbol, symbols=symbols) # type: ignore[reportCallIssue]
|
|
@@ -6,6 +6,7 @@ from collections.abc import Awaitable, Callable, Sequence
|
|
|
6
6
|
from typing import Any, Literal
|
|
7
7
|
|
|
8
8
|
from unicex._base import Websocket
|
|
9
|
+
from unicex.utils import validate_single_symbol_args
|
|
9
10
|
|
|
10
11
|
from .client import Client
|
|
11
12
|
|
|
@@ -46,10 +47,7 @@ class WebsocketManager:
|
|
|
46
47
|
Возвращает:
|
|
47
48
|
`str`: JSON-строка с сообщением для подписки на вебсокет.
|
|
48
49
|
"""
|
|
49
|
-
|
|
50
|
-
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
51
|
-
if not (symbol or symbols):
|
|
52
|
-
raise ValueError("Either symbol or symbols must be provided")
|
|
50
|
+
validate_single_symbol_args(symbol, symbols)
|
|
53
51
|
|
|
54
52
|
tickers = [symbol] if symbol else symbols
|
|
55
53
|
streams: list[dict] = [
|
unicex/bybit/adapter.py
CHANGED
|
@@ -6,6 +6,7 @@ from collections.abc import Awaitable, Callable, Sequence
|
|
|
6
6
|
from typing import Any, Literal
|
|
7
7
|
|
|
8
8
|
from unicex._base import Websocket
|
|
9
|
+
from unicex.utils import validate_single_symbol_args
|
|
9
10
|
|
|
10
11
|
from .client import Client
|
|
11
12
|
|
|
@@ -108,10 +109,7 @@ class WebsocketManager:
|
|
|
108
109
|
Возвращает:
|
|
109
110
|
`Websocket`: Объект для управления вебсокет соединением.
|
|
110
111
|
"""
|
|
111
|
-
|
|
112
|
-
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
113
|
-
if not (symbol or symbols):
|
|
114
|
-
raise ValueError("Either symbol or symbols must be provided")
|
|
112
|
+
validate_single_symbol_args(symbol, symbols)
|
|
115
113
|
|
|
116
114
|
tickers = [symbol] if symbol else symbols
|
|
117
115
|
topics = [f"orderbook.{depth}.{ticker.upper()}" for ticker in tickers] # type: ignore
|
|
@@ -152,10 +150,7 @@ class WebsocketManager:
|
|
|
152
150
|
Возвращает:
|
|
153
151
|
`Websocket`: Объект для управления вебсокет соединением.
|
|
154
152
|
"""
|
|
155
|
-
|
|
156
|
-
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
157
|
-
if not (symbol or symbols):
|
|
158
|
-
raise ValueError("Either symbol or symbols must be provided")
|
|
153
|
+
validate_single_symbol_args(symbol, symbols)
|
|
159
154
|
|
|
160
155
|
tickers = [symbol] if symbol else symbols
|
|
161
156
|
topics = [f"kline.{interval}.{ticker.upper()}" for ticker in tickers] # type: ignore
|
|
@@ -192,10 +187,7 @@ class WebsocketManager:
|
|
|
192
187
|
Возвращает:
|
|
193
188
|
`Websocket`: Объект для управления вебсокет соединением.
|
|
194
189
|
"""
|
|
195
|
-
|
|
196
|
-
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
197
|
-
if not (symbol or symbols):
|
|
198
|
-
raise ValueError("Either symbol or symbols must be provided")
|
|
190
|
+
validate_single_symbol_args(symbol, symbols)
|
|
199
191
|
|
|
200
192
|
tickers = [symbol] if symbol else symbols
|
|
201
193
|
topics = [f"publicTrade.{ticker.upper()}" for ticker in tickers] # type: ignore
|
|
@@ -232,10 +224,7 @@ class WebsocketManager:
|
|
|
232
224
|
Возвращает:
|
|
233
225
|
`Websocket`: Объект для управления вебсокет соединением.
|
|
234
226
|
"""
|
|
235
|
-
|
|
236
|
-
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
237
|
-
if not (symbol or symbols):
|
|
238
|
-
raise ValueError("Either symbol or symbols must be provided")
|
|
227
|
+
validate_single_symbol_args(symbol, symbols)
|
|
239
228
|
|
|
240
229
|
tickers = [symbol] if symbol else symbols
|
|
241
230
|
topics = [f"tickers.{ticker.upper()}" for ticker in tickers] # type: ignore
|
|
@@ -280,10 +269,7 @@ class WebsocketManager:
|
|
|
280
269
|
stacklevel=2,
|
|
281
270
|
)
|
|
282
271
|
|
|
283
|
-
|
|
284
|
-
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
285
|
-
if not (symbol or symbols):
|
|
286
|
-
raise ValueError("Either symbol or symbols must be provided")
|
|
272
|
+
validate_single_symbol_args(symbol, symbols)
|
|
287
273
|
|
|
288
274
|
tickers = [symbol] if symbol else symbols
|
|
289
275
|
topics = [f"liquidation.{ticker.upper()}" for ticker in tickers] # type: ignore
|
|
@@ -320,10 +306,7 @@ class WebsocketManager:
|
|
|
320
306
|
Возвращает:
|
|
321
307
|
`Websocket`: Объект для управления вебсокет соединением.
|
|
322
308
|
"""
|
|
323
|
-
|
|
324
|
-
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
325
|
-
if not (symbol or symbols):
|
|
326
|
-
raise ValueError("Either symbol or symbols must be provided")
|
|
309
|
+
validate_single_symbol_args(symbol, symbols)
|
|
327
310
|
|
|
328
311
|
tickers = [symbol] if symbol else symbols
|
|
329
312
|
topics = [f"allLiquidation.{ticker.upper()}" for ticker in tickers] # type: ignore
|
unicex/extra.py
CHANGED
|
@@ -179,23 +179,22 @@ def generate_ex_link(exchange: Exchange, market_type: MarketType, symbol: str):
|
|
|
179
179
|
Возвращает:
|
|
180
180
|
`str`: Ссылка на биржу.
|
|
181
181
|
"""
|
|
182
|
-
symbol = normalize_symbol(symbol)
|
|
183
182
|
ticker = normalize_ticker(symbol)
|
|
184
183
|
if exchange == Exchange.BINANCE:
|
|
185
184
|
if market_type == MarketType.FUTURES:
|
|
186
|
-
return f"https://www.binance.com/en/futures/{
|
|
185
|
+
return f"https://www.binance.com/en/futures/{ticker}USDT"
|
|
187
186
|
else:
|
|
188
187
|
return f"https://www.binance.com/en/trade/{ticker}_USDT?type=spot"
|
|
189
188
|
elif exchange == Exchange.BYBIT:
|
|
190
189
|
if market_type == MarketType.FUTURES:
|
|
191
|
-
return f"https://www.bybit.com/trade/usdt/{
|
|
190
|
+
return f"https://www.bybit.com/trade/usdt/{ticker}USDT"
|
|
192
191
|
else:
|
|
193
192
|
return f"https://www.bybit.com/en/trade/spot/{ticker}/USDT"
|
|
194
193
|
elif exchange == Exchange.BITGET:
|
|
195
194
|
if market_type == MarketType.FUTURES:
|
|
196
|
-
return f"https://www.bitget.com/ru/futures/usdt/{
|
|
195
|
+
return f"https://www.bitget.com/ru/futures/usdt/{ticker}USDT"
|
|
197
196
|
else:
|
|
198
|
-
return f"https://www.bitget.com/ru/spot/{
|
|
197
|
+
return f"https://www.bitget.com/ru/spot/{ticker}USDT"
|
|
199
198
|
elif exchange == Exchange.OKX:
|
|
200
199
|
if market_type == MarketType.FUTURES:
|
|
201
200
|
return f"https://www.okx.com/ru/trade-swap/{ticker.lower()}-usdt-swap"
|
|
@@ -218,14 +217,14 @@ def generate_ex_link(exchange: Exchange, market_type: MarketType, symbol: str):
|
|
|
218
217
|
return f"https://app.hyperliquid.xyz/trade/{ticker}/USDC"
|
|
219
218
|
elif exchange == Exchange.KUCOIN:
|
|
220
219
|
if market_type == MarketType.FUTURES:
|
|
221
|
-
return f"https://www.kucoin.com/trade/futures/{
|
|
220
|
+
return f"https://www.kucoin.com/trade/futures/{ticker}USDTM"
|
|
222
221
|
else:
|
|
223
222
|
return f"https://www.kucoin.com/trade/{ticker}-USDT"
|
|
224
223
|
elif exchange == Exchange.BINGX:
|
|
225
224
|
if market_type == MarketType.FUTURES:
|
|
226
225
|
return f"https://bingx.com/en/perpetual/{ticker}-USDT"
|
|
227
226
|
else:
|
|
228
|
-
return f"https://bingx.com/en/spot/{
|
|
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,34 +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_{
|
|
274
|
+
return f"{base_url}/Gate_{ticker}_USDT"
|
|
277
275
|
case Exchange.HYPERLIQUID:
|
|
278
|
-
return f"{base_url}/Hyperliquid_{
|
|
276
|
+
return f"{base_url}/Hyperliquid_{ticker}-USD"
|
|
279
277
|
case Exchange.KUCOIN:
|
|
280
|
-
return f"https://www.coinglass.com/tv/ru/KuCoin_{
|
|
278
|
+
return f"https://www.coinglass.com/tv/ru/KuCoin_{ticker}USDTM"
|
|
281
279
|
case Exchange.BINGX:
|
|
282
|
-
return f"https://www.coinglass.com/tv/ru/BingX_{
|
|
280
|
+
return f"https://www.coinglass.com/tv/ru/BingX_{ticker}-USDT"
|
|
283
281
|
case _:
|
|
284
|
-
return f"{base_url}/{exchange.capitalize()}_{
|
|
282
|
+
return f"{base_url}/{exchange.capitalize()}_{ticker}USDT"
|
|
285
283
|
else:
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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"
|
|
291
291
|
|
|
292
292
|
|
|
293
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,6 +11,7 @@ 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
|
|
|
@@ -173,6 +175,107 @@ class Adapter:
|
|
|
173
175
|
item["contract"]: OpenInterestItem(
|
|
174
176
|
t=int(time.time() * 1000),
|
|
175
177
|
v=float(item["total_size"]) * float(item["quanto_multiplier"]),
|
|
178
|
+
u="coins",
|
|
176
179
|
)
|
|
177
180
|
for item in raw_data
|
|
178
181
|
}
|
|
182
|
+
|
|
183
|
+
@staticmethod
|
|
184
|
+
def klines_message(raw_msg: Any) -> list[KlineDict]:
|
|
185
|
+
"""Преобразует вебсокет-сообщение со свечами в унифицированный формат.
|
|
186
|
+
|
|
187
|
+
Параметры:
|
|
188
|
+
raw_msg (Any): Сырое сообщение с вебсокета.
|
|
189
|
+
|
|
190
|
+
Возвращает:
|
|
191
|
+
list[KlineDict]: Список свечей в унифицированном формате.
|
|
192
|
+
"""
|
|
193
|
+
data = raw_msg["result"]
|
|
194
|
+
return [
|
|
195
|
+
KlineDict(
|
|
196
|
+
s=data["n"].split("_", 1)[1], # XRP_USDT
|
|
197
|
+
t=int(data["t"]) * 1000,
|
|
198
|
+
o=float(data["o"]),
|
|
199
|
+
h=float(data["h"]),
|
|
200
|
+
l=float(data["l"]),
|
|
201
|
+
c=float(data["c"]),
|
|
202
|
+
v=float(data["a"]),
|
|
203
|
+
q=float(data["v"]),
|
|
204
|
+
T=None,
|
|
205
|
+
x=not data["w"], # w=False → свеча закрыта
|
|
206
|
+
)
|
|
207
|
+
]
|
|
208
|
+
|
|
209
|
+
@staticmethod
|
|
210
|
+
def futures_klines_message(raw_msg: Any) -> list[KlineDict]:
|
|
211
|
+
"""Преобразует вебсокет-сообщение со свечами в унифицированный формат.
|
|
212
|
+
|
|
213
|
+
Параметры:
|
|
214
|
+
raw_msg (Any): Сырое сообщение с вебсокета.
|
|
215
|
+
|
|
216
|
+
Возвращает:
|
|
217
|
+
list[KlineDict]: Список свечей в унифицированном формате.
|
|
218
|
+
"""
|
|
219
|
+
return [
|
|
220
|
+
KlineDict(
|
|
221
|
+
s=item["n"].split("_", 1)[1], # XRP_USDT
|
|
222
|
+
t=int(item["t"]) * 1000,
|
|
223
|
+
o=float(item["o"]),
|
|
224
|
+
h=float(item["h"]),
|
|
225
|
+
l=float(item["l"]),
|
|
226
|
+
c=float(item["c"]),
|
|
227
|
+
v=float(item["a"]),
|
|
228
|
+
q=float(item["v"]),
|
|
229
|
+
T=None,
|
|
230
|
+
x=not item["w"], # w=False → свеча закрыта
|
|
231
|
+
)
|
|
232
|
+
for item in sorted(
|
|
233
|
+
raw_msg["result"],
|
|
234
|
+
key=lambda x: int(x["t"]),
|
|
235
|
+
)
|
|
236
|
+
]
|
|
237
|
+
|
|
238
|
+
@staticmethod
|
|
239
|
+
def trades_message(raw_msg: Any) -> list[TradeDict]:
|
|
240
|
+
"""Преобразует вебсокет-сообщение со сделками в унифицированный формат.
|
|
241
|
+
|
|
242
|
+
Параметры:
|
|
243
|
+
raw_msg (Any): Сырое сообщение с вебсокета.
|
|
244
|
+
|
|
245
|
+
Возвращает:
|
|
246
|
+
list[TradeDict]: Список сделок в унифицированном формате.
|
|
247
|
+
"""
|
|
248
|
+
trade = raw_msg["result"]
|
|
249
|
+
return [
|
|
250
|
+
TradeDict(
|
|
251
|
+
t=trade["create_time_ms"],
|
|
252
|
+
s=trade["currency_pair"],
|
|
253
|
+
S=trade["side"].upper(),
|
|
254
|
+
p=float(trade["price"]),
|
|
255
|
+
v=float(trade["amount"]),
|
|
256
|
+
)
|
|
257
|
+
]
|
|
258
|
+
|
|
259
|
+
@staticmethod
|
|
260
|
+
def futures_trades_message(raw_msg: Any) -> list[TradeDict]:
|
|
261
|
+
"""Преобразует вебсокет-сообщение со сделками в унифицированный формат.
|
|
262
|
+
|
|
263
|
+
Параметры:
|
|
264
|
+
raw_msg (Any): Сырое сообщение с вебсокета.
|
|
265
|
+
|
|
266
|
+
Возвращает:
|
|
267
|
+
list[TradeDict]: Список сделок в унифицированном формате.
|
|
268
|
+
"""
|
|
269
|
+
return [
|
|
270
|
+
TradeDict(
|
|
271
|
+
t=item["create_time_ms"],
|
|
272
|
+
s=item["contract"],
|
|
273
|
+
S="BUY" if float(item["size"]) > 0 else "SELL",
|
|
274
|
+
p=float(item["price"]),
|
|
275
|
+
v=abs(float(item["size"])),
|
|
276
|
+
)
|
|
277
|
+
for item in sorted(
|
|
278
|
+
raw_msg["result"],
|
|
279
|
+
key=lambda x: x["create_time_ms"],
|
|
280
|
+
)
|
|
281
|
+
]
|
|
@@ -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
|
|
@@ -36,6 +36,20 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
36
36
|
self._websocket_manager = WebsocketManager(self._client, **ws_kwargs) # type: ignore
|
|
37
37
|
self._adapter = Adapter()
|
|
38
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
|
+
|
|
39
53
|
@overload
|
|
40
54
|
def klines(
|
|
41
55
|
self,
|
|
@@ -76,7 +90,14 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
76
90
|
Возвращает:
|
|
77
91
|
`Websocket`: Экземпляр вебсокета для управления соединением.
|
|
78
92
|
"""
|
|
79
|
-
|
|
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
|
+
)
|
|
80
101
|
|
|
81
102
|
@overload
|
|
82
103
|
def futures_klines(
|
|
@@ -118,7 +139,14 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
118
139
|
Возвращает:
|
|
119
140
|
`Websocket`: Экземпляр вебсокета.
|
|
120
141
|
"""
|
|
121
|
-
|
|
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
|
+
)
|
|
122
150
|
|
|
123
151
|
@overload
|
|
124
152
|
def trades(
|
|
@@ -156,7 +184,10 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
156
184
|
Возвращает:
|
|
157
185
|
`Websocket`: Экземпляр вебсокета.
|
|
158
186
|
"""
|
|
159
|
-
|
|
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)
|
|
160
191
|
|
|
161
192
|
@overload
|
|
162
193
|
def aggtrades(
|
|
@@ -194,7 +225,7 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
194
225
|
Возвращает:
|
|
195
226
|
`Websocket`: Экземпляр вебсокета.
|
|
196
227
|
"""
|
|
197
|
-
|
|
228
|
+
return self.trades(callback=callback, symbol=symbol, symbols=symbols) # type: ignore[reportCallIssue]
|
|
198
229
|
|
|
199
230
|
@overload
|
|
200
231
|
def futures_trades(
|
|
@@ -232,7 +263,10 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
232
263
|
Возвращает:
|
|
233
264
|
`Websocket`: Экземпляр вебсокета.
|
|
234
265
|
"""
|
|
235
|
-
|
|
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)
|
|
236
270
|
|
|
237
271
|
@overload
|
|
238
272
|
def futures_aggtrades(
|
|
@@ -270,4 +304,4 @@ class UniWebsocketManager(IUniWebsocketManager):
|
|
|
270
304
|
Возвращает:
|
|
271
305
|
`Websocket`: Экземпляр вебсокета.
|
|
272
306
|
"""
|
|
273
|
-
|
|
307
|
+
return self.futures_trades(callback=callback, symbol=symbol, symbols=symbols) # type: ignore[reportCallIssue]
|
unicex/hyperliquid/adapter.py
CHANGED
unicex/kucoin/adapter.py
CHANGED
|
@@ -126,27 +126,22 @@ class Adapter:
|
|
|
126
126
|
item["symbol"]: OpenInterestItem(
|
|
127
127
|
t=item["ts"],
|
|
128
128
|
v=float(item["openInterest"]) * Adapter._get_contract_size(item["symbol"]),
|
|
129
|
+
u="coins",
|
|
129
130
|
)
|
|
130
131
|
for item in raw_data["data"]
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
@staticmethod
|
|
134
|
-
def funding_rate(raw_data: dict) ->
|
|
135
|
+
def funding_rate(raw_data: dict) -> float:
|
|
135
136
|
"""Преобразует историю ставок финансирования в унифицированный формат.
|
|
136
137
|
|
|
137
138
|
Параметры:
|
|
138
139
|
raw_data (dict): Сырой ответ с биржи.
|
|
139
140
|
|
|
140
141
|
Возвращает:
|
|
141
|
-
|
|
142
|
+
float: Актуальная ставка финансирования.
|
|
142
143
|
"""
|
|
143
|
-
|
|
144
|
-
history = raw_data["data"]["list"]
|
|
145
|
-
if not history:
|
|
146
|
-
return {}
|
|
147
|
-
|
|
148
|
-
last_point = max(history, key=lambda item: int(item["ts"]))
|
|
149
|
-
return {symbol: float(last_point["fundingRate"]) * 100}
|
|
144
|
+
return round(raw_data["data"]["nextFundingRate"] * 100, 6)
|
|
150
145
|
|
|
151
146
|
@staticmethod
|
|
152
147
|
def _get_contract_size(symbol: str) -> float:
|
unicex/kucoin/client.py
CHANGED
|
@@ -120,3 +120,16 @@ class Client(BaseClient):
|
|
|
120
120
|
"/api/ua/v1/market/funding-rate-history",
|
|
121
121
|
params=params,
|
|
122
122
|
)
|
|
123
|
+
|
|
124
|
+
async def funding_rate(self, symbol: str) -> dict[str, Any]:
|
|
125
|
+
"""Получение текущей ставки финансирования.
|
|
126
|
+
|
|
127
|
+
https://www.kucoin.com/docs-new/rest/ua/get-current-funding-rate
|
|
128
|
+
"""
|
|
129
|
+
params = {"symbol": symbol}
|
|
130
|
+
|
|
131
|
+
return await self._make_request(
|
|
132
|
+
"GET",
|
|
133
|
+
"/api/ua/v1/market/funding-rate",
|
|
134
|
+
params=params,
|
|
135
|
+
)
|