unicex 0.15.4__tar.gz → 0.16.2__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.2}/PKG-INFO +5 -5
  2. {unicex-0.15.4 → unicex-0.16.2}/README.md +4 -4
  3. {unicex-0.15.4 → unicex-0.16.2}/pyproject.toml +1 -1
  4. {unicex-0.15.4 → unicex-0.16.2}/unicex/__init__.py +0 -2
  5. {unicex-0.15.4 → unicex-0.16.2}/unicex/binance/adapter.py +2 -3
  6. {unicex-0.15.4 → unicex-0.16.2}/unicex/bingx/uni_websocket_manager.py +2 -2
  7. {unicex-0.15.4 → unicex-0.16.2}/unicex/bitget/adapter.py +1 -2
  8. {unicex-0.15.4 → unicex-0.16.2}/unicex/bitget/uni_websocket_manager.py +21 -4
  9. {unicex-0.15.4 → unicex-0.16.2}/unicex/gate/adapter.py +112 -0
  10. {unicex-0.15.4 → unicex-0.16.2}/unicex/gate/uni_websocket_manager.py +41 -7
  11. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/adapter.py +103 -0
  12. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/uni_websocket_manager.py +25 -7
  13. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/websocket_manager.py +24 -1
  14. {unicex-0.15.4 → unicex-0.16.2}/unicex/okx/adapter.py +60 -0
  15. {unicex-0.15.4 → unicex-0.16.2}/unicex/okx/exchange_info.py +2 -2
  16. {unicex-0.15.4 → unicex-0.16.2}/unicex/okx/uni_websocket_manager.py +44 -7
  17. {unicex-0.15.4 → unicex-0.16.2}/unicex/okx/websocket_manager.py +116 -163
  18. {unicex-0.15.4 → unicex-0.16.2}/unicex/types.py +2 -9
  19. {unicex-0.15.4 → unicex-0.16.2/unicex.egg-info}/PKG-INFO +5 -5
  20. {unicex-0.15.4 → unicex-0.16.2}/LICENSE +0 -0
  21. {unicex-0.15.4 → unicex-0.16.2}/setup.cfg +0 -0
  22. {unicex-0.15.4 → unicex-0.16.2}/unicex/_abc/__init__.py +0 -0
  23. {unicex-0.15.4 → unicex-0.16.2}/unicex/_abc/exchange_info.py +0 -0
  24. {unicex-0.15.4 → unicex-0.16.2}/unicex/_abc/uni_client.py +0 -0
  25. {unicex-0.15.4 → unicex-0.16.2}/unicex/_abc/uni_websocket_manager.py +0 -0
  26. {unicex-0.15.4 → unicex-0.16.2}/unicex/_base/__init__.py +0 -0
  27. {unicex-0.15.4 → unicex-0.16.2}/unicex/_base/client.py +0 -0
  28. {unicex-0.15.4 → unicex-0.16.2}/unicex/_base/websocket.py +0 -0
  29. {unicex-0.15.4 → unicex-0.16.2}/unicex/binance/__init__.py +0 -0
  30. {unicex-0.15.4 → unicex-0.16.2}/unicex/binance/client.py +0 -0
  31. {unicex-0.15.4 → unicex-0.16.2}/unicex/binance/exchange_info.py +0 -0
  32. {unicex-0.15.4 → unicex-0.16.2}/unicex/binance/uni_client.py +0 -0
  33. {unicex-0.15.4 → unicex-0.16.2}/unicex/binance/uni_websocket_manager.py +0 -0
  34. {unicex-0.15.4 → unicex-0.16.2}/unicex/binance/user_websocket.py +0 -0
  35. {unicex-0.15.4 → unicex-0.16.2}/unicex/binance/websocket_manager.py +0 -0
  36. {unicex-0.15.4 → unicex-0.16.2}/unicex/bingx/__init__.py +0 -0
  37. {unicex-0.15.4 → unicex-0.16.2}/unicex/bingx/adapter.py +0 -0
  38. {unicex-0.15.4 → unicex-0.16.2}/unicex/bingx/client.py +0 -0
  39. {unicex-0.15.4 → unicex-0.16.2}/unicex/bingx/exchange_info.py +0 -0
  40. {unicex-0.15.4 → unicex-0.16.2}/unicex/bingx/uni_client.py +0 -0
  41. {unicex-0.15.4 → unicex-0.16.2}/unicex/bingx/user_websocket.py +0 -0
  42. {unicex-0.15.4 → unicex-0.16.2}/unicex/bingx/websocket_manager.py +0 -0
  43. {unicex-0.15.4 → unicex-0.16.2}/unicex/bitget/__init__.py +0 -0
  44. {unicex-0.15.4 → unicex-0.16.2}/unicex/bitget/client.py +0 -0
  45. {unicex-0.15.4 → unicex-0.16.2}/unicex/bitget/exchange_info.py +0 -0
  46. {unicex-0.15.4 → unicex-0.16.2}/unicex/bitget/uni_client.py +0 -0
  47. {unicex-0.15.4 → unicex-0.16.2}/unicex/bitget/user_websocket.py +0 -0
  48. {unicex-0.15.4 → unicex-0.16.2}/unicex/bitget/websocket_manager.py +0 -0
  49. {unicex-0.15.4 → unicex-0.16.2}/unicex/bybit/__init__.py +0 -0
  50. {unicex-0.15.4 → unicex-0.16.2}/unicex/bybit/adapter.py +0 -0
  51. {unicex-0.15.4 → unicex-0.16.2}/unicex/bybit/client.py +0 -0
  52. {unicex-0.15.4 → unicex-0.16.2}/unicex/bybit/exchange_info.py +0 -0
  53. {unicex-0.15.4 → unicex-0.16.2}/unicex/bybit/uni_client.py +0 -0
  54. {unicex-0.15.4 → unicex-0.16.2}/unicex/bybit/uni_websocket_manager.py +0 -0
  55. {unicex-0.15.4 → unicex-0.16.2}/unicex/bybit/user_websocket.py +0 -0
  56. {unicex-0.15.4 → unicex-0.16.2}/unicex/bybit/websocket_manager.py +0 -0
  57. {unicex-0.15.4 → unicex-0.16.2}/unicex/enums.py +0 -0
  58. {unicex-0.15.4 → unicex-0.16.2}/unicex/exceptions.py +0 -0
  59. {unicex-0.15.4 → unicex-0.16.2}/unicex/extra.py +0 -0
  60. {unicex-0.15.4 → unicex-0.16.2}/unicex/gate/__init__.py +0 -0
  61. {unicex-0.15.4 → unicex-0.16.2}/unicex/gate/client.py +0 -0
  62. {unicex-0.15.4 → unicex-0.16.2}/unicex/gate/exchange_info.py +0 -0
  63. {unicex-0.15.4 → unicex-0.16.2}/unicex/gate/uni_client.py +0 -0
  64. {unicex-0.15.4 → unicex-0.16.2}/unicex/gate/user_websocket.py +0 -0
  65. {unicex-0.15.4 → unicex-0.16.2}/unicex/gate/websocket_manager.py +0 -0
  66. {unicex-0.15.4 → unicex-0.16.2}/unicex/hyperliquid/__init__.py +0 -0
  67. {unicex-0.15.4 → unicex-0.16.2}/unicex/hyperliquid/adapter.py +0 -0
  68. {unicex-0.15.4 → unicex-0.16.2}/unicex/hyperliquid/client.py +0 -0
  69. {unicex-0.15.4 → unicex-0.16.2}/unicex/hyperliquid/exchange_info.py +0 -0
  70. {unicex-0.15.4 → unicex-0.16.2}/unicex/hyperliquid/uni_client.py +0 -0
  71. {unicex-0.15.4 → unicex-0.16.2}/unicex/hyperliquid/uni_websocket_manager.py +0 -0
  72. {unicex-0.15.4 → unicex-0.16.2}/unicex/hyperliquid/user_websocket.py +0 -0
  73. {unicex-0.15.4 → unicex-0.16.2}/unicex/hyperliquid/websocket_manager.py +0 -0
  74. {unicex-0.15.4 → unicex-0.16.2}/unicex/kucoin/__init__.py +0 -0
  75. {unicex-0.15.4 → unicex-0.16.2}/unicex/kucoin/adapter.py +0 -0
  76. {unicex-0.15.4 → unicex-0.16.2}/unicex/kucoin/client.py +0 -0
  77. {unicex-0.15.4 → unicex-0.16.2}/unicex/kucoin/exchange_info.py +0 -0
  78. {unicex-0.15.4 → unicex-0.16.2}/unicex/kucoin/uni_client.py +0 -0
  79. {unicex-0.15.4 → unicex-0.16.2}/unicex/kucoin/uni_websocket_manager.py +0 -0
  80. {unicex-0.15.4 → unicex-0.16.2}/unicex/kucoin/user_websocket.py +0 -0
  81. {unicex-0.15.4 → unicex-0.16.2}/unicex/kucoin/websocket_manager.py +0 -0
  82. {unicex-0.15.4 → unicex-0.16.2}/unicex/mapper.py +0 -0
  83. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/__init__.py +0 -0
  84. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PrivateAccountV3Api_pb2.py +0 -0
  85. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PrivateDealsV3Api_pb2.py +0 -0
  86. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PrivateOrdersV3Api_pb2.py +0 -0
  87. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PublicAggreBookTickerV3Api_pb2.py +0 -0
  88. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PublicAggreDealsV3Api_pb2.py +0 -0
  89. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PublicAggreDepthsV3Api_pb2.py +0 -0
  90. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PublicBookTickerBatchV3Api_pb2.py +0 -0
  91. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PublicBookTickerV3Api_pb2.py +0 -0
  92. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PublicDealsV3Api_pb2.py +0 -0
  93. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PublicFuture_pb2.py +0 -0
  94. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PublicIncreaseDepthsBatchV3Api_pb2.py +0 -0
  95. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PublicIncreaseDepthsV3Api_pb2.py +0 -0
  96. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PublicLimitDepthsV3Api_pb2.py +0 -0
  97. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PublicMiniTickerV3Api_pb2.py +0 -0
  98. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PublicMiniTickersV3Api_pb2.py +0 -0
  99. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PublicSpotKlineV3Api_pb2.py +0 -0
  100. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/PushDataV3ApiWrapper_pb2.py +0 -0
  101. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/_spot_ws_proto/__init__.py +0 -0
  102. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/client.py +0 -0
  103. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/exchange_info.py +0 -0
  104. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/uni_client.py +0 -0
  105. {unicex-0.15.4 → unicex-0.16.2}/unicex/mexc/user_websocket.py +0 -0
  106. {unicex-0.15.4 → unicex-0.16.2}/unicex/okx/__init__.py +0 -0
  107. {unicex-0.15.4 → unicex-0.16.2}/unicex/okx/client.py +0 -0
  108. {unicex-0.15.4 → unicex-0.16.2}/unicex/okx/uni_client.py +0 -0
  109. {unicex-0.15.4 → unicex-0.16.2}/unicex/okx/user_websocket.py +0 -0
  110. {unicex-0.15.4 → unicex-0.16.2}/unicex/utils.py +0 -0
  111. {unicex-0.15.4 → unicex-0.16.2}/unicex.egg-info/SOURCES.txt +0 -0
  112. {unicex-0.15.4 → unicex-0.16.2}/unicex.egg-info/dependency_links.txt +0 -0
  113. {unicex-0.15.4 → unicex-0.16.2}/unicex.egg-info/requires.txt +0 -0
  114. {unicex-0.15.4 → unicex-0.16.2}/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.2
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.2"
8
8
 
9
9
  description = "Unified Crypto Exchange API "
10
10
  readme = "README.md"
@@ -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",
@@ -112,7 +111,6 @@ from .types import (
112
111
  TickerDailyItem,
113
112
  KlineDict,
114
113
  TradeDict,
115
- AggTradeDict,
116
114
  RequestMethod,
117
115
  LoggerLike,
118
116
  OpenInterestDict,
@@ -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,
@@ -150,7 +149,7 @@ class Adapter:
150
149
  ]
151
150
 
152
151
  @staticmethod
153
- def aggtrades_message(raw_msg: dict) -> list[AggTradeDict]:
152
+ def aggtrades_message(raw_msg: dict) -> list[TradeDict]:
154
153
  """Преобразует сырое сообщение с вебсокета, в котором содержится информация о
155
154
  аггрегированных сделке/сделках в унифицированный вид.
156
155
 
@@ -162,7 +161,7 @@ class Adapter:
162
161
  """
163
162
  msg = raw_msg.get("data", raw_msg)
164
163
  return [
165
- AggTradeDict(
164
+ TradeDict(
166
165
  t=int(msg["T"]),
167
166
  s=str(msg["s"]),
168
167
  S="SELL" if bool(msg["m"]) else "BUY",
@@ -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
@@ -1,7 +1,6 @@
1
1
  from typing import Any
2
2
 
3
3
  from unicex.types import (
4
- AggTradeDict,
5
4
  KlineDict,
6
5
  OpenInterestDict,
7
6
  OpenInterestItem,
@@ -155,7 +154,7 @@ class Adapter:
155
154
  symbol = raw_msg["arg"]["instId"]
156
155
 
157
156
  return [
158
- AggTradeDict(
157
+ TradeDict(
159
158
  t=int(trade["ts"]),
160
159
  s=symbol,
161
160
  S=trade["side"].upper(),
@@ -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,9 +11,12 @@ 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
 
18
+ from .exchange_info import ExchangeInfo
19
+
16
20
 
17
21
  @decorate_all_methods(catch_adapter_errors)
18
22
  class Adapter:
@@ -177,3 +181,111 @@ class Adapter:
177
181
  )
178
182
  for item in raw_data
179
183
  }
184
+
185
+ @staticmethod
186
+ def klines_message(raw_msg: Any) -> list[KlineDict]:
187
+ """Преобразует вебсокет-сообщение со свечами в унифицированный формат.
188
+
189
+ Параметры:
190
+ raw_msg (Any): Сырое сообщение с вебсокета.
191
+
192
+ Возвращает:
193
+ list[KlineDict]: Список свечей в унифицированном формате.
194
+ """
195
+ data = raw_msg["result"]
196
+ return [
197
+ KlineDict(
198
+ s=data["n"].split("_", 1)[1], # XRP_USDT
199
+ t=int(data["t"]) * 1000,
200
+ o=float(data["o"]),
201
+ h=float(data["h"]),
202
+ l=float(data["l"]),
203
+ c=float(data["c"]),
204
+ v=float(data["a"]),
205
+ q=float(data["v"]),
206
+ T=None,
207
+ x=not data["w"], # w=False → свеча закрыта
208
+ )
209
+ ]
210
+
211
+ @staticmethod
212
+ def futures_klines_message(raw_msg: Any) -> list[KlineDict]:
213
+ """Преобразует вебсокет-сообщение со свечами в унифицированный формат.
214
+
215
+ Параметры:
216
+ raw_msg (Any): Сырое сообщение с вебсокета.
217
+
218
+ Возвращает:
219
+ list[KlineDict]: Список свечей в унифицированном формате.
220
+ """
221
+ return [
222
+ KlineDict(
223
+ s=item["n"].split("_", 1)[1], # XRP_USDT
224
+ t=int(item["t"]) * 1000,
225
+ o=float(item["o"]),
226
+ h=float(item["h"]),
227
+ l=float(item["l"]),
228
+ c=float(item["c"]),
229
+ v=float(item["a"]),
230
+ q=float(item["v"]),
231
+ T=None,
232
+ x=not item["w"], # w=False → свеча закрыта
233
+ )
234
+ for item in sorted(
235
+ raw_msg["result"],
236
+ key=lambda x: int(x["t"]),
237
+ )
238
+ ]
239
+
240
+ @staticmethod
241
+ def trades_message(raw_msg: Any) -> list[TradeDict]:
242
+ """Преобразует вебсокет-сообщение со сделками в унифицированный формат.
243
+
244
+ Параметры:
245
+ raw_msg (Any): Сырое сообщение с вебсокета.
246
+
247
+ Возвращает:
248
+ list[TradeDict]: Список сделок в унифицированном формате.
249
+ """
250
+ trade = raw_msg["result"]
251
+ return [
252
+ TradeDict(
253
+ t=trade["create_time_ms"],
254
+ s=trade["currency_pair"],
255
+ S=trade["side"].upper(),
256
+ p=float(trade["price"]),
257
+ v=float(trade["amount"]),
258
+ )
259
+ ]
260
+
261
+ @staticmethod
262
+ def futures_trades_message(raw_msg: Any) -> list[TradeDict]:
263
+ """Преобразует вебсокет-сообщение со сделками в унифицированный формат.
264
+
265
+ Параметры:
266
+ raw_msg (Any): Сырое сообщение с вебсокета.
267
+
268
+ Возвращает:
269
+ list[TradeDict]: Список сделок в унифицированном формате.
270
+ """
271
+ return [
272
+ TradeDict(
273
+ t=item["create_time_ms"],
274
+ s=item["contract"],
275
+ S="BUY" if float(item["size"]) > 0 else "SELL",
276
+ p=float(item["price"]),
277
+ v=abs(float(item["size"])) * Adapter._get_contract_size(item["contract"]),
278
+ )
279
+ for item in sorted(
280
+ raw_msg["result"],
281
+ key=lambda x: x["create_time_ms"],
282
+ )
283
+ ]
284
+
285
+ @staticmethod
286
+ def _get_contract_size(symbol: str) -> float:
287
+ """Возвращает размер контракта для указанного символа тикера."""
288
+ try:
289
+ return ExchangeInfo.get_futures_ticker_info(symbol)["contract_size"] or 1
290
+ except: # noqa
291
+ return 1
@@ -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"] * Adapter._get_contract_size(raw_msg["symbol"]),
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