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.
Files changed (63) hide show
  1. unicex/__init__.py +36 -2
  2. unicex/_abc/exchange_info.py +1 -1
  3. unicex/_abc/uni_client.py +31 -1
  4. unicex/_abc/uni_websocket_manager.py +1 -1
  5. unicex/_base/client.py +7 -0
  6. unicex/_base/websocket.py +31 -9
  7. unicex/binance/adapter.py +7 -16
  8. unicex/binance/client.py +25 -25
  9. unicex/binance/uni_websocket_manager.py +6 -2
  10. unicex/bingx/__init__.py +27 -0
  11. unicex/bingx/adapter.py +284 -0
  12. unicex/bingx/client.py +521 -0
  13. unicex/bingx/exchange_info.py +22 -0
  14. unicex/bingx/uni_client.py +191 -0
  15. unicex/bingx/uni_websocket_manager.py +283 -0
  16. unicex/bingx/user_websocket.py +7 -0
  17. unicex/bingx/websocket_manager.py +118 -0
  18. unicex/bitget/adapter.py +2 -2
  19. unicex/bitget/client.py +64 -64
  20. unicex/bitget/uni_websocket_manager.py +27 -6
  21. unicex/bitget/websocket_manager.py +2 -4
  22. unicex/bybit/adapter.py +1 -0
  23. unicex/bybit/client.py +4 -4
  24. unicex/bybit/exchange_info.py +1 -1
  25. unicex/bybit/uni_websocket_manager.py +33 -4
  26. unicex/bybit/websocket_manager.py +8 -24
  27. unicex/enums.py +19 -3
  28. unicex/extra.py +37 -35
  29. unicex/gate/adapter.py +113 -0
  30. unicex/gate/client.py +9 -9
  31. unicex/gate/uni_client.py +1 -3
  32. unicex/gate/uni_websocket_manager.py +47 -9
  33. unicex/hyperliquid/adapter.py +1 -0
  34. unicex/hyperliquid/client.py +12 -12
  35. unicex/hyperliquid/uni_client.py +4 -7
  36. unicex/hyperliquid/uni_websocket_manager.py +6 -2
  37. unicex/kucoin/__init__.py +27 -0
  38. unicex/kucoin/adapter.py +181 -0
  39. unicex/kucoin/client.py +135 -0
  40. unicex/kucoin/exchange_info.py +50 -0
  41. unicex/kucoin/uni_client.py +208 -0
  42. unicex/kucoin/uni_websocket_manager.py +273 -0
  43. unicex/kucoin/user_websocket.py +7 -0
  44. unicex/kucoin/websocket_manager.py +11 -0
  45. unicex/mapper.py +62 -21
  46. unicex/mexc/adapter.py +104 -0
  47. unicex/mexc/client.py +7 -7
  48. unicex/mexc/uni_client.py +1 -3
  49. unicex/mexc/uni_websocket_manager.py +31 -9
  50. unicex/mexc/websocket_manager.py +27 -6
  51. unicex/okx/adapter.py +51 -0
  52. unicex/okx/client.py +15 -15
  53. unicex/okx/exchange_info.py +2 -2
  54. unicex/okx/uni_websocket_manager.py +50 -9
  55. unicex/okx/websocket_manager.py +119 -166
  56. unicex/types.py +14 -10
  57. unicex/utils.py +44 -1
  58. {unicex-0.13.17.dist-info → unicex-0.16.5.dist-info}/METADATA +7 -5
  59. unicex-0.16.5.dist-info/RECORD +109 -0
  60. unicex-0.13.17.dist-info/RECORD +0 -93
  61. {unicex-0.13.17.dist-info → unicex-0.16.5.dist-info}/WHEEL +0 -0
  62. {unicex-0.13.17.dist-info → unicex-0.16.5.dist-info}/licenses/LICENSE +0 -0
  63. {unicex-0.13.17.dist-info → unicex-0.16.5.dist-info}/top_level.txt +0 -0
unicex/__init__.py CHANGED
@@ -18,7 +18,6 @@ __all__ = [
18
18
  "TickerDailyItem",
19
19
  "KlineDict",
20
20
  "TradeDict",
21
- "AggTradeDict",
22
21
  "RequestMethod",
23
22
  "LoggerLike",
24
23
  "OpenInterestDict",
@@ -81,6 +80,20 @@ __all__ = [
81
80
  "GateioWebsocketManager",
82
81
  "GateioUserWebsocket",
83
82
  "GateioExchangeInfo",
83
+ # Kucoin
84
+ "KucoinClient",
85
+ "KucoinUniClient",
86
+ "KucoinUniWebsocketManager",
87
+ "KucoinWebsocketManager",
88
+ "KucoinUserWebsocket",
89
+ "KucoinExchangeInfo",
90
+ # BingX
91
+ "BingXClient",
92
+ "BingXUniClient",
93
+ "BingXUniWebsocketManager",
94
+ "BingXWebsocketManager",
95
+ "BingXUserWebsocket",
96
+ "BingXExchangeInfo",
84
97
  ]
85
98
 
86
99
  # ruff: noqa
@@ -98,7 +111,6 @@ from .types import (
98
111
  TickerDailyItem,
99
112
  KlineDict,
100
113
  TradeDict,
101
- AggTradeDict,
102
114
  RequestMethod,
103
115
  LoggerLike,
104
116
  OpenInterestDict,
@@ -173,6 +185,24 @@ from .okx import (
173
185
  ExchangeInfo as OkxExchangeInfo,
174
186
  )
175
187
 
188
+ from .kucoin import (
189
+ Client as KucoinClient,
190
+ UniClient as KucoinUniClient,
191
+ UniWebsocketManager as KucoinUniWebsocketManager,
192
+ UserWebsocket as KucoinUserWebsocket,
193
+ WebsocketManager as KucoinWebsocketManager,
194
+ ExchangeInfo as KucoinExchangeInfo,
195
+ )
196
+
197
+ from .bingx import (
198
+ Client as BingXClient,
199
+ UniClient as BingXUniClient,
200
+ UniWebsocketManager as BingXUniWebsocketManager,
201
+ UserWebsocket as BingXUserWebsocket,
202
+ WebsocketManager as BingXWebsocketManager,
203
+ ExchangeInfo as BingXExchangeInfo,
204
+ )
205
+
176
206
 
177
207
  async def load_exchanges_info() -> None:
178
208
  """Единожды загружает информацию о тикерах на всех биржах."""
@@ -184,6 +214,8 @@ async def load_exchanges_info() -> None:
184
214
  HyperliquidExchangeInfo.load_exchange_info(),
185
215
  MexcExchangeInfo.load_exchange_info(),
186
216
  OkxExchangeInfo.load_exchange_info(),
217
+ KucoinExchangeInfo.load_exchange_info(),
218
+ BingXExchangeInfo.load_exchange_info(),
187
219
  )
188
220
 
189
221
 
@@ -197,4 +229,6 @@ async def start_exchanges_info(parse_interval_seconds: int = 60 * 60) -> None:
197
229
  HyperliquidExchangeInfo.start(parse_interval_seconds),
198
230
  MexcExchangeInfo.start(parse_interval_seconds),
199
231
  OkxExchangeInfo.start(parse_interval_seconds),
232
+ KucoinExchangeInfo.start(parse_interval_seconds),
233
+ BingXExchangeInfo.start(parse_interval_seconds),
200
234
  )
@@ -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
@@ -1,5 +1,6 @@
1
1
  __all__ = ["IUniClient"]
2
2
 
3
+ import time
3
4
  from abc import ABC, abstractmethod
4
5
  from typing import Generic, Self, TypeVar, overload
5
6
 
@@ -14,7 +15,7 @@ TClient = TypeVar("TClient", bound="BaseClient")
14
15
 
15
16
 
16
17
  class IUniClient(ABC, Generic[TClient]):
17
- """Интерфейс для реализации асинхронного унифицированного клиента."""
18
+ """Интерфейс для унифицированного клиента."""
18
19
 
19
20
  def __init__(
20
21
  self,
@@ -175,6 +176,35 @@ class IUniClient(ABC, Generic[TClient]):
175
176
  tickers = await self.tickers(only_usdt)
176
177
  return batched_list(tickers, batch_size)
177
178
 
179
+ @staticmethod
180
+ def limit_to_start_and_end_time(
181
+ interval: Timeframe, limit: int, use_milliseconds: bool = True
182
+ ) -> tuple[int, int]:
183
+ """Преобразует `limit` в `start_time` и `end_time`.
184
+
185
+ Параметры:
186
+ interval (`Timeframe`): Интервал времени.
187
+ limit (`int`): Количество элементов.
188
+ use_milliseconds (`bool`): Использовать миллисекунды.
189
+
190
+ Нужен, потому что на некоторых биржах параметр `limit` не принимается напрямую.
191
+ """
192
+ end_time = int(time.time())
193
+ start_time = end_time - (limit * interval.to_seconds) # type: ignore[reportOptionalOperand]
194
+ if use_milliseconds:
195
+ start_time *= 1000
196
+ end_time *= 1000
197
+ return start_time, end_time
198
+
199
+ @staticmethod
200
+ def to_seconds(value: int | None) -> int | None:
201
+ """Преобразует значение из миллисекунд в секунды для передачи в API."""
202
+ if value is None:
203
+ return None
204
+ if value >= 1_000_000_000_000:
205
+ return value // 1000
206
+ return value
207
+
178
208
  @abstractmethod
179
209
  async def futures_tickers(self, only_usdt: bool = True) -> list[str]:
180
210
  """Возвращает список тикеров.
@@ -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/_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__(
@@ -103,6 +103,11 @@ class Websocket:
103
103
  await asyncio.sleep(self._reconnect_timeout)
104
104
  await self.start()
105
105
 
106
+ @property
107
+ def running(self) -> bool:
108
+ """Возвращает статус вебсокета."""
109
+ return self._running
110
+
106
111
  async def _connect(self) -> None:
107
112
  """Подключается к вебсокету и настраивает соединение."""
108
113
  self._logger.debug(f"Establishing connection with {self._url}")
@@ -114,7 +119,7 @@ class Websocket:
114
119
  # Цикл получения сообщений
115
120
  while self._running:
116
121
  message = await conn.recv()
117
- await self._handle_message(message)
122
+ await self._handle_message(message, conn)
118
123
 
119
124
  except websockets.exceptions.ConnectionClosed as e:
120
125
  self._logger.error(f"Websocket connection was closed unexpectedly: {e}")
@@ -128,7 +133,7 @@ class Websocket:
128
133
  else:
129
134
  return # Выходим из итератора, если вебсокет уже выключен
130
135
 
131
- async def _handle_message(self, message: str | bytes) -> None:
136
+ async def _handle_message(self, message: str | bytes, conn: ClientConnection) -> None:
132
137
  """Обрабатывает входящее сообщение вебсокета."""
133
138
  try:
134
139
  # Обновленяем время последнего сообщения
@@ -136,17 +141,22 @@ class Websocket:
136
141
 
137
142
  # Ложим сообщение в очередь, предварительно его сериализуя
138
143
  decoded_message = self._decoder.decode(message)
139
- await self._queue.put(decoded_message)
140
144
 
141
- # Проверяем размер очереди сообщений и выбрасываем ошибку, если он превышает максимальный размер
142
- self._check_queue_size()
145
+ # Проверяем - вдруг декордер вернул "ping"
146
+ if decoded_message == "ping":
147
+ await self._send_pong(conn)
148
+ else:
149
+ await self._queue.put(decoded_message)
150
+
151
+ # Проверяем размер очереди сообщений и выбрасываем ошибку, если он превышает максимальный размер
152
+ self._check_queue_size()
143
153
  except QueueOverflowError:
144
154
  self._logger.error("Message queue is overflow")
145
155
  except orjson.JSONDecodeError as e:
146
156
  if message in ["ping", "pong"]:
147
157
  self._logger.debug(f"Received ping message: {message}")
148
158
  else:
149
- self._logger.error(f"Failed to decode JSON message: {message}, error: {e}")
159
+ self._logger.error(f"Failed to decode message: {message}, error: {e}")
150
160
  except Exception as e:
151
161
  self._logger.error(f"Unexpected error: {e}")
152
162
 
@@ -256,6 +266,18 @@ class Websocket:
256
266
  return
257
267
  await asyncio.sleep(1)
258
268
 
269
+ async def _send_pong(self, conn: ClientConnection) -> None:
270
+ """Отправляет pong сообщение."""
271
+ if self._pong_message:
272
+ if isinstance(self._pong_message, Callable):
273
+ pong_message = self._pong_message()
274
+ else:
275
+ pong_message = self._pong_message
276
+ await conn.send(pong_message)
277
+ else:
278
+ await conn.pong()
279
+ self._logger.debug("Sent pong message")
280
+
259
281
  def __repr__(self) -> str:
260
282
  """Репрезентация вебсокета."""
261
283
  return f"<Websocket(url={self._url[:15]}...)>"
unicex/binance/adapter.py CHANGED
@@ -2,7 +2,6 @@ __all__ = ["Adapter"]
2
2
 
3
3
 
4
4
  from unicex.types import (
5
- AggTradeDict,
6
5
  KlineDict,
7
6
  OpenInterestItem,
8
7
  TickerDailyDict,
@@ -62,18 +61,6 @@ class Adapter:
62
61
  """
63
62
  return {item["symbol"]: float(item["price"]) for item in raw_data}
64
63
 
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
64
  @staticmethod
78
65
  def klines(raw_data: list[list], symbol: str) -> list[KlineDict]:
79
66
  """Преобразует сырой ответ, в котором содержатся данные о котировках тикеров в унифицированный формат.
@@ -127,7 +114,11 @@ class Adapter:
127
114
  Возвращает:
128
115
  OpenInterestItem: Словарь со временем и объемом открытого интереса в монетах.
129
116
  """
130
- return OpenInterestItem(t=raw_data["time"], v=float(raw_data["openInterest"]))
117
+ return OpenInterestItem(
118
+ t=raw_data["time"],
119
+ v=float(raw_data["openInterest"]),
120
+ u="coins",
121
+ )
131
122
 
132
123
  @staticmethod
133
124
  def klines_message(raw_msg: dict) -> list[KlineDict]:
@@ -158,7 +149,7 @@ class Adapter:
158
149
  ]
159
150
 
160
151
  @staticmethod
161
- def aggtrades_message(raw_msg: dict) -> list[AggTradeDict]:
152
+ def aggtrades_message(raw_msg: dict) -> list[TradeDict]:
162
153
  """Преобразует сырое сообщение с вебсокета, в котором содержится информация о
163
154
  аггрегированных сделке/сделках в унифицированный вид.
164
155
 
@@ -170,7 +161,7 @@ class Adapter:
170
161
  """
171
162
  msg = raw_msg.get("data", raw_msg)
172
163
  return [
173
- AggTradeDict(
164
+ TradeDict(
174
165
  t=int(msg["T"]),
175
166
  s=str(msg["s"]),
176
167
  S="SELL" if bool(msg["m"]) else "BUY",
unicex/binance/client.py CHANGED
@@ -7,7 +7,7 @@ from typing import Any
7
7
 
8
8
  from unicex._base import BaseClient
9
9
  from unicex.exceptions import NotAuthorized
10
- from unicex.types import RequestMethod
10
+ from unicex.types import NumberLike, RequestMethod
11
11
  from unicex.utils import dict_to_query_string, filter_params, generate_hmac_sha256_signature
12
12
 
13
13
 
@@ -367,13 +367,13 @@ class Client(BaseClient):
367
367
  symbol: str,
368
368
  side: str,
369
369
  type: str,
370
- quantity: float | None = None,
371
- quote_order_qty: float | None = None,
372
- price: float | None = None,
373
- stop_price: float | None = None,
370
+ quantity: NumberLike | None = None,
371
+ quote_order_qty: NumberLike | None = None,
372
+ price: NumberLike | None = None,
373
+ stop_price: NumberLike | None = None,
374
374
  time_in_force: str | None = None,
375
375
  new_client_order_id: str | None = None,
376
- iceberg_qty: float | None = None,
376
+ iceberg_qty: NumberLike | None = None,
377
377
  new_order_resp_type: str | None = None,
378
378
  self_trade_prevention_mode: str | None = None,
379
379
  ) -> dict:
@@ -405,13 +405,13 @@ class Client(BaseClient):
405
405
  symbol: str,
406
406
  side: str,
407
407
  type: str,
408
- quantity: float | None = None,
409
- quote_order_qty: float | None = None,
410
- price: float | None = None,
411
- stop_price: float | None = None,
408
+ quantity: NumberLike | None = None,
409
+ quote_order_qty: NumberLike | None = None,
410
+ price: NumberLike | None = None,
411
+ stop_price: NumberLike | None = None,
412
412
  time_in_force: str | None = None,
413
413
  new_client_order_id: str | None = None,
414
- iceberg_qty: float | None = None,
414
+ iceberg_qty: NumberLike | None = None,
415
415
  new_order_resp_type: str | None = None,
416
416
  self_trade_prevention_mode: str | None = None,
417
417
  ) -> dict:
@@ -482,26 +482,26 @@ class Client(BaseClient):
482
482
  self,
483
483
  symbol: str,
484
484
  side: str,
485
- quantity: str,
485
+ quantity: NumberLike,
486
486
  list_client_order_id: str | None = None,
487
487
  # ABOVE ORDER
488
488
  above_type: str = "TAKE_PROFIT_LIMIT",
489
489
  above_client_order_id: str | None = None,
490
- above_price: str | None = None,
491
- above_stop_price: str | None = None,
490
+ above_price: NumberLike | None = None,
491
+ above_stop_price: NumberLike | None = None,
492
492
  above_trailing_delta: int | None = None,
493
493
  above_time_in_force: str | None = None,
494
- above_iceberg_qty: str | None = None,
494
+ above_iceberg_qty: NumberLike | None = None,
495
495
  above_strategy_id: int | None = None,
496
496
  above_strategy_type: int | None = None,
497
497
  # BELOW ORDER
498
498
  below_type: str = "STOP_LOSS_LIMIT",
499
499
  below_client_order_id: str | None = None,
500
- below_price: str | None = None,
501
- below_stop_price: str | None = None,
500
+ below_price: NumberLike | None = None,
501
+ below_stop_price: NumberLike | None = None,
502
502
  below_trailing_delta: int | None = None,
503
503
  below_time_in_force: str | None = None,
504
- below_iceberg_qty: str | None = None,
504
+ below_iceberg_qty: NumberLike | None = None,
505
505
  below_strategy_id: int | None = None,
506
506
  below_strategy_type: int | None = None,
507
507
  # EXTRA
@@ -1126,13 +1126,13 @@ class Client(BaseClient):
1126
1126
  symbol: str,
1127
1127
  side: str,
1128
1128
  type: str,
1129
- quantity: float | None = None,
1129
+ quantity: NumberLike | None = None,
1130
1130
  reduce_only: bool | None = None,
1131
- price: float | None = None,
1131
+ price: NumberLike | None = None,
1132
1132
  new_client_order_id: str | None = None,
1133
- stop_price: float | None = None,
1133
+ stop_price: NumberLike | None = None,
1134
1134
  close_position: bool | None = None,
1135
- activation_price: float | None = None,
1135
+ activation_price: NumberLike | None = None,
1136
1136
  callback_rate: float | None = None,
1137
1137
  time_in_force: str | None = None,
1138
1138
  working_type: str | None = None,
@@ -1176,8 +1176,8 @@ class Client(BaseClient):
1176
1176
  orig_client_order_id: str | None = None,
1177
1177
  symbol: str | None = None,
1178
1178
  side: str | None = None,
1179
- quantity: float | None = None,
1180
- price: float | None = None,
1179
+ quantity: NumberLike | None = None,
1180
+ price: NumberLike | None = None,
1181
1181
  price_match: str | None = None,
1182
1182
  ) -> dict:
1183
1183
  """Изменение ордера на фьючерсах.
@@ -1356,7 +1356,7 @@ class Client(BaseClient):
1356
1356
  self,
1357
1357
  symbol: str,
1358
1358
  position_side: str,
1359
- amount: float,
1359
+ amount: NumberLike,
1360
1360
  type: int,
1361
1361
  ) -> dict:
1362
1362
  """Изменение изолированной маржи позиции.
@@ -20,16 +20,20 @@ class UniWebsocketManager(IUniWebsocketManager):
20
20
  """Реализация менеджера асинхронных унифицированных вебсокетов для биржи Binance."""
21
21
 
22
22
  def __init__(
23
- self, client: Client | UniClient | None = None, logger: LoggerLike | None = None
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`): Клиент Binance или унифицированный клиент. Нужен для подключения к приватным топикам.
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
  def klines(
@@ -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)