unicex 0.13.17__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 +200 -0
- unicex/_abc/__init__.py +11 -0
- unicex/_abc/exchange_info.py +216 -0
- unicex/_abc/uni_client.py +329 -0
- unicex/_abc/uni_websocket_manager.py +294 -0
- unicex/_base/__init__.py +9 -0
- unicex/_base/client.py +214 -0
- unicex/_base/websocket.py +261 -0
- unicex/binance/__init__.py +27 -0
- unicex/binance/adapter.py +202 -0
- unicex/binance/client.py +1577 -0
- unicex/binance/exchange_info.py +62 -0
- unicex/binance/uni_client.py +188 -0
- unicex/binance/uni_websocket_manager.py +166 -0
- unicex/binance/user_websocket.py +186 -0
- unicex/binance/websocket_manager.py +912 -0
- unicex/bitget/__init__.py +27 -0
- unicex/bitget/adapter.py +188 -0
- unicex/bitget/client.py +2514 -0
- unicex/bitget/exchange_info.py +48 -0
- unicex/bitget/uni_client.py +198 -0
- unicex/bitget/uni_websocket_manager.py +275 -0
- unicex/bitget/user_websocket.py +7 -0
- unicex/bitget/websocket_manager.py +232 -0
- unicex/bybit/__init__.py +27 -0
- unicex/bybit/adapter.py +208 -0
- unicex/bybit/client.py +1876 -0
- unicex/bybit/exchange_info.py +53 -0
- unicex/bybit/uni_client.py +200 -0
- unicex/bybit/uni_websocket_manager.py +291 -0
- unicex/bybit/user_websocket.py +7 -0
- unicex/bybit/websocket_manager.py +339 -0
- unicex/enums.py +273 -0
- unicex/exceptions.py +64 -0
- unicex/extra.py +335 -0
- unicex/gate/__init__.py +27 -0
- unicex/gate/adapter.py +178 -0
- unicex/gate/client.py +1667 -0
- unicex/gate/exchange_info.py +55 -0
- unicex/gate/uni_client.py +214 -0
- unicex/gate/uni_websocket_manager.py +269 -0
- unicex/gate/user_websocket.py +7 -0
- unicex/gate/websocket_manager.py +513 -0
- unicex/hyperliquid/__init__.py +27 -0
- unicex/hyperliquid/adapter.py +261 -0
- unicex/hyperliquid/client.py +2315 -0
- unicex/hyperliquid/exchange_info.py +119 -0
- unicex/hyperliquid/uni_client.py +325 -0
- unicex/hyperliquid/uni_websocket_manager.py +269 -0
- unicex/hyperliquid/user_websocket.py +7 -0
- unicex/hyperliquid/websocket_manager.py +393 -0
- unicex/mapper.py +111 -0
- unicex/mexc/__init__.py +27 -0
- unicex/mexc/_spot_ws_proto/PrivateAccountV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PrivateDealsV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PrivateOrdersV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicAggreBookTickerV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicAggreDealsV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicAggreDepthsV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicBookTickerBatchV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicBookTickerV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicDealsV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicFuture_pb2.py +103 -0
- unicex/mexc/_spot_ws_proto/PublicIncreaseDepthsBatchV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicIncreaseDepthsV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicLimitDepthsV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicMiniTickerV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicMiniTickersV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PublicSpotKlineV3Api_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/PushDataV3ApiWrapper_pb2.py +38 -0
- unicex/mexc/_spot_ws_proto/__init__.py +335 -0
- unicex/mexc/adapter.py +239 -0
- unicex/mexc/client.py +846 -0
- unicex/mexc/exchange_info.py +47 -0
- unicex/mexc/uni_client.py +211 -0
- unicex/mexc/uni_websocket_manager.py +269 -0
- unicex/mexc/user_websocket.py +7 -0
- unicex/mexc/websocket_manager.py +456 -0
- unicex/okx/__init__.py +27 -0
- unicex/okx/adapter.py +150 -0
- unicex/okx/client.py +2864 -0
- unicex/okx/exchange_info.py +47 -0
- unicex/okx/uni_client.py +202 -0
- unicex/okx/uni_websocket_manager.py +269 -0
- unicex/okx/user_websocket.py +7 -0
- unicex/okx/websocket_manager.py +743 -0
- unicex/types.py +164 -0
- unicex/utils.py +218 -0
- unicex-0.13.17.dist-info/METADATA +243 -0
- unicex-0.13.17.dist-info/RECORD +93 -0
- unicex-0.13.17.dist-info/WHEEL +5 -0
- unicex-0.13.17.dist-info/licenses/LICENSE +28 -0
- unicex-0.13.17.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
__all__ = ["WebsocketManager"]
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from collections.abc import Awaitable, Callable, Sequence
|
|
5
|
+
from typing import Any, Literal
|
|
6
|
+
|
|
7
|
+
import orjson
|
|
8
|
+
from google.protobuf.json_format import MessageToDict
|
|
9
|
+
|
|
10
|
+
from unicex._base import Websocket
|
|
11
|
+
|
|
12
|
+
from ._spot_ws_proto import PushDataV3ApiWrapper
|
|
13
|
+
from .client import Client
|
|
14
|
+
|
|
15
|
+
type CallbackType = Callable[[Any], Awaitable[None]]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class WebsocketManager:
|
|
19
|
+
"""Менеджер асинхронных вебсокетов для Mexc."""
|
|
20
|
+
|
|
21
|
+
_SPOT_URL: str = "wss://wbs-api.mexc.com/ws"
|
|
22
|
+
"""Базовый URL для вебсокета на спот."""
|
|
23
|
+
|
|
24
|
+
_FUTURES_URL: str = "wss://contract.mexc.com/edge"
|
|
25
|
+
"""Базовый URL для вебсокета на фьючерсы."""
|
|
26
|
+
|
|
27
|
+
class _MexcProtobufDecoder:
|
|
28
|
+
"""Класс для декодирования сообщений в формате Protobuf со спотового рынка Mexc."""
|
|
29
|
+
|
|
30
|
+
def decode(self, message: Any) -> dict:
|
|
31
|
+
if isinstance(message, bytes):
|
|
32
|
+
wrapper = PushDataV3ApiWrapper() # noqa
|
|
33
|
+
wrapper.ParseFromString(message)
|
|
34
|
+
return MessageToDict(wrapper, preserving_proto_field_name=True) # type: ignore
|
|
35
|
+
elif isinstance(message, str):
|
|
36
|
+
return orjson.loads(message)
|
|
37
|
+
else:
|
|
38
|
+
raise ValueError(f"Invalid message type: {type(message)}")
|
|
39
|
+
|
|
40
|
+
def __init__(self, client: Client | None = None, **ws_kwargs: Any) -> None:
|
|
41
|
+
"""Инициализирует менеджер вебсокетов для Mexc.
|
|
42
|
+
|
|
43
|
+
Параметры:
|
|
44
|
+
client (`Client | None`): Клиент для выполнения запросов. Нужен, чтобы открыть приватные вебсокеты.
|
|
45
|
+
ws_kwargs (`dict[str, Any]`): Дополнительные аргументы, которые прокидываются в `Websocket`.
|
|
46
|
+
"""
|
|
47
|
+
self.client = client
|
|
48
|
+
self._ws_kwargs = ws_kwargs
|
|
49
|
+
|
|
50
|
+
def _generate_subscription_message(
|
|
51
|
+
self,
|
|
52
|
+
channel_template: str,
|
|
53
|
+
symbol: str | None = None,
|
|
54
|
+
symbols: Sequence[str] | None = None,
|
|
55
|
+
**template_kwargs: Any,
|
|
56
|
+
) -> list[str]:
|
|
57
|
+
"""Сформировать сообщение для подписки на вебсокет."""
|
|
58
|
+
if symbol and symbols:
|
|
59
|
+
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
60
|
+
if not (symbol or symbols):
|
|
61
|
+
raise ValueError("Either symbol or symbols must be provided")
|
|
62
|
+
|
|
63
|
+
if symbol:
|
|
64
|
+
params = [channel_template.format(symbol=symbol, **template_kwargs)]
|
|
65
|
+
elif symbols:
|
|
66
|
+
params = [
|
|
67
|
+
channel_template.format(symbol=symbol, **template_kwargs) for symbol in symbols
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
return [json.dumps({"method": "SUBSCRIPTION", "params": params})]
|
|
71
|
+
|
|
72
|
+
def _generate_futures_subscription_message(
|
|
73
|
+
self,
|
|
74
|
+
topic: str,
|
|
75
|
+
symbol: str | None = None,
|
|
76
|
+
symbols: Sequence[str] | None = None,
|
|
77
|
+
require_symbol: bool = True,
|
|
78
|
+
**additional_params_kwargs: Any,
|
|
79
|
+
) -> list[str]:
|
|
80
|
+
"""Сформировать сообщение для подписки на фьючерсный вебсокет."""
|
|
81
|
+
if symbol and symbols:
|
|
82
|
+
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
83
|
+
if require_symbol and not (symbol or symbols):
|
|
84
|
+
raise ValueError("Either symbol or symbols must be provided")
|
|
85
|
+
|
|
86
|
+
if symbol:
|
|
87
|
+
symbols = [symbol]
|
|
88
|
+
if symbols:
|
|
89
|
+
return [
|
|
90
|
+
json.dumps({"method": topic, "param": {"symbol": s, **additional_params_kwargs}})
|
|
91
|
+
for s in symbols
|
|
92
|
+
] # type: ignore
|
|
93
|
+
return [json.dumps({"method": topic, "param": {**additional_params_kwargs}})]
|
|
94
|
+
|
|
95
|
+
def _create_websocket(
|
|
96
|
+
self, callback: CallbackType, subscription_messages: list[str]
|
|
97
|
+
) -> Websocket:
|
|
98
|
+
"""Шорткат для создания вебсокета."""
|
|
99
|
+
return Websocket(
|
|
100
|
+
callback=callback,
|
|
101
|
+
url=self._SPOT_URL,
|
|
102
|
+
subscription_messages=subscription_messages,
|
|
103
|
+
decoder=self._MexcProtobufDecoder,
|
|
104
|
+
ping_message='{"method": "PING"}',
|
|
105
|
+
**self._ws_kwargs,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def _create_futures_websocket(
|
|
109
|
+
self, callback: CallbackType, subscription_messages: list[str]
|
|
110
|
+
) -> Websocket:
|
|
111
|
+
"""Шорткат для создания фьючерсного вебсокета."""
|
|
112
|
+
return Websocket(
|
|
113
|
+
callback=callback,
|
|
114
|
+
url=self._FUTURES_URL,
|
|
115
|
+
subscription_messages=subscription_messages,
|
|
116
|
+
ping_message='{"method": "ping"}',
|
|
117
|
+
**self._ws_kwargs,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
def trade(
|
|
121
|
+
self,
|
|
122
|
+
callback: CallbackType,
|
|
123
|
+
symbol: str | None = None,
|
|
124
|
+
symbols: Sequence[str] | None = None,
|
|
125
|
+
update_interval: Literal["100ms", "10ms"] = "100ms",
|
|
126
|
+
) -> Websocket:
|
|
127
|
+
"""Создает вебсокет для получения сделок.
|
|
128
|
+
|
|
129
|
+
https://mexcdevelop.github.io/apidocs/spot_v3_en/#trade-streams
|
|
130
|
+
|
|
131
|
+
Параметры:
|
|
132
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
133
|
+
symbol (`str | None`): Один символ для подписки.
|
|
134
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
135
|
+
update_interval (`Literal["100ms", "10ms"]`): Интервал обновления.
|
|
136
|
+
|
|
137
|
+
Возвращает:
|
|
138
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
139
|
+
"""
|
|
140
|
+
subscription_messages = self._generate_subscription_message(
|
|
141
|
+
channel_template="spot@public.aggre.deals.v3.api.pb@{update_interval}@{symbol}",
|
|
142
|
+
symbol=symbol,
|
|
143
|
+
symbols=symbols,
|
|
144
|
+
update_interval=update_interval,
|
|
145
|
+
)
|
|
146
|
+
return self._create_websocket(callback, subscription_messages)
|
|
147
|
+
|
|
148
|
+
def klines(
|
|
149
|
+
self,
|
|
150
|
+
callback: CallbackType,
|
|
151
|
+
interval: str,
|
|
152
|
+
symbol: str | None = None,
|
|
153
|
+
symbols: Sequence[str] | None = None,
|
|
154
|
+
) -> Websocket:
|
|
155
|
+
"""Создает вебсокет для получения K-line (candlestick) данных.
|
|
156
|
+
|
|
157
|
+
https://mexcdevelop.github.io/apidocs/spot_v3_en/#k-line-streams
|
|
158
|
+
|
|
159
|
+
Параметры:
|
|
160
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
161
|
+
symbol (`str | None`): Один символ для подписки.
|
|
162
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
163
|
+
interval (`Literal[...]`): Интервал K-line.
|
|
164
|
+
|
|
165
|
+
Возвращает:
|
|
166
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
167
|
+
"""
|
|
168
|
+
subscription_messages = self._generate_subscription_message(
|
|
169
|
+
channel_template="spot@public.kline.v3.api.pb@{symbol}@{interval}",
|
|
170
|
+
symbol=symbol,
|
|
171
|
+
symbols=symbols,
|
|
172
|
+
interval=interval,
|
|
173
|
+
)
|
|
174
|
+
return self._create_websocket(callback, subscription_messages)
|
|
175
|
+
|
|
176
|
+
def diff_depth(
|
|
177
|
+
self,
|
|
178
|
+
callback: CallbackType,
|
|
179
|
+
symbol: str | None = None,
|
|
180
|
+
symbols: Sequence[str] | None = None,
|
|
181
|
+
update_speed: Literal["100ms", "10ms"] = "100ms",
|
|
182
|
+
) -> Websocket:
|
|
183
|
+
"""Создает вебсокет для получения инкрементальных изменений в книге заявок.
|
|
184
|
+
|
|
185
|
+
https://mexcdevelop.github.io/apidocs/spot_v3_en/#diff-depth-stream
|
|
186
|
+
|
|
187
|
+
Параметры:
|
|
188
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
189
|
+
symbol (`str | None`): Один символ для подписки.
|
|
190
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
191
|
+
update_speed (`Literal["100ms", "10ms"]`): Скорость обновления данных.
|
|
192
|
+
|
|
193
|
+
Возвращает:
|
|
194
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
195
|
+
"""
|
|
196
|
+
subscription_messages = self._generate_subscription_message(
|
|
197
|
+
channel_template="spot@public.aggre.depth.v3.api.pb@{update_speed}@{symbol}",
|
|
198
|
+
symbol=symbol,
|
|
199
|
+
symbols=symbols,
|
|
200
|
+
update_speed=update_speed,
|
|
201
|
+
)
|
|
202
|
+
return self._create_websocket(callback, subscription_messages)
|
|
203
|
+
|
|
204
|
+
def partial_depth(
|
|
205
|
+
self,
|
|
206
|
+
callback: CallbackType,
|
|
207
|
+
symbol: str | None = None,
|
|
208
|
+
symbols: Sequence[str] | None = None,
|
|
209
|
+
levels: Literal["5", "10", "20"] = "5",
|
|
210
|
+
) -> Websocket:
|
|
211
|
+
"""Создает вебсокет для получения ограниченной глубины книги заявок.
|
|
212
|
+
|
|
213
|
+
https://mexcdevelop.github.io/apidocs/spot_v3_en/#partial-book-depth-streams
|
|
214
|
+
|
|
215
|
+
Параметры:
|
|
216
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
217
|
+
symbol (`str | None`): Один символ для подписки.
|
|
218
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
219
|
+
levels (`Literal["5", "10", "20"]`): Количество уровней глубины.
|
|
220
|
+
|
|
221
|
+
Возвращает:
|
|
222
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
223
|
+
"""
|
|
224
|
+
subscription_messages = self._generate_subscription_message(
|
|
225
|
+
channel_template="spot@public.limit.depth.v3.api.pb@{symbol}@{levels}",
|
|
226
|
+
symbol=symbol,
|
|
227
|
+
symbols=symbols,
|
|
228
|
+
levels=levels,
|
|
229
|
+
)
|
|
230
|
+
return self._create_websocket(callback, subscription_messages)
|
|
231
|
+
|
|
232
|
+
def book_ticker(
|
|
233
|
+
self,
|
|
234
|
+
callback: CallbackType,
|
|
235
|
+
symbol: str | None = None,
|
|
236
|
+
symbols: Sequence[str] | None = None,
|
|
237
|
+
update_speed: Literal["100ms", "10ms"] = "100ms",
|
|
238
|
+
) -> Websocket:
|
|
239
|
+
"""Создает вебсокет для получения лучших цен покупки и продажи в реальном времени.
|
|
240
|
+
|
|
241
|
+
https://mexcdevelop.github.io/apidocs/spot_v3_en/#individual-symbol-book-ticker-streams
|
|
242
|
+
|
|
243
|
+
Параметры:
|
|
244
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
245
|
+
symbol (`str | None`): Один символ для подписки.
|
|
246
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
247
|
+
update_speed (`Literal["100ms", "10ms"]`): Скорость обновления данных.
|
|
248
|
+
|
|
249
|
+
Возвращает:
|
|
250
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
251
|
+
"""
|
|
252
|
+
subscription_messages = self._generate_subscription_message(
|
|
253
|
+
channel_template="spot@public.aggre.bookTicker.v3.api.pb@{update_speed}@{symbol}",
|
|
254
|
+
symbol=symbol,
|
|
255
|
+
symbols=symbols,
|
|
256
|
+
update_speed=update_speed,
|
|
257
|
+
)
|
|
258
|
+
return self._create_websocket(callback, subscription_messages)
|
|
259
|
+
|
|
260
|
+
def book_ticker_batch(
|
|
261
|
+
self,
|
|
262
|
+
callback: CallbackType,
|
|
263
|
+
symbol: str | None = None,
|
|
264
|
+
symbols: Sequence[str] | None = None,
|
|
265
|
+
) -> Websocket:
|
|
266
|
+
"""Создает вебсокет для получения лучших цен покупки и продажи (батч версия).
|
|
267
|
+
|
|
268
|
+
https://mexcdevelop.github.io/apidocs/spot_v3_en/#individual-symbol-book-ticker-streams-batch-aggregation
|
|
269
|
+
|
|
270
|
+
Параметры:
|
|
271
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
272
|
+
symbol (`str | None`): Один символ для подписки.
|
|
273
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
274
|
+
|
|
275
|
+
Возвращает:
|
|
276
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
277
|
+
"""
|
|
278
|
+
subscription_messages = self._generate_subscription_message(
|
|
279
|
+
channel_template="spot@public.bookTicker.batch.v3.api.pb@{symbol}",
|
|
280
|
+
symbol=symbol,
|
|
281
|
+
symbols=symbols,
|
|
282
|
+
)
|
|
283
|
+
return self._create_websocket(callback, subscription_messages)
|
|
284
|
+
|
|
285
|
+
def futures_tickers(
|
|
286
|
+
self,
|
|
287
|
+
callback: CallbackType,
|
|
288
|
+
) -> Websocket:
|
|
289
|
+
"""Создает вебсокет для получения тикеров всех фьючерсных контрактов.
|
|
290
|
+
|
|
291
|
+
https://mexcdevelop.github.io/apidocs/contract_v1_en/#public-channels
|
|
292
|
+
|
|
293
|
+
Параметры:
|
|
294
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
295
|
+
|
|
296
|
+
Возвращает:
|
|
297
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
298
|
+
"""
|
|
299
|
+
subscription_messages = self._generate_futures_subscription_message(
|
|
300
|
+
topic="sub.tickers", require_symbol=False
|
|
301
|
+
)
|
|
302
|
+
return self._create_futures_websocket(callback, subscription_messages)
|
|
303
|
+
|
|
304
|
+
def futures_ticker(
|
|
305
|
+
self,
|
|
306
|
+
callback: CallbackType,
|
|
307
|
+
symbol: str | None = None,
|
|
308
|
+
symbols: Sequence[str] | None = None,
|
|
309
|
+
) -> Websocket:
|
|
310
|
+
"""Создает вебсокет для получения тикера конкретных фьючерсных контрактов.
|
|
311
|
+
|
|
312
|
+
https://mexcdevelop.github.io/apidocs/contract_v1_en/#public-channels
|
|
313
|
+
|
|
314
|
+
Параметры:
|
|
315
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
316
|
+
symbol (`str | None`): Символ фьючерсного контракта.
|
|
317
|
+
symbols (`Sequence[str] | None`): Последовательность символов фьючерсных контрактов.
|
|
318
|
+
|
|
319
|
+
Возвращает:
|
|
320
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
321
|
+
"""
|
|
322
|
+
subscription_messages = self._generate_futures_subscription_message(
|
|
323
|
+
topic="sub.ticker", symbol=symbol, symbols=symbols
|
|
324
|
+
)
|
|
325
|
+
return self._create_futures_websocket(callback, subscription_messages)
|
|
326
|
+
|
|
327
|
+
def futures_depth(
|
|
328
|
+
self,
|
|
329
|
+
callback: CallbackType,
|
|
330
|
+
limit: Literal[5, 10, 20] | None = None,
|
|
331
|
+
is_full: bool = False,
|
|
332
|
+
compress: bool = False,
|
|
333
|
+
symbol: str | None = None,
|
|
334
|
+
symbols: Sequence[str] | None = None,
|
|
335
|
+
) -> Websocket:
|
|
336
|
+
"""Создает вебсокет для получения глубины рынка фьючерсных контрактов.
|
|
337
|
+
|
|
338
|
+
https://mexcdevelop.github.io/apidocs/contract_v1_en/#public-channels
|
|
339
|
+
|
|
340
|
+
Параметры:
|
|
341
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
342
|
+
limit (`Literal[5, 10, 20] | None`): Количество уровней в стакане заявок.
|
|
343
|
+
is_full (`bool`): Получать полную глубину рынка.
|
|
344
|
+
compress (`bool`): Использовать сжатие данных.
|
|
345
|
+
symbol (`str | None`): Символ фьючерсного контракта.
|
|
346
|
+
symbols (`Sequence[str] | None`): Последовательность символов фьючерсных контрактов.
|
|
347
|
+
|
|
348
|
+
Возвращает:
|
|
349
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
350
|
+
"""
|
|
351
|
+
additional_params = {}
|
|
352
|
+
if limit:
|
|
353
|
+
additional_params["limit"] = limit
|
|
354
|
+
if compress:
|
|
355
|
+
additional_params["compress"] = compress
|
|
356
|
+
topic = "sub.depth"
|
|
357
|
+
if is_full:
|
|
358
|
+
topic += ".full"
|
|
359
|
+
subscription_messages = self._generate_futures_subscription_message(
|
|
360
|
+
topic=topic, symbol=symbol, symbols=symbols, **additional_params
|
|
361
|
+
)
|
|
362
|
+
return self._create_futures_websocket(callback, subscription_messages)
|
|
363
|
+
|
|
364
|
+
def futures_kline(
|
|
365
|
+
self,
|
|
366
|
+
callback: CallbackType,
|
|
367
|
+
interval: str,
|
|
368
|
+
symbol: str | None = None,
|
|
369
|
+
symbols: Sequence[str] | None = None,
|
|
370
|
+
) -> Websocket:
|
|
371
|
+
"""Создает вебсокет для получения свечных данных фьючерсных контрактов.
|
|
372
|
+
|
|
373
|
+
https://mexcdevelop.github.io/apidocs/contract_v1_en/#public-channels
|
|
374
|
+
|
|
375
|
+
Параметры:
|
|
376
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
377
|
+
interval (`str`): Временной интервал для свечей.
|
|
378
|
+
symbol (`str | None`): Символ фьючерсного контракта.
|
|
379
|
+
symbols (`Sequence[str] | None`): Последовательность символов фьючерсных контрактов.
|
|
380
|
+
|
|
381
|
+
Возвращает:
|
|
382
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
383
|
+
"""
|
|
384
|
+
subscription_messages = self._generate_futures_subscription_message(
|
|
385
|
+
topic="sub.deal", symbol=symbol, symbols=symbols, interval=interval
|
|
386
|
+
)
|
|
387
|
+
return self._create_futures_websocket(callback, subscription_messages)
|
|
388
|
+
|
|
389
|
+
def funding_rate(
|
|
390
|
+
self,
|
|
391
|
+
callback: CallbackType,
|
|
392
|
+
symbol: str | None = None,
|
|
393
|
+
symbols: Sequence[str] | None = None,
|
|
394
|
+
) -> Websocket:
|
|
395
|
+
"""Создает вебсокет для получения ставки финансирования фьючерсных контрактов.
|
|
396
|
+
|
|
397
|
+
https://mexcdevelop.github.io/apidocs/contract_v1_en/#public-channels
|
|
398
|
+
|
|
399
|
+
Параметры:
|
|
400
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
401
|
+
symbol (`str | None`): Символ фьючерсного контракта.
|
|
402
|
+
symbols (`Sequence[str] | None`): Последовательность символов фьючерсных контрактов.
|
|
403
|
+
|
|
404
|
+
Возвращает:
|
|
405
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
406
|
+
"""
|
|
407
|
+
subscription_messages = self._generate_futures_subscription_message(
|
|
408
|
+
topic="sub.funding.rate", symbol=symbol, symbols=symbols
|
|
409
|
+
)
|
|
410
|
+
return self._create_futures_websocket(callback, subscription_messages)
|
|
411
|
+
|
|
412
|
+
def futures_index_price(
|
|
413
|
+
self,
|
|
414
|
+
callback: CallbackType,
|
|
415
|
+
symbol: str | None = None,
|
|
416
|
+
symbols: Sequence[str] | None = None,
|
|
417
|
+
) -> Websocket:
|
|
418
|
+
"""Создает вебсокет для получения индексной цены фьючерсных контрактов.
|
|
419
|
+
|
|
420
|
+
https://mexcdevelop.github.io/apidocs/contract_v1_en/#public-channels
|
|
421
|
+
|
|
422
|
+
Параметры:
|
|
423
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
424
|
+
symbol (`str | None`): Символ фьючерсного контракта.
|
|
425
|
+
symbols (`Sequence[str] | None`): Последовательность символов фьючерсных контрактов.
|
|
426
|
+
|
|
427
|
+
Возвращает:
|
|
428
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
429
|
+
"""
|
|
430
|
+
subscription_messages = self._generate_futures_subscription_message(
|
|
431
|
+
topic="sub.index.price", symbol=symbol, symbols=symbols
|
|
432
|
+
)
|
|
433
|
+
return self._create_futures_websocket(callback, subscription_messages)
|
|
434
|
+
|
|
435
|
+
def futures_fair_price(
|
|
436
|
+
self,
|
|
437
|
+
callback: CallbackType,
|
|
438
|
+
symbol: str | None = None,
|
|
439
|
+
symbols: Sequence[str] | None = None,
|
|
440
|
+
) -> Websocket:
|
|
441
|
+
"""Создает вебсокет для получения справедливой цены фьючерсных контрактов.
|
|
442
|
+
|
|
443
|
+
https://mexcdevelop.github.io/apidocs/contract_v1_en/#public-channels
|
|
444
|
+
|
|
445
|
+
Параметры:
|
|
446
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
447
|
+
symbol (`str | None`): Символ фьючерсного контракта.
|
|
448
|
+
symbols (`Sequence[str] | None`): Последовательность символов фьючерсных контрактов.
|
|
449
|
+
|
|
450
|
+
Возвращает:
|
|
451
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
452
|
+
"""
|
|
453
|
+
subscription_messages = self._generate_futures_subscription_message(
|
|
454
|
+
topic="sub.fair.price", symbol=symbol, symbols=symbols
|
|
455
|
+
)
|
|
456
|
+
return self._create_futures_websocket(callback, subscription_messages)
|
unicex/okx/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Пакет, содержащий реализации клиентов и менеджеров для работы с биржей Okx."""
|
|
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
|
+
"""Загружает информацию о бирже Okx."""
|
|
22
|
+
await ExchangeInfo.load_exchange_info()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def start_exchange_info(parse_interval_seconds: int = 60 * 60) -> None:
|
|
26
|
+
"""Запускает процесс обновления информации о бирже Okx."""
|
|
27
|
+
await ExchangeInfo.start(parse_interval_seconds)
|
unicex/okx/adapter.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
__all__ = ["Adapter"]
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from unicex.types import (
|
|
5
|
+
KlineDict,
|
|
6
|
+
OpenInterestDict,
|
|
7
|
+
OpenInterestItem,
|
|
8
|
+
TickerDailyDict,
|
|
9
|
+
TickerDailyItem,
|
|
10
|
+
)
|
|
11
|
+
from unicex.utils import catch_adapter_errors, decorate_all_methods
|
|
12
|
+
|
|
13
|
+
from .exchange_info import ExchangeInfo
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@decorate_all_methods(catch_adapter_errors)
|
|
17
|
+
class Adapter:
|
|
18
|
+
"""Адаптер для унификации данных с Okx API."""
|
|
19
|
+
|
|
20
|
+
@staticmethod
|
|
21
|
+
def tickers(raw_data: dict, only_usdt: bool) -> list[str]:
|
|
22
|
+
"""Преобразует сырые данные о тикерах в список унифицированных символов.
|
|
23
|
+
|
|
24
|
+
Параметры:
|
|
25
|
+
raw_data (`dict`): Сырой ответ от OKX.
|
|
26
|
+
only_usdt (`bool`): Возвращать только тикеры в паре с USDT.
|
|
27
|
+
|
|
28
|
+
Возвращает:
|
|
29
|
+
`list[str]`: Список тикеров.
|
|
30
|
+
"""
|
|
31
|
+
return [
|
|
32
|
+
item["instId"]
|
|
33
|
+
for item in raw_data["data"]
|
|
34
|
+
if item["instId"].endswith("-USDT") or not only_usdt
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def futures_tickers(raw_data: dict, only_usdt: bool) -> list[str]:
|
|
39
|
+
"""Преобразует сырые данные о тикерах в список унифицированных символов.
|
|
40
|
+
|
|
41
|
+
Параметры:
|
|
42
|
+
raw_data (`dict`): Сырой ответ от OKX.
|
|
43
|
+
only_usdt (`bool`): Возвращать только тикеры в паре с USDT.
|
|
44
|
+
|
|
45
|
+
Возвращает:
|
|
46
|
+
`list[str]`: Список тикеров.
|
|
47
|
+
"""
|
|
48
|
+
return [
|
|
49
|
+
item["instId"]
|
|
50
|
+
for item in raw_data["data"]
|
|
51
|
+
if item["instId"].endswith("-USDT-SWAP") or not only_usdt
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def ticker_24hr(raw_data: dict) -> TickerDailyDict:
|
|
56
|
+
"""Преобразует статистику 24ч в унифицированный формат.
|
|
57
|
+
|
|
58
|
+
# NOTE: Обратите внимание, изменение цены в случае с OKX возвращается относительно открытия 1 day свечи.
|
|
59
|
+
"""
|
|
60
|
+
result = {}
|
|
61
|
+
for item in raw_data["data"]:
|
|
62
|
+
try:
|
|
63
|
+
result[item["instId"]] = TickerDailyItem(
|
|
64
|
+
p=round(
|
|
65
|
+
(float(item["last"]) - float(item["open24h"]))
|
|
66
|
+
/ float(item["open24h"])
|
|
67
|
+
* 100,
|
|
68
|
+
2,
|
|
69
|
+
),
|
|
70
|
+
v=float(item["vol24h"]),
|
|
71
|
+
q=float(item["volCcy24h"]),
|
|
72
|
+
)
|
|
73
|
+
except (ValueError, TypeError, KeyError):
|
|
74
|
+
continue
|
|
75
|
+
return result
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def futures_ticker_24hr(raw_data: dict) -> TickerDailyDict:
|
|
79
|
+
"""Преобразует статистику 24ч в унифицированный формат.
|
|
80
|
+
|
|
81
|
+
# NOTE: Обратите внимание, изменение цены в случае с OKX возвращается относительно открытия 1 day свечи.
|
|
82
|
+
"""
|
|
83
|
+
return {
|
|
84
|
+
item["instId"]: TickerDailyItem(
|
|
85
|
+
p=round(
|
|
86
|
+
(float(item["last"]) - float(item["open24h"])) / float(item["open24h"]) * 100, 2
|
|
87
|
+
),
|
|
88
|
+
v=float(item["volCcy24h"]),
|
|
89
|
+
q=float(item["volCcy24h"]) * float(item["last"]),
|
|
90
|
+
)
|
|
91
|
+
for item in raw_data["data"]
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def last_price(raw_data: dict) -> dict[str, float]:
|
|
96
|
+
"""Преобразует данные о последней цене в унифицированный формат."""
|
|
97
|
+
result = {}
|
|
98
|
+
for item in raw_data["data"]:
|
|
99
|
+
try:
|
|
100
|
+
result[item["instId"]] = float(item["last"])
|
|
101
|
+
except (ValueError, TypeError, KeyError):
|
|
102
|
+
continue
|
|
103
|
+
return result
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def klines(raw_data: dict, symbol: str) -> list[KlineDict]:
|
|
107
|
+
"""Преобразует данные о свечах в унифицированный формат."""
|
|
108
|
+
return [
|
|
109
|
+
KlineDict(
|
|
110
|
+
s=symbol,
|
|
111
|
+
t=int(kline[0]),
|
|
112
|
+
o=float(kline[1]),
|
|
113
|
+
h=float(kline[2]),
|
|
114
|
+
l=float(kline[3]),
|
|
115
|
+
c=float(kline[4]),
|
|
116
|
+
v=float(kline[6]),
|
|
117
|
+
q=float(kline[7]),
|
|
118
|
+
T=None,
|
|
119
|
+
x=bool(int(kline[8])),
|
|
120
|
+
)
|
|
121
|
+
for kline in sorted(
|
|
122
|
+
raw_data["data"],
|
|
123
|
+
key=lambda x: int(x[0]),
|
|
124
|
+
)
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def funding_rate(raw_data: dict) -> dict[str, float]:
|
|
129
|
+
"""Преобразует данные о ставках финансирования в унифицированный формат."""
|
|
130
|
+
data = raw_data["data"][0]
|
|
131
|
+
return {data["instId"]: float(data["fundingRate"]) * 100}
|
|
132
|
+
|
|
133
|
+
@staticmethod
|
|
134
|
+
def open_interest(raw_data: dict) -> OpenInterestDict:
|
|
135
|
+
"""Преобразует данные об открытом интересе в унифицированный формат."""
|
|
136
|
+
return {
|
|
137
|
+
item["instId"]: OpenInterestItem(
|
|
138
|
+
t=int(item["ts"]),
|
|
139
|
+
v=float(item["oiCcy"]),
|
|
140
|
+
)
|
|
141
|
+
for item in raw_data["data"]
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@staticmethod
|
|
145
|
+
def _get_contract_size(symbol: str) -> float:
|
|
146
|
+
"""Возвращает размер контракта для указанного символа тикера."""
|
|
147
|
+
try:
|
|
148
|
+
return ExchangeInfo.get_futures_ticker_info(symbol)["contract_size"] or 1
|
|
149
|
+
except: # noqa
|
|
150
|
+
return 1
|