unicex 0.14.9__py3-none-any.whl → 0.15.1__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 +18 -0
- unicex/_base/websocket.py +26 -9
- unicex/binance/adapter.py +0 -12
- 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 +120 -0
- unicex/enums.py +1 -0
- unicex/extra.py +7 -0
- unicex/mapper.py +59 -24
- unicex/mexc/_spot_ws_proto/__init__.py +335 -335
- unicex/mexc/websocket_manager.py +1 -1
- unicex/okx/websocket_manager.py +3 -3
- unicex/utils.py +17 -0
- {unicex-0.14.9.dist-info → unicex-0.15.1.dist-info}/METADATA +2 -1
- {unicex-0.14.9.dist-info → unicex-0.15.1.dist-info}/RECORD +23 -15
- {unicex-0.14.9.dist-info → unicex-0.15.1.dist-info}/WHEEL +0 -0
- {unicex-0.14.9.dist-info → unicex-0.15.1.dist-info}/licenses/LICENSE +0 -0
- {unicex-0.14.9.dist-info → unicex-0.15.1.dist-info}/top_level.txt +0 -0
unicex/__init__.py
CHANGED
|
@@ -88,6 +88,13 @@ __all__ = [
|
|
|
88
88
|
"KucoinWebsocketManager",
|
|
89
89
|
"KucoinUserWebsocket",
|
|
90
90
|
"KucoinExchangeInfo",
|
|
91
|
+
# BingX
|
|
92
|
+
"BingXClient",
|
|
93
|
+
"BingXUniClient",
|
|
94
|
+
"BingXUniWebsocketManager",
|
|
95
|
+
"BingXWebsocketManager",
|
|
96
|
+
"BingXUserWebsocket",
|
|
97
|
+
"BingXExchangeInfo",
|
|
91
98
|
]
|
|
92
99
|
|
|
93
100
|
# ruff: noqa
|
|
@@ -189,6 +196,15 @@ from .kucoin import (
|
|
|
189
196
|
ExchangeInfo as KucoinExchangeInfo,
|
|
190
197
|
)
|
|
191
198
|
|
|
199
|
+
from .bingx import (
|
|
200
|
+
Client as BingXClient,
|
|
201
|
+
UniClient as BingXUniClient,
|
|
202
|
+
UniWebsocketManager as BingXUniWebsocketManager,
|
|
203
|
+
UserWebsocket as BingXUserWebsocket,
|
|
204
|
+
WebsocketManager as BingXWebsocketManager,
|
|
205
|
+
ExchangeInfo as BingXExchangeInfo,
|
|
206
|
+
)
|
|
207
|
+
|
|
192
208
|
|
|
193
209
|
async def load_exchanges_info() -> None:
|
|
194
210
|
"""Единожды загружает информацию о тикерах на всех биржах."""
|
|
@@ -201,6 +217,7 @@ async def load_exchanges_info() -> None:
|
|
|
201
217
|
MexcExchangeInfo.load_exchange_info(),
|
|
202
218
|
OkxExchangeInfo.load_exchange_info(),
|
|
203
219
|
KucoinExchangeInfo.load_exchange_info(),
|
|
220
|
+
BingXExchangeInfo.load_exchange_info(),
|
|
204
221
|
)
|
|
205
222
|
|
|
206
223
|
|
|
@@ -215,4 +232,5 @@ async def start_exchanges_info(parse_interval_seconds: int = 60 * 60) -> None:
|
|
|
215
232
|
MexcExchangeInfo.start(parse_interval_seconds),
|
|
216
233
|
OkxExchangeInfo.start(parse_interval_seconds),
|
|
217
234
|
KucoinExchangeInfo.start(parse_interval_seconds),
|
|
235
|
+
BingXExchangeInfo.start(parse_interval_seconds),
|
|
218
236
|
)
|
unicex/_base/websocket.py
CHANGED
|
@@ -3,7 +3,7 @@ __all__ = ["Websocket"]
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import time
|
|
5
5
|
from collections.abc import Awaitable, Callable
|
|
6
|
-
from typing import Any, Protocol
|
|
6
|
+
from typing import Any, Literal, Protocol
|
|
7
7
|
|
|
8
8
|
import orjson
|
|
9
9
|
import websockets
|
|
@@ -23,12 +23,12 @@ class Websocket:
|
|
|
23
23
|
class _DecoderProtocol(Protocol):
|
|
24
24
|
"""Протокол декодирования сообщений."""
|
|
25
25
|
|
|
26
|
-
def decode(self, message: Any) -> dict: ...
|
|
26
|
+
def decode(self, message: Any) -> dict | Literal["ping"]: ...
|
|
27
27
|
|
|
28
28
|
class _JsonDecoder:
|
|
29
29
|
"""Протокол декодирования сообщений в формате JSON."""
|
|
30
30
|
|
|
31
|
-
def decode(self, message: Any) -> dict:
|
|
31
|
+
def decode(self, message: Any) -> dict | Literal["ping"]:
|
|
32
32
|
return orjson.loads(message)
|
|
33
33
|
|
|
34
34
|
def __init__(
|
|
@@ -114,7 +114,7 @@ class Websocket:
|
|
|
114
114
|
# Цикл получения сообщений
|
|
115
115
|
while self._running:
|
|
116
116
|
message = await conn.recv()
|
|
117
|
-
await self._handle_message(message)
|
|
117
|
+
await self._handle_message(message, conn)
|
|
118
118
|
|
|
119
119
|
except websockets.exceptions.ConnectionClosed as e:
|
|
120
120
|
self._logger.error(f"Websocket connection was closed unexpectedly: {e}")
|
|
@@ -128,7 +128,7 @@ class Websocket:
|
|
|
128
128
|
else:
|
|
129
129
|
return # Выходим из итератора, если вебсокет уже выключен
|
|
130
130
|
|
|
131
|
-
async def _handle_message(self, message: str | bytes) -> None:
|
|
131
|
+
async def _handle_message(self, message: str | bytes, conn: ClientConnection) -> None:
|
|
132
132
|
"""Обрабатывает входящее сообщение вебсокета."""
|
|
133
133
|
try:
|
|
134
134
|
# Обновленяем время последнего сообщения
|
|
@@ -136,17 +136,22 @@ class Websocket:
|
|
|
136
136
|
|
|
137
137
|
# Ложим сообщение в очередь, предварительно его сериализуя
|
|
138
138
|
decoded_message = self._decoder.decode(message)
|
|
139
|
-
await self._queue.put(decoded_message)
|
|
140
139
|
|
|
141
|
-
# Проверяем
|
|
142
|
-
|
|
140
|
+
# Проверяем - вдруг декордер вернул "ping"
|
|
141
|
+
if decoded_message == "ping":
|
|
142
|
+
await self._send_pong(conn)
|
|
143
|
+
else:
|
|
144
|
+
await self._queue.put(decoded_message)
|
|
145
|
+
|
|
146
|
+
# Проверяем размер очереди сообщений и выбрасываем ошибку, если он превышает максимальный размер
|
|
147
|
+
self._check_queue_size()
|
|
143
148
|
except QueueOverflowError:
|
|
144
149
|
self._logger.error("Message queue is overflow")
|
|
145
150
|
except orjson.JSONDecodeError as e:
|
|
146
151
|
if message in ["ping", "pong"]:
|
|
147
152
|
self._logger.debug(f"Received ping message: {message}")
|
|
148
153
|
else:
|
|
149
|
-
self._logger.error(f"Failed to decode
|
|
154
|
+
self._logger.error(f"Failed to decode message: {message}, error: {e}")
|
|
150
155
|
except Exception as e:
|
|
151
156
|
self._logger.error(f"Unexpected error: {e}")
|
|
152
157
|
|
|
@@ -256,6 +261,18 @@ class Websocket:
|
|
|
256
261
|
return
|
|
257
262
|
await asyncio.sleep(1)
|
|
258
263
|
|
|
264
|
+
async def _send_pong(self, conn: ClientConnection) -> None:
|
|
265
|
+
"""Отправляет pong сообщение."""
|
|
266
|
+
if self._pong_message:
|
|
267
|
+
if isinstance(self._pong_message, Callable):
|
|
268
|
+
pong_message = self._pong_message()
|
|
269
|
+
else:
|
|
270
|
+
pong_message = self._pong_message
|
|
271
|
+
await conn.send(pong_message)
|
|
272
|
+
else:
|
|
273
|
+
await conn.pong()
|
|
274
|
+
self._logger.debug("Sent pong message")
|
|
275
|
+
|
|
259
276
|
def __repr__(self) -> str:
|
|
260
277
|
"""Репрезентация вебсокета."""
|
|
261
278
|
return f"<Websocket(url={self._url[:15]}...)>"
|
unicex/binance/adapter.py
CHANGED
|
@@ -62,18 +62,6 @@ class Adapter:
|
|
|
62
62
|
"""
|
|
63
63
|
return {item["symbol"]: float(item["price"]) for item in raw_data}
|
|
64
64
|
|
|
65
|
-
@staticmethod
|
|
66
|
-
def futures_last_price(raw_data: list[dict]) -> dict[str, float]:
|
|
67
|
-
"""Преобразует сырой ответ, в котором содержатся данные о тикере за последние 24 часа в унифицированный формат.
|
|
68
|
-
|
|
69
|
-
Параметры:
|
|
70
|
-
raw_data (list[dict]): Сырой ответ с биржи.
|
|
71
|
-
|
|
72
|
-
Возвращает:
|
|
73
|
-
dict[str, float]: Словарь, где ключ - тикер, а значение - последняя цена.
|
|
74
|
-
"""
|
|
75
|
-
return Adapter.last_price(raw_data)
|
|
76
|
-
|
|
77
65
|
@staticmethod
|
|
78
66
|
def klines(raw_data: list[list], symbol: str) -> list[KlineDict]:
|
|
79
67
|
"""Преобразует сырой ответ, в котором содержатся данные о котировках тикеров в унифицированный формат.
|
unicex/bingx/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Пакет, содержащий реализации клиентов и менеджеров для работы с биржей BingX."""
|
|
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
|
+
"""Загружает информацию о бирже BingX."""
|
|
22
|
+
await ExchangeInfo.load_exchange_info()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def start_exchange_info(parse_interval_seconds: int = 60 * 60) -> None:
|
|
26
|
+
"""Запускает процесс обновления информации о бирже BingX."""
|
|
27
|
+
await ExchangeInfo.start(parse_interval_seconds)
|
unicex/bingx/adapter.py
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
__all__ = ["Adapter"]
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from unicex.types import (
|
|
6
|
+
KlineDict,
|
|
7
|
+
LiquidationDict,
|
|
8
|
+
OpenInterestItem,
|
|
9
|
+
TickerDailyDict,
|
|
10
|
+
TradeDict,
|
|
11
|
+
)
|
|
12
|
+
from unicex.utils import catch_adapter_errors, decorate_all_methods
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@decorate_all_methods(catch_adapter_errors)
|
|
16
|
+
class Adapter:
|
|
17
|
+
"""Адаптер для унификации данных с BingX API."""
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def tickers(raw_data: dict, only_usdt: bool) -> list[str]:
|
|
21
|
+
"""Преобразует сырой ответ, в котором содержатся данные о тикерах в список тикеров.
|
|
22
|
+
|
|
23
|
+
Параметры:
|
|
24
|
+
raw_data (dict): Сырой ответ с биржи.
|
|
25
|
+
only_usdt (bool): Флаг, указывающий, нужно ли включать только тикеры в паре к USDT.
|
|
26
|
+
|
|
27
|
+
Возвращает:
|
|
28
|
+
list[str]: Список тикеров.
|
|
29
|
+
"""
|
|
30
|
+
data = raw_data["data"]
|
|
31
|
+
items = data["symbols"] if isinstance(data, dict) and "symbols" in data else data
|
|
32
|
+
return [
|
|
33
|
+
item["symbol"] for item in items if item["symbol"].endswith("USDT") or not only_usdt
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def ticker_24hr(raw_data: dict) -> TickerDailyDict:
|
|
38
|
+
"""Преобразует сырой ответ, в котором содержатся данные о тикере за последние 24 часа в унифицированный формат.
|
|
39
|
+
|
|
40
|
+
Параметры:
|
|
41
|
+
raw_data (dict): Сырой ответ с биржи.
|
|
42
|
+
|
|
43
|
+
Возвращает:
|
|
44
|
+
TickerDailyDict: Словарь, где ключ - тикер, а значение - статистика за последние 24 часа.
|
|
45
|
+
"""
|
|
46
|
+
items = raw_data["data"]
|
|
47
|
+
result = {}
|
|
48
|
+
for item in items:
|
|
49
|
+
percent_raw = item["priceChangePercent"]
|
|
50
|
+
percent_str = str(percent_raw).strip()
|
|
51
|
+
if percent_str.endswith("%"):
|
|
52
|
+
percent_str = percent_str[:-1]
|
|
53
|
+
result[item["symbol"]] = {
|
|
54
|
+
"p": float(percent_str),
|
|
55
|
+
"v": float(item["volume"]),
|
|
56
|
+
"q": float(item["quoteVolume"]),
|
|
57
|
+
}
|
|
58
|
+
return result
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def open_interest(raw_data: dict) -> OpenInterestItem:
|
|
62
|
+
"""Преобразует сырой ответ, в котором содержатся данные об открытом интересе, в унифицированный формат.
|
|
63
|
+
|
|
64
|
+
Параметры:
|
|
65
|
+
raw_data (dict): Сырой ответ с биржи.
|
|
66
|
+
|
|
67
|
+
Возвращает:
|
|
68
|
+
OpenInterestItem: Словарь со временем и объемом открытого интереса в монетах.
|
|
69
|
+
"""
|
|
70
|
+
item = raw_data["data"]
|
|
71
|
+
return OpenInterestItem(t=int(item["time"]), v=float(item["openInterest"]))
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def funding_rate(raw_data: dict) -> dict[str, float]:
|
|
75
|
+
"""Преобразует сырой ответ, в котором содержатся данные о ставках финансирования, в унифицированный формат.
|
|
76
|
+
|
|
77
|
+
Параметры:
|
|
78
|
+
raw_data (dict): Сырой ответ с биржи.
|
|
79
|
+
|
|
80
|
+
Возвращает:
|
|
81
|
+
dict[str, float]: Словарь, где ключ - тикер, а значение - ставка финансирования.
|
|
82
|
+
"""
|
|
83
|
+
data = raw_data["data"]
|
|
84
|
+
items = data if isinstance(data, list) else [data]
|
|
85
|
+
result: dict[str, float] = {}
|
|
86
|
+
for item in items:
|
|
87
|
+
if "lastFundingRate" in item:
|
|
88
|
+
result[item["symbol"]] = float(item["lastFundingRate"]) * 100
|
|
89
|
+
elif "fundingRate" in item and item["fundingRate"] != "":
|
|
90
|
+
result[item["symbol"]] = float(item["fundingRate"]) * 100
|
|
91
|
+
return result
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def last_price(raw_data: dict) -> dict[str, float]:
|
|
95
|
+
"""Преобразует сырой ответ, в котором содержатся данные о последней цене, в унифицированный формат.
|
|
96
|
+
|
|
97
|
+
Параметры:
|
|
98
|
+
raw_data (dict): Сырой ответ с биржи.
|
|
99
|
+
|
|
100
|
+
Возвращает:
|
|
101
|
+
dict[str, float]: Словарь, где ключ - тикер, а значение - последняя цена.
|
|
102
|
+
"""
|
|
103
|
+
data = raw_data["data"]
|
|
104
|
+
if isinstance(data, list):
|
|
105
|
+
return {
|
|
106
|
+
item["symbol"]: float(item["lastPrice"] if "lastPrice" in item else item["price"])
|
|
107
|
+
for item in data
|
|
108
|
+
}
|
|
109
|
+
if isinstance(data, dict):
|
|
110
|
+
return {data["symbol"]: float(data["price"])}
|
|
111
|
+
return {}
|
|
112
|
+
|
|
113
|
+
@staticmethod
|
|
114
|
+
def klines(raw_data: dict) -> list[KlineDict]:
|
|
115
|
+
"""Преобразует сырой ответ, в котором содержатся данные о свечах, в унифицированный формат.
|
|
116
|
+
|
|
117
|
+
Параметры:
|
|
118
|
+
raw_data (dict): Сырой ответ с биржи.
|
|
119
|
+
|
|
120
|
+
Возвращает:
|
|
121
|
+
list[KlineDict]: Список словарей, где каждый словарь содержит данные о свече.
|
|
122
|
+
"""
|
|
123
|
+
data = raw_data["data"]
|
|
124
|
+
items = data["klines"] if isinstance(data, dict) and "klines" in data else data
|
|
125
|
+
if "symbol" in raw_data:
|
|
126
|
+
symbol = raw_data["symbol"]
|
|
127
|
+
elif isinstance(data, dict) and "symbol" in data:
|
|
128
|
+
symbol = data["symbol"]
|
|
129
|
+
else:
|
|
130
|
+
symbol = ""
|
|
131
|
+
|
|
132
|
+
if items and isinstance(items[0], dict):
|
|
133
|
+
return [
|
|
134
|
+
KlineDict(
|
|
135
|
+
s=symbol,
|
|
136
|
+
t=int(kline["time"]),
|
|
137
|
+
o=float(kline["open"]),
|
|
138
|
+
h=float(kline["high"]),
|
|
139
|
+
l=float(kline["low"]),
|
|
140
|
+
c=float(kline["close"]),
|
|
141
|
+
v=float(kline["volume"]),
|
|
142
|
+
q=0.0,
|
|
143
|
+
T=None,
|
|
144
|
+
x=None,
|
|
145
|
+
)
|
|
146
|
+
for kline in sorted(
|
|
147
|
+
items,
|
|
148
|
+
key=lambda x: int(x["time"]),
|
|
149
|
+
)
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
return [
|
|
153
|
+
KlineDict(
|
|
154
|
+
s=symbol,
|
|
155
|
+
t=int(kline[0]),
|
|
156
|
+
o=float(kline[1]),
|
|
157
|
+
h=float(kline[2]),
|
|
158
|
+
l=float(kline[3]),
|
|
159
|
+
c=float(kline[4]),
|
|
160
|
+
v=float(kline[5]),
|
|
161
|
+
q=float(kline[7]) if len(kline) > 7 else 0.0,
|
|
162
|
+
T=int(kline[6]) if len(kline) > 6 else None,
|
|
163
|
+
x=None,
|
|
164
|
+
)
|
|
165
|
+
for kline in sorted(
|
|
166
|
+
items,
|
|
167
|
+
key=lambda x: int(x[0]),
|
|
168
|
+
)
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
@staticmethod
|
|
172
|
+
def Klines_message(msg: Any) -> list[KlineDict]:
|
|
173
|
+
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
174
|
+
свече/свечах в унифицированный вид.
|
|
175
|
+
|
|
176
|
+
Параметры:
|
|
177
|
+
msg (Any): Сырое сообщение с вебсокета.
|
|
178
|
+
|
|
179
|
+
Возвращает:
|
|
180
|
+
list[KlineDict]: Список словарей, где каждый словарь содержит данные о свече.
|
|
181
|
+
"""
|
|
182
|
+
...
|
|
183
|
+
|
|
184
|
+
@staticmethod
|
|
185
|
+
def futures_klines_message(msg: Any) -> list[KlineDict]:
|
|
186
|
+
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
187
|
+
свече/свечах в унифицированный вид.
|
|
188
|
+
|
|
189
|
+
Параметры:
|
|
190
|
+
msg (Any): Сырое сообщение с вебсокета.
|
|
191
|
+
|
|
192
|
+
Возвращает:
|
|
193
|
+
list[KlineDict]: Список словарей, где каждый словарь содержит данные о свече.
|
|
194
|
+
"""
|
|
195
|
+
...
|
|
196
|
+
|
|
197
|
+
@staticmethod
|
|
198
|
+
def aggtrades_message(msg: Any) -> list[TradeDict]:
|
|
199
|
+
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
200
|
+
аггрегированных сделке/сделках в унифицированный вид.
|
|
201
|
+
|
|
202
|
+
Параметры:
|
|
203
|
+
msg (Any): Сырое сообщение с вебсокета.
|
|
204
|
+
|
|
205
|
+
Возвращает:
|
|
206
|
+
list[KlineDict]: Список словарей, где каждый словарь содержит данные о сделке.
|
|
207
|
+
"""
|
|
208
|
+
...
|
|
209
|
+
|
|
210
|
+
@staticmethod
|
|
211
|
+
def futures_aggtrades_message(msg: Any) -> list[TradeDict]:
|
|
212
|
+
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
213
|
+
аггрегированных сделке/сделках в унифицированный вид.
|
|
214
|
+
|
|
215
|
+
Параметры:
|
|
216
|
+
msg (Any): Сырое сообщение с вебсокета.
|
|
217
|
+
|
|
218
|
+
Возвращает:
|
|
219
|
+
list[KlineDict]: Список словарей, где каждый словарь содержит данные о сделке.
|
|
220
|
+
"""
|
|
221
|
+
...
|
|
222
|
+
|
|
223
|
+
@staticmethod
|
|
224
|
+
def trades_message(msg: Any) -> list[TradeDict]:
|
|
225
|
+
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
226
|
+
сделке/сделках в унифицированный вид.
|
|
227
|
+
|
|
228
|
+
Параметры:
|
|
229
|
+
msg (Any): Сырое сообщение с вебсокета.
|
|
230
|
+
|
|
231
|
+
Возвращает:
|
|
232
|
+
list[KlineDict]: Список словарей, где каждый словарь содержит данные о сделке.
|
|
233
|
+
"""
|
|
234
|
+
return [
|
|
235
|
+
TradeDict(
|
|
236
|
+
t=int(trade["T"]),
|
|
237
|
+
s=str(trade["s"]),
|
|
238
|
+
S="SELL" if bool(trade["m"]) else "BUY",
|
|
239
|
+
p=float(trade["p"]),
|
|
240
|
+
v=float(trade["q"]),
|
|
241
|
+
)
|
|
242
|
+
for trade in sorted(
|
|
243
|
+
msg["data"],
|
|
244
|
+
key=lambda x: int(x["T"]),
|
|
245
|
+
)
|
|
246
|
+
]
|
|
247
|
+
|
|
248
|
+
@staticmethod
|
|
249
|
+
def futures_trades_message(msg: Any) -> list[TradeDict]:
|
|
250
|
+
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
251
|
+
сделке/сделках в унифицированный вид.
|
|
252
|
+
|
|
253
|
+
Параметры:
|
|
254
|
+
msg (Any): Сырое сообщение с вебсокета.
|
|
255
|
+
|
|
256
|
+
Возвращает:
|
|
257
|
+
list[KlineDict]: Список словарей, где каждый словарь содержит данные о сделке.
|
|
258
|
+
"""
|
|
259
|
+
return [
|
|
260
|
+
TradeDict(
|
|
261
|
+
t=int(trade["T"]),
|
|
262
|
+
s=str(trade["s"]),
|
|
263
|
+
S="SELL" if bool(trade["m"]) else "BUY",
|
|
264
|
+
p=float(trade["p"]),
|
|
265
|
+
v=float(trade["q"]),
|
|
266
|
+
)
|
|
267
|
+
for trade in sorted(
|
|
268
|
+
msg["data"],
|
|
269
|
+
key=lambda x: int(x["T"]),
|
|
270
|
+
)
|
|
271
|
+
]
|
|
272
|
+
|
|
273
|
+
@staticmethod
|
|
274
|
+
def liquidations_message(msg: Any) -> list[LiquidationDict]:
|
|
275
|
+
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
276
|
+
ликвидации/ликвидациях в унифицированный вид.
|
|
277
|
+
|
|
278
|
+
Параметры:
|
|
279
|
+
msg (Any): Сырое сообщение с вебсокета.
|
|
280
|
+
|
|
281
|
+
Возвращает:
|
|
282
|
+
list[LiquidationDict]: Список словарей, где каждый словарь содержит данные о ликвидации.
|
|
283
|
+
"""
|
|
284
|
+
...
|