unicex 0.15.4__tar.gz → 0.16.0__tar.gz

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 (114) hide show
  1. {unicex-0.15.4/unicex.egg-info → unicex-0.16.0}/PKG-INFO +5 -5
  2. {unicex-0.15.4 → unicex-0.16.0}/README.md +4 -4
  3. {unicex-0.15.4 → unicex-0.16.0}/pyproject.toml +1 -1
  4. {unicex-0.15.4 → unicex-0.16.0}/unicex/bingx/uni_websocket_manager.py +2 -2
  5. {unicex-0.15.4 → unicex-0.16.0}/unicex/bitget/uni_websocket_manager.py +21 -4
  6. {unicex-0.15.4 → unicex-0.16.0}/unicex/gate/adapter.py +102 -0
  7. {unicex-0.15.4 → unicex-0.16.0}/unicex/gate/uni_websocket_manager.py +41 -7
  8. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/adapter.py +103 -0
  9. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/uni_websocket_manager.py +25 -7
  10. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/websocket_manager.py +24 -1
  11. {unicex-0.15.4 → unicex-0.16.0}/unicex/okx/adapter.py +50 -0
  12. {unicex-0.15.4 → unicex-0.16.0}/unicex/okx/uni_websocket_manager.py +44 -7
  13. {unicex-0.15.4 → unicex-0.16.0}/unicex/types.py +2 -2
  14. {unicex-0.15.4 → unicex-0.16.0/unicex.egg-info}/PKG-INFO +5 -5
  15. {unicex-0.15.4 → unicex-0.16.0}/LICENSE +0 -0
  16. {unicex-0.15.4 → unicex-0.16.0}/setup.cfg +0 -0
  17. {unicex-0.15.4 → unicex-0.16.0}/unicex/__init__.py +0 -0
  18. {unicex-0.15.4 → unicex-0.16.0}/unicex/_abc/__init__.py +0 -0
  19. {unicex-0.15.4 → unicex-0.16.0}/unicex/_abc/exchange_info.py +0 -0
  20. {unicex-0.15.4 → unicex-0.16.0}/unicex/_abc/uni_client.py +0 -0
  21. {unicex-0.15.4 → unicex-0.16.0}/unicex/_abc/uni_websocket_manager.py +0 -0
  22. {unicex-0.15.4 → unicex-0.16.0}/unicex/_base/__init__.py +0 -0
  23. {unicex-0.15.4 → unicex-0.16.0}/unicex/_base/client.py +0 -0
  24. {unicex-0.15.4 → unicex-0.16.0}/unicex/_base/websocket.py +0 -0
  25. {unicex-0.15.4 → unicex-0.16.0}/unicex/binance/__init__.py +0 -0
  26. {unicex-0.15.4 → unicex-0.16.0}/unicex/binance/adapter.py +0 -0
  27. {unicex-0.15.4 → unicex-0.16.0}/unicex/binance/client.py +0 -0
  28. {unicex-0.15.4 → unicex-0.16.0}/unicex/binance/exchange_info.py +0 -0
  29. {unicex-0.15.4 → unicex-0.16.0}/unicex/binance/uni_client.py +0 -0
  30. {unicex-0.15.4 → unicex-0.16.0}/unicex/binance/uni_websocket_manager.py +0 -0
  31. {unicex-0.15.4 → unicex-0.16.0}/unicex/binance/user_websocket.py +0 -0
  32. {unicex-0.15.4 → unicex-0.16.0}/unicex/binance/websocket_manager.py +0 -0
  33. {unicex-0.15.4 → unicex-0.16.0}/unicex/bingx/__init__.py +0 -0
  34. {unicex-0.15.4 → unicex-0.16.0}/unicex/bingx/adapter.py +0 -0
  35. {unicex-0.15.4 → unicex-0.16.0}/unicex/bingx/client.py +0 -0
  36. {unicex-0.15.4 → unicex-0.16.0}/unicex/bingx/exchange_info.py +0 -0
  37. {unicex-0.15.4 → unicex-0.16.0}/unicex/bingx/uni_client.py +0 -0
  38. {unicex-0.15.4 → unicex-0.16.0}/unicex/bingx/user_websocket.py +0 -0
  39. {unicex-0.15.4 → unicex-0.16.0}/unicex/bingx/websocket_manager.py +0 -0
  40. {unicex-0.15.4 → unicex-0.16.0}/unicex/bitget/__init__.py +0 -0
  41. {unicex-0.15.4 → unicex-0.16.0}/unicex/bitget/adapter.py +0 -0
  42. {unicex-0.15.4 → unicex-0.16.0}/unicex/bitget/client.py +0 -0
  43. {unicex-0.15.4 → unicex-0.16.0}/unicex/bitget/exchange_info.py +0 -0
  44. {unicex-0.15.4 → unicex-0.16.0}/unicex/bitget/uni_client.py +0 -0
  45. {unicex-0.15.4 → unicex-0.16.0}/unicex/bitget/user_websocket.py +0 -0
  46. {unicex-0.15.4 → unicex-0.16.0}/unicex/bitget/websocket_manager.py +0 -0
  47. {unicex-0.15.4 → unicex-0.16.0}/unicex/bybit/__init__.py +0 -0
  48. {unicex-0.15.4 → unicex-0.16.0}/unicex/bybit/adapter.py +0 -0
  49. {unicex-0.15.4 → unicex-0.16.0}/unicex/bybit/client.py +0 -0
  50. {unicex-0.15.4 → unicex-0.16.0}/unicex/bybit/exchange_info.py +0 -0
  51. {unicex-0.15.4 → unicex-0.16.0}/unicex/bybit/uni_client.py +0 -0
  52. {unicex-0.15.4 → unicex-0.16.0}/unicex/bybit/uni_websocket_manager.py +0 -0
  53. {unicex-0.15.4 → unicex-0.16.0}/unicex/bybit/user_websocket.py +0 -0
  54. {unicex-0.15.4 → unicex-0.16.0}/unicex/bybit/websocket_manager.py +0 -0
  55. {unicex-0.15.4 → unicex-0.16.0}/unicex/enums.py +0 -0
  56. {unicex-0.15.4 → unicex-0.16.0}/unicex/exceptions.py +0 -0
  57. {unicex-0.15.4 → unicex-0.16.0}/unicex/extra.py +0 -0
  58. {unicex-0.15.4 → unicex-0.16.0}/unicex/gate/__init__.py +0 -0
  59. {unicex-0.15.4 → unicex-0.16.0}/unicex/gate/client.py +0 -0
  60. {unicex-0.15.4 → unicex-0.16.0}/unicex/gate/exchange_info.py +0 -0
  61. {unicex-0.15.4 → unicex-0.16.0}/unicex/gate/uni_client.py +0 -0
  62. {unicex-0.15.4 → unicex-0.16.0}/unicex/gate/user_websocket.py +0 -0
  63. {unicex-0.15.4 → unicex-0.16.0}/unicex/gate/websocket_manager.py +0 -0
  64. {unicex-0.15.4 → unicex-0.16.0}/unicex/hyperliquid/__init__.py +0 -0
  65. {unicex-0.15.4 → unicex-0.16.0}/unicex/hyperliquid/adapter.py +0 -0
  66. {unicex-0.15.4 → unicex-0.16.0}/unicex/hyperliquid/client.py +0 -0
  67. {unicex-0.15.4 → unicex-0.16.0}/unicex/hyperliquid/exchange_info.py +0 -0
  68. {unicex-0.15.4 → unicex-0.16.0}/unicex/hyperliquid/uni_client.py +0 -0
  69. {unicex-0.15.4 → unicex-0.16.0}/unicex/hyperliquid/uni_websocket_manager.py +0 -0
  70. {unicex-0.15.4 → unicex-0.16.0}/unicex/hyperliquid/user_websocket.py +0 -0
  71. {unicex-0.15.4 → unicex-0.16.0}/unicex/hyperliquid/websocket_manager.py +0 -0
  72. {unicex-0.15.4 → unicex-0.16.0}/unicex/kucoin/__init__.py +0 -0
  73. {unicex-0.15.4 → unicex-0.16.0}/unicex/kucoin/adapter.py +0 -0
  74. {unicex-0.15.4 → unicex-0.16.0}/unicex/kucoin/client.py +0 -0
  75. {unicex-0.15.4 → unicex-0.16.0}/unicex/kucoin/exchange_info.py +0 -0
  76. {unicex-0.15.4 → unicex-0.16.0}/unicex/kucoin/uni_client.py +0 -0
  77. {unicex-0.15.4 → unicex-0.16.0}/unicex/kucoin/uni_websocket_manager.py +0 -0
  78. {unicex-0.15.4 → unicex-0.16.0}/unicex/kucoin/user_websocket.py +0 -0
  79. {unicex-0.15.4 → unicex-0.16.0}/unicex/kucoin/websocket_manager.py +0 -0
  80. {unicex-0.15.4 → unicex-0.16.0}/unicex/mapper.py +0 -0
  81. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/__init__.py +0 -0
  82. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PrivateAccountV3Api_pb2.py +0 -0
  83. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PrivateDealsV3Api_pb2.py +0 -0
  84. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PrivateOrdersV3Api_pb2.py +0 -0
  85. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PublicAggreBookTickerV3Api_pb2.py +0 -0
  86. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PublicAggreDealsV3Api_pb2.py +0 -0
  87. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PublicAggreDepthsV3Api_pb2.py +0 -0
  88. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PublicBookTickerBatchV3Api_pb2.py +0 -0
  89. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PublicBookTickerV3Api_pb2.py +0 -0
  90. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PublicDealsV3Api_pb2.py +0 -0
  91. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PublicFuture_pb2.py +0 -0
  92. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PublicIncreaseDepthsBatchV3Api_pb2.py +0 -0
  93. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PublicIncreaseDepthsV3Api_pb2.py +0 -0
  94. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PublicLimitDepthsV3Api_pb2.py +0 -0
  95. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PublicMiniTickerV3Api_pb2.py +0 -0
  96. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PublicMiniTickersV3Api_pb2.py +0 -0
  97. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PublicSpotKlineV3Api_pb2.py +0 -0
  98. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/PushDataV3ApiWrapper_pb2.py +0 -0
  99. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/_spot_ws_proto/__init__.py +0 -0
  100. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/client.py +0 -0
  101. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/exchange_info.py +0 -0
  102. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/uni_client.py +0 -0
  103. {unicex-0.15.4 → unicex-0.16.0}/unicex/mexc/user_websocket.py +0 -0
  104. {unicex-0.15.4 → unicex-0.16.0}/unicex/okx/__init__.py +0 -0
  105. {unicex-0.15.4 → unicex-0.16.0}/unicex/okx/client.py +0 -0
  106. {unicex-0.15.4 → unicex-0.16.0}/unicex/okx/exchange_info.py +0 -0
  107. {unicex-0.15.4 → unicex-0.16.0}/unicex/okx/uni_client.py +0 -0
  108. {unicex-0.15.4 → unicex-0.16.0}/unicex/okx/user_websocket.py +0 -0
  109. {unicex-0.15.4 → unicex-0.16.0}/unicex/okx/websocket_manager.py +0 -0
  110. {unicex-0.15.4 → unicex-0.16.0}/unicex/utils.py +0 -0
  111. {unicex-0.15.4 → unicex-0.16.0}/unicex.egg-info/SOURCES.txt +0 -0
  112. {unicex-0.15.4 → unicex-0.16.0}/unicex.egg-info/dependency_links.txt +0 -0
  113. {unicex-0.15.4 → unicex-0.16.0}/unicex.egg-info/requires.txt +0 -0
  114. {unicex-0.15.4 → unicex-0.16.0}/unicex.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unicex
3
- Version: 0.15.4
3
+ Version: 0.16.0
4
4
  Summary: Unified Crypto Exchange API
5
5
  Author-email: LoveBloodAndDiamonds <ayazshakirzyanov27@gmail.com>
6
6
  License: BSD 3-Clause License
@@ -56,12 +56,12 @@ Dynamic: license-file
56
56
  | Exchange | Client | Auth | WS Manager | User WS | Uni Client | Uni WS Manager | ExchangeInfo |
57
57
  |-----------------|--------|------|------------|---------|------------|----------------|--------------|
58
58
  | **Binance** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
59
- | **Bitget** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
59
+ | **Bitget** | ✓ | ✓ | ✓ | | ✓ || ✓ |
60
60
  | **Bybit** | ✓ | ✓ | ✓ | | ✓ | ✓ | ✓ |
61
- | **Gateio** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
61
+ | **Gateio** | ✓ | ✓ | ✓ | | ✓ || ✓ |
62
62
  | **Hyperliquid** | ✓ | ✓ | ✓ | ✓ | ✓ | | |
63
- | **Mexc** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
64
- | **Okx** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
63
+ | **Mexc** | ✓ | ✓ | ✓ | | ✓ || ✓ |
64
+ | **Okx** | ✓ | ✓ | ✓ | | ✓ || ✓ |
65
65
  | **Kucoin** | | | | | ✓ | | |
66
66
  | **BingX** | | | | | ✓ | | |
67
67
  ---
@@ -7,12 +7,12 @@
7
7
  | Exchange | Client | Auth | WS Manager | User WS | Uni Client | Uni WS Manager | ExchangeInfo |
8
8
  |-----------------|--------|------|------------|---------|------------|----------------|--------------|
9
9
  | **Binance** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
10
- | **Bitget** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
10
+ | **Bitget** | ✓ | ✓ | ✓ | | ✓ || ✓ |
11
11
  | **Bybit** | ✓ | ✓ | ✓ | | ✓ | ✓ | ✓ |
12
- | **Gateio** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
12
+ | **Gateio** | ✓ | ✓ | ✓ | | ✓ || ✓ |
13
13
  | **Hyperliquid** | ✓ | ✓ | ✓ | ✓ | ✓ | | |
14
- | **Mexc** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
15
- | **Okx** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
14
+ | **Mexc** | ✓ | ✓ | ✓ | | ✓ || ✓ |
15
+ | **Okx** | ✓ | ✓ | ✓ | | ✓ || ✓ |
16
16
  | **Kucoin** | | | | | ✓ | | |
17
17
  | **BingX** | | | | | ✓ | | |
18
18
  ---
@@ -4,7 +4,7 @@ name = "unicex"
4
4
  # • PATCH (x.y.Z) → увеличивается при багфиксе, который не ломает совместимость.
5
5
  # • MINOR (x.Y.z) → увеличивается при добавлении новой функциональности, но без ломающих изменений (backward-compatible).
6
6
  # • MAJOR (X.y.z) → увеличивается при изменениях, которые ломают обратную совместимость.
7
- version = "0.15.4"
7
+ version = "0.16.0"
8
8
 
9
9
  description = "Unified Crypto Exchange API "
10
10
  readme = "README.md"
@@ -199,7 +199,7 @@ class UniWebsocketManager(IUniWebsocketManager):
199
199
  Возвращает:
200
200
  `Websocket`: Экземпляр вебсокета.
201
201
  """
202
- raise NotImplementedError()
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
- raise NotImplementedError()
283
+ return self.futures_trades(callback, symbol=symbol, symbols=symbols) # type: ignore
@@ -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
- raise NotImplementedError()
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
- raise NotImplementedError()
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
- raise NotImplementedError()
296
+ return self.futures_trades(callback=callback, symbol=symbol, symbols=symbols) # type: ignore[reportCallIssue]
@@ -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
 
@@ -177,3 +179,103 @@ class Adapter:
177
179
  )
178
180
  for item in raw_data
179
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
- raise NotImplementedError()
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
- raise NotImplementedError()
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
- raise NotImplementedError()
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
- raise NotImplementedError()
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
- raise NotImplementedError()
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
- raise NotImplementedError()
307
+ return self.futures_trades(callback=callback, symbol=symbol, symbols=symbols) # type: ignore[reportCallIssue]
@@ -1,11 +1,14 @@
1
1
  __all__ = ["Adapter"]
2
2
 
3
+ from typing import Any
4
+
3
5
  from unicex.types import (
4
6
  KlineDict,
5
7
  OpenInterestDict,
6
8
  OpenInterestItem,
7
9
  TickerDailyDict,
8
10
  TickerDailyItem,
11
+ TradeDict,
9
12
  )
10
13
  from unicex.utils import catch_adapter_errors, decorate_all_methods
11
14
 
@@ -231,6 +234,106 @@ class Adapter:
231
234
 
232
235
  return sorted(klines, key=lambda kline_item: kline_item["t"])
233
236
 
237
+ @staticmethod
238
+ def klines_message(raw_msg: Any) -> list[KlineDict]:
239
+ """Преобразует вебсокет-сообщение со свечами в унифицированный формат.
240
+
241
+ Параметры:
242
+ raw_msg (Any): Сырое сообщение с вебсокета.
243
+
244
+ Возвращает:
245
+ list[KlineDict]: Список свечей в унифицированном формате.
246
+ """
247
+ kline = raw_msg["publicSpotKline"]
248
+ return [
249
+ KlineDict(
250
+ s=raw_msg["symbol"],
251
+ t=int(kline["windowStart"]) * 1000,
252
+ o=float(kline["openingPrice"]),
253
+ h=float(kline["highestPrice"]),
254
+ l=float(kline["lowestPrice"]),
255
+ c=float(kline["closingPrice"]),
256
+ v=float(kline["volume"]),
257
+ T=int(kline["windowEnd"]) * 1000,
258
+ x=None,
259
+ q=float(kline["amount"]),
260
+ )
261
+ ]
262
+
263
+ @staticmethod
264
+ def futures_klines_message(raw_msg: Any) -> list[KlineDict]:
265
+ """Преобразует вебсокет-сообщение со свечами в унифицированный формат.
266
+
267
+ Параметры:
268
+ raw_msg (Any): Сырое сообщение с вебсокета.
269
+
270
+ Возвращает:
271
+ list[KlineDict]: Список свечей в унифицированном формате.
272
+ """
273
+ data = raw_msg["data"]
274
+ return [
275
+ KlineDict(
276
+ s=data["symbol"],
277
+ t=data["t"] * 1000,
278
+ o=data["o"],
279
+ h=data["h"],
280
+ l=data["l"],
281
+ c=data["c"],
282
+ v=data["q"], # Контракты
283
+ q=data["a"],
284
+ T=None,
285
+ x=None,
286
+ )
287
+ ]
288
+
289
+ @staticmethod
290
+ def trades_message(raw_msg: Any) -> list[TradeDict]:
291
+ """Преобразует вебсокет-сообщение со сделками в унифицированный формат.
292
+
293
+ Параметры:
294
+ raw_msg (Any): Сырое сообщение с вебсокета.
295
+
296
+ Возвращает:
297
+ list[TradeDict]: Список сделок в унифицированном формате.
298
+ """
299
+ return [
300
+ TradeDict(
301
+ t=trade["time"],
302
+ s=raw_msg["symbol"],
303
+ S="BUY" if trade["tradeType"] == 1 else "SELL",
304
+ p=float(trade["price"]),
305
+ v=float(trade["quantity"]),
306
+ )
307
+ for trade in sorted(
308
+ raw_msg["publicAggreDeals"]["deals"],
309
+ key=lambda item: item["time"],
310
+ )
311
+ ]
312
+
313
+ @staticmethod
314
+ def futures_trades_message(raw_msg: Any) -> list[TradeDict]:
315
+ """Преобразует вебсокет-сообщение со сделками в унифицированный формат.
316
+
317
+ Параметры:
318
+ raw_msg (Any): Сырое сообщение с вебсокета.
319
+
320
+ Возвращает:
321
+ list[TradeDict]: Список сделок в унифицированном формате.
322
+ """
323
+ return [
324
+ TradeDict(
325
+ t=item["t"],
326
+ s=raw_msg["symbol"],
327
+ S="BUY" if item["T"] == 1 else "SELL",
328
+ p=item["p"],
329
+ v=item["v"],
330
+ )
331
+ for item in sorted(
332
+ raw_msg["data"],
333
+ key=lambda item: item["t"],
334
+ )
335
+ ]
336
+
234
337
  @staticmethod
235
338
  def _get_contract_size(symbol: str) -> float:
236
339
  """Возвращает размер контракта для указанного символа тикера."""
@@ -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,15 @@ class UniWebsocketManager(IUniWebsocketManager):
76
76
  Возвращает:
77
77
  `Websocket`: Экземпляр вебсокета для управления соединением.
78
78
  """
79
- raise NotImplementedError()
79
+ wrapper = self._make_wrapper(self._adapter.klines_message, callback)
80
+ return self._websocket_manager.klines(
81
+ callback=wrapper,
82
+ symbol=symbol,
83
+ symbols=symbols,
84
+ interval=timeframe.to_exchange_format(
85
+ Exchange.MEXC, MarketType.FUTURES
86
+ ), # Тут фьючерсный интервал, потому что для вебсокета MEXC решили что сделают так (идиоты)
87
+ )
80
88
 
81
89
  @overload
82
90
  def futures_klines(
@@ -118,7 +126,13 @@ class UniWebsocketManager(IUniWebsocketManager):
118
126
  Возвращает:
119
127
  `Websocket`: Экземпляр вебсокета.
120
128
  """
121
- raise NotImplementedError()
129
+ wrapper = self._make_wrapper(self._adapter.futures_klines_message, callback)
130
+ return self._websocket_manager.futures_kline(
131
+ callback=wrapper,
132
+ symbol=symbol,
133
+ symbols=symbols,
134
+ interval=timeframe.to_exchange_format(Exchange.MEXC, MarketType.FUTURES),
135
+ )
122
136
 
123
137
  @overload
124
138
  def trades(
@@ -156,7 +170,8 @@ class UniWebsocketManager(IUniWebsocketManager):
156
170
  Возвращает:
157
171
  `Websocket`: Экземпляр вебсокета.
158
172
  """
159
- raise NotImplementedError()
173
+ wrapper = self._make_wrapper(self._adapter.trades_message, callback)
174
+ return self._websocket_manager.trade(callback=wrapper, symbol=symbol, symbols=symbols)
160
175
 
161
176
  @overload
162
177
  def aggtrades(
@@ -194,7 +209,7 @@ class UniWebsocketManager(IUniWebsocketManager):
194
209
  Возвращает:
195
210
  `Websocket`: Экземпляр вебсокета.
196
211
  """
197
- raise NotImplementedError()
212
+ return self.trades(callback=callback, symbol=symbol, symbols=symbols) # type: ignore[reportCallIssue]
198
213
 
199
214
  @overload
200
215
  def futures_trades(
@@ -232,7 +247,10 @@ class UniWebsocketManager(IUniWebsocketManager):
232
247
  Возвращает:
233
248
  `Websocket`: Экземпляр вебсокета.
234
249
  """
235
- raise NotImplementedError()
250
+ wrapper = self._make_wrapper(self._adapter.futures_trades_message, callback)
251
+ return self._websocket_manager.futures_trade(
252
+ callback=wrapper, symbol=symbol, symbols=symbols
253
+ )
236
254
 
237
255
  @overload
238
256
  def futures_aggtrades(
@@ -270,4 +288,4 @@ class UniWebsocketManager(IUniWebsocketManager):
270
288
  Возвращает:
271
289
  `Websocket`: Экземпляр вебсокета.
272
290
  """
273
- raise NotImplementedError()
291
+ return self.futures_trades(callback=callback, symbol=symbol, symbols=symbols) # type: ignore[reportCallIssue]
@@ -380,7 +380,30 @@ class WebsocketManager:
380
380
  `Websocket`: Объект для управления вебсокет соединением.
381
381
  """
382
382
  subscription_messages = self._generate_futures_subscription_message(
383
- topic="sub.deal", symbol=symbol, symbols=symbols, interval=interval
383
+ topic="sub.kline", symbol=symbol, symbols=symbols, interval=interval
384
+ )
385
+ return self._create_futures_websocket(callback, subscription_messages)
386
+
387
+ def futures_trade(
388
+ self,
389
+ callback: CallbackType,
390
+ symbol: str | None = None,
391
+ symbols: Sequence[str] | None = None,
392
+ ) -> Websocket:
393
+ """Создает вебсокет для получения сделок по фьючерсным контрактам.
394
+
395
+ https://mexcdevelop.github.io/apidocs/contract_v1_en/#public-channels
396
+
397
+ Параметры:
398
+ callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
399
+ symbol (`str | None`): Символ фьючерсного контракта.
400
+ symbols (`Sequence[str] | None`): Последовательность символов фьючерсных контрактов.
401
+
402
+ Возвращает:
403
+ `Websocket`: Объект для управления вебсокет соединением.
404
+ """
405
+ subscription_messages = self._generate_futures_subscription_message(
406
+ topic="sub.deal", symbol=symbol, symbols=symbols
384
407
  )
385
408
  return self._create_futures_websocket(callback, subscription_messages)
386
409
 
@@ -1,12 +1,15 @@
1
1
  __all__ = ["Adapter"]
2
2
 
3
3
 
4
+ from typing import Any
5
+
4
6
  from unicex.types import (
5
7
  KlineDict,
6
8
  OpenInterestDict,
7
9
  OpenInterestItem,
8
10
  TickerDailyDict,
9
11
  TickerDailyItem,
12
+ TradeDict,
10
13
  )
11
14
  from unicex.utils import catch_adapter_errors, decorate_all_methods
12
15
 
@@ -139,3 +142,50 @@ class Adapter:
139
142
  )
140
143
  for item in raw_data["data"]
141
144
  }
145
+
146
+ @staticmethod
147
+ def klines_message(raw_msg: Any) -> list[KlineDict]:
148
+ """Преобразует вебсокет-сообщение со свечами в унифицированный формат.
149
+
150
+ Параметры:
151
+ raw_msg (Any): Сырое сообщение с вебсокета.
152
+
153
+ Возвращает:
154
+ list[KlineDict]: Список свечей в унифицированном формате.
155
+ """
156
+ return [
157
+ KlineDict(
158
+ s=raw_msg["arg"]["instId"],
159
+ t=int(kline[0]),
160
+ o=float(kline[1]),
161
+ h=float(kline[2]),
162
+ l=float(kline[3]),
163
+ c=float(kline[4]),
164
+ v=float(kline[6]),
165
+ q=float(kline[7]),
166
+ T=None,
167
+ x=bool(int(kline[8])),
168
+ )
169
+ for kline in sorted(raw_msg["data"], key=lambda item: int(item[0]))
170
+ ]
171
+
172
+ @staticmethod
173
+ def trades_message(raw_msg: Any) -> list[TradeDict]:
174
+ """Преобразует вебсокет-сообщение со сделками в унифицированный формат.
175
+
176
+ Параметры:
177
+ raw_msg (Any): Сырое сообщение с вебсокета.
178
+
179
+ Возвращает:
180
+ list[TradeDict]: Список сделок в унифицированном формате.
181
+ """
182
+ return [
183
+ TradeDict(
184
+ t=int(trade["ts"]),
185
+ s=trade["instId"],
186
+ S=trade["side"].upper(),
187
+ p=float(trade["px"]),
188
+ v=float(trade["sz"]),
189
+ )
190
+ for trade in sorted(raw_msg["data"], key=lambda item: int(item["ts"]))
191
+ ]
@@ -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, Timeframe
9
9
  from unicex.types import LoggerLike
10
10
 
11
11
  from .adapter import Adapter
@@ -36,6 +36,23 @@ 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_symbol(
40
+ self,
41
+ symbol: str | None,
42
+ symbols: Sequence[str] | None,
43
+ ) -> 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
+ normalized = list(symbols)
51
+ if len(normalized) != 1:
52
+ raise ValueError("OKX websocket поддерживает только один тикер на соединение")
53
+ return normalized[0]
54
+ raise ValueError("Either symbol or symbols must be provided")
55
+
39
56
  @overload
40
57
  def klines(
41
58
  self,
@@ -76,7 +93,13 @@ class UniWebsocketManager(IUniWebsocketManager):
76
93
  Возвращает:
77
94
  `Websocket`: Экземпляр вебсокета для управления соединением.
78
95
  """
79
- raise NotImplementedError()
96
+ inst_id = self._normalize_symbol(symbol, symbols)
97
+ wrapper = self._make_wrapper(self._adapter.klines_message, callback)
98
+ return self._websocket_manager.candlesticks(
99
+ callback=wrapper,
100
+ interval=timeframe.to_exchange_format(Exchange.OKX), # type: ignore
101
+ inst_id=inst_id,
102
+ )
80
103
 
81
104
  @overload
82
105
  def futures_klines(
@@ -118,7 +141,13 @@ class UniWebsocketManager(IUniWebsocketManager):
118
141
  Возвращает:
119
142
  `Websocket`: Экземпляр вебсокета.
120
143
  """
121
- raise NotImplementedError()
144
+ inst_id = self._normalize_symbol(symbol, symbols)
145
+ wrapper = self._make_wrapper(self._adapter.klines_message, callback)
146
+ return self._websocket_manager.candlesticks(
147
+ callback=wrapper,
148
+ interval=timeframe.to_exchange_format(Exchange.OKX), # type: ignore
149
+ inst_id=inst_id,
150
+ )
122
151
 
123
152
  @overload
124
153
  def trades(
@@ -156,7 +185,9 @@ class UniWebsocketManager(IUniWebsocketManager):
156
185
  Возвращает:
157
186
  `Websocket`: Экземпляр вебсокета.
158
187
  """
159
- raise NotImplementedError()
188
+ inst_id = self._normalize_symbol(symbol, symbols)
189
+ wrapper = self._make_wrapper(self._adapter.trades_message, callback)
190
+ return self._websocket_manager.all_trades(callback=wrapper, inst_id=inst_id)
160
191
 
161
192
  @overload
162
193
  def aggtrades(
@@ -194,7 +225,9 @@ class UniWebsocketManager(IUniWebsocketManager):
194
225
  Возвращает:
195
226
  `Websocket`: Экземпляр вебсокета.
196
227
  """
197
- raise NotImplementedError()
228
+ inst_id = self._normalize_symbol(symbol, symbols)
229
+ wrapper = self._make_wrapper(self._adapter.trades_message, callback)
230
+ return self._websocket_manager.trades(callback=wrapper, inst_id=inst_id)
198
231
 
199
232
  @overload
200
233
  def futures_trades(
@@ -232,7 +265,9 @@ class UniWebsocketManager(IUniWebsocketManager):
232
265
  Возвращает:
233
266
  `Websocket`: Экземпляр вебсокета.
234
267
  """
235
- raise NotImplementedError()
268
+ inst_id = self._normalize_symbol(symbol, symbols)
269
+ wrapper = self._make_wrapper(self._adapter.trades_message, callback)
270
+ return self._websocket_manager.all_trades(callback=wrapper, inst_id=inst_id)
236
271
 
237
272
  @overload
238
273
  def futures_aggtrades(
@@ -270,4 +305,6 @@ class UniWebsocketManager(IUniWebsocketManager):
270
305
  Возвращает:
271
306
  `Websocket`: Экземпляр вебсокета.
272
307
  """
273
- raise NotImplementedError()
308
+ inst_id = self._normalize_symbol(symbol, symbols)
309
+ wrapper = self._make_wrapper(self._adapter.trades_message, callback)
310
+ return self._websocket_manager.trades(callback=wrapper, inst_id=inst_id)
@@ -79,10 +79,10 @@ class KlineDict(TypedDict):
79
79
  q: float
80
80
  """Объем свечи. В долларах."""
81
81
 
82
- T: int | None
82
+ T: int | None # `None` means untrackable
83
83
  """Время закрытия. В миллисекундах."""
84
84
 
85
- x: bool | None
85
+ x: bool | None # `None` means untrackable
86
86
  """Флаг закрыта ли свеча."""
87
87
 
88
88
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unicex
3
- Version: 0.15.4
3
+ Version: 0.16.0
4
4
  Summary: Unified Crypto Exchange API
5
5
  Author-email: LoveBloodAndDiamonds <ayazshakirzyanov27@gmail.com>
6
6
  License: BSD 3-Clause License
@@ -56,12 +56,12 @@ Dynamic: license-file
56
56
  | Exchange | Client | Auth | WS Manager | User WS | Uni Client | Uni WS Manager | ExchangeInfo |
57
57
  |-----------------|--------|------|------------|---------|------------|----------------|--------------|
58
58
  | **Binance** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
59
- | **Bitget** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
59
+ | **Bitget** | ✓ | ✓ | ✓ | | ✓ || ✓ |
60
60
  | **Bybit** | ✓ | ✓ | ✓ | | ✓ | ✓ | ✓ |
61
- | **Gateio** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
61
+ | **Gateio** | ✓ | ✓ | ✓ | | ✓ || ✓ |
62
62
  | **Hyperliquid** | ✓ | ✓ | ✓ | ✓ | ✓ | | |
63
- | **Mexc** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
64
- | **Okx** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
63
+ | **Mexc** | ✓ | ✓ | ✓ | | ✓ || ✓ |
64
+ | **Okx** | ✓ | ✓ | ✓ | | ✓ || ✓ |
65
65
  | **Kucoin** | | | | | ✓ | | |
66
66
  | **BingX** | | | | | ✓ | | |
67
67
  ---
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes