unicex 0.4.0__py3-none-any.whl → 0.7.0__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 (98) hide show
  1. unicex/__init__.py +56 -124
  2. unicex/_abc/__init__.py +2 -0
  3. unicex/_abc/exchange_info.py +176 -0
  4. unicex/_abc/uni_client.py +2 -2
  5. unicex/_base/client.py +2 -2
  6. unicex/binance/__init__.py +12 -0
  7. unicex/binance/adapter.py +1 -1
  8. unicex/binance/exchange_info.py +12 -0
  9. unicex/bitget/__init__.py +14 -4
  10. unicex/bitget/adapter.py +1 -1
  11. unicex/bitget/exchange_info.py +12 -0
  12. unicex/bitget/uni_websocket_manager.py +1 -1
  13. unicex/bybit/__init__.py +12 -0
  14. unicex/bybit/adapter.py +1 -1
  15. unicex/bybit/exchange_info.py +12 -0
  16. unicex/enums.py +16 -5
  17. unicex/extra.py +49 -0
  18. unicex/gateio/__init__.py +12 -0
  19. unicex/gateio/adapter.py +2 -2
  20. unicex/gateio/exchange_info.py +12 -0
  21. unicex/hyperliquid/__init__.py +12 -0
  22. unicex/hyperliquid/adapter.py +140 -30
  23. unicex/hyperliquid/client.py +2208 -125
  24. unicex/hyperliquid/exchange_info.py +100 -0
  25. unicex/hyperliquid/uni_client.py +176 -22
  26. unicex/mapper.py +35 -33
  27. unicex/mexc/__init__.py +12 -0
  28. unicex/mexc/adapter.py +28 -11
  29. unicex/mexc/exchange_info.py +32 -0
  30. unicex/mexc/uni_client.py +6 -0
  31. unicex/okx/__init__.py +12 -0
  32. unicex/okx/adapter.py +23 -7
  33. unicex/okx/exchange_info.py +50 -0
  34. unicex/okx/uni_client.py +2 -2
  35. unicex/types.py +31 -0
  36. unicex-0.7.0.dist-info/METADATA +192 -0
  37. unicex-0.7.0.dist-info/RECORD +75 -0
  38. unicex/bitrue/__init__.py +0 -15
  39. unicex/bitrue/adapter.py +0 -8
  40. unicex/bitrue/client.py +0 -128
  41. unicex/bitrue/uni_client.py +0 -151
  42. unicex/bitrue/uni_websocket_manager.py +0 -269
  43. unicex/bitrue/user_websocket.py +0 -7
  44. unicex/bitrue/websocket_manager.py +0 -11
  45. unicex/bitunix/__init__.py +0 -15
  46. unicex/bitunix/adapter.py +0 -8
  47. unicex/bitunix/client.py +0 -8
  48. unicex/bitunix/uni_client.py +0 -151
  49. unicex/bitunix/uni_websocket_manager.py +0 -269
  50. unicex/bitunix/user_websocket.py +0 -7
  51. unicex/bitunix/websocket_manager.py +0 -11
  52. unicex/btse/__init__.py +0 -15
  53. unicex/btse/adapter.py +0 -8
  54. unicex/btse/client.py +0 -123
  55. unicex/btse/uni_client.py +0 -151
  56. unicex/btse/uni_websocket_manager.py +0 -269
  57. unicex/btse/user_websocket.py +0 -7
  58. unicex/btse/websocket_manager.py +0 -11
  59. unicex/kcex/__init__.py +0 -15
  60. unicex/kcex/adapter.py +0 -8
  61. unicex/kcex/client.py +0 -8
  62. unicex/kcex/uni_client.py +0 -151
  63. unicex/kcex/uni_websocket_manager.py +0 -269
  64. unicex/kcex/user_websocket.py +0 -7
  65. unicex/kcex/websocket_manager.py +0 -11
  66. unicex/kraken/__init__.py +0 -15
  67. unicex/kraken/adapter.py +0 -8
  68. unicex/kraken/client.py +0 -165
  69. unicex/kraken/uni_client.py +0 -151
  70. unicex/kraken/uni_websocket_manager.py +0 -269
  71. unicex/kraken/user_websocket.py +0 -7
  72. unicex/kraken/websocket_manager.py +0 -11
  73. unicex/kucoin/__init__.py +0 -15
  74. unicex/kucoin/adapter.py +0 -8
  75. unicex/kucoin/client.py +0 -120
  76. unicex/kucoin/uni_client.py +0 -151
  77. unicex/kucoin/uni_websocket_manager.py +0 -269
  78. unicex/kucoin/user_websocket.py +0 -7
  79. unicex/kucoin/websocket_manager.py +0 -11
  80. unicex/weex/__init__.py +0 -15
  81. unicex/weex/adapter.py +0 -8
  82. unicex/weex/client.py +0 -8
  83. unicex/weex/uni_client.py +0 -151
  84. unicex/weex/uni_websocket_manager.py +0 -269
  85. unicex/weex/user_websocket.py +0 -7
  86. unicex/weex/websocket_manager.py +0 -11
  87. unicex/xt/__init__.py +0 -15
  88. unicex/xt/adapter.py +0 -8
  89. unicex/xt/client.py +0 -8
  90. unicex/xt/uni_client.py +0 -151
  91. unicex/xt/uni_websocket_manager.py +0 -269
  92. unicex/xt/user_websocket.py +0 -7
  93. unicex/xt/websocket_manager.py +0 -11
  94. unicex-0.4.0.dist-info/METADATA +0 -170
  95. unicex-0.4.0.dist-info/RECORD +0 -123
  96. {unicex-0.4.0.dist-info → unicex-0.7.0.dist-info}/WHEEL +0 -0
  97. {unicex-0.4.0.dist-info → unicex-0.7.0.dist-info}/licenses/LICENSE +0 -0
  98. {unicex-0.4.0.dist-info → unicex-0.7.0.dist-info}/top_level.txt +0 -0
unicex/mexc/uni_client.py CHANGED
@@ -138,9 +138,15 @@ class UniClient(IUniClient[Client]):
138
138
  Возвращает:
139
139
  list[KlineDict]: Список свечей для тикера.
140
140
  """
141
+ if not limit and not all([start_time, end_time]):
142
+ raise ValueError("limit and (start_time and end_time) must be provided")
143
+
141
144
  if limit: # Перезаписываем start_time и end_time если указан limit, т.к. по умолчанию Mexc Futures не принимают этот параметр
145
+ if not isinstance(interval, Timeframe):
146
+ raise ValueError("interval must be a Timeframe if limit param provided")
142
147
  end_time = int(time.time())
143
148
  start_time = end_time - (limit * interval.to_seconds) # type: ignore[reportOptionalOperand]
149
+
144
150
  interval = (
145
151
  interval.to_exchange_format(Exchange.MEXC, MarketType.FUTURES)
146
152
  if isinstance(interval, Timeframe)
unicex/okx/__init__.py CHANGED
@@ -6,10 +6,22 @@ __all__ = [
6
6
  "UserWebsocket",
7
7
  "WebsocketManager",
8
8
  "UniWebsocketManager",
9
+ "ExchangeInfo",
9
10
  ]
10
11
 
11
12
  from .client import Client
13
+ from .exchange_info import ExchangeInfo
12
14
  from .uni_client import UniClient
13
15
  from .uni_websocket_manager import UniWebsocketManager
14
16
  from .user_websocket import UserWebsocket
15
17
  from .websocket_manager import WebsocketManager
18
+
19
+
20
+ async def load_exchange_info() -> None:
21
+ """Загружает информацию о бирже Okx."""
22
+ await ExchangeInfo.load_exchange_info()
23
+
24
+
25
+ async def start_exchange_info(parse_interval_seconds: int = 60 * 60) -> None:
26
+ """Запускает процесс обновления информации о бирже Okx."""
27
+ await ExchangeInfo.start(parse_interval_seconds)
unicex/okx/adapter.py CHANGED
@@ -10,6 +10,8 @@ from unicex.types import (
10
10
  )
11
11
  from unicex.utils import catch_adapter_errors, decorate_all_methods
12
12
 
13
+ from .exchange_info import ExchangeInfo
14
+
13
15
 
14
16
  @decorate_all_methods(catch_adapter_errors)
15
17
  class Adapter:
@@ -29,7 +31,7 @@ class Adapter:
29
31
  return [
30
32
  item["instId"]
31
33
  for item in raw_data["data"]
32
- if only_usdt or item["instId"].endswith("-USDT")
34
+ if item["instId"].endswith("-USDT") or not only_usdt
33
35
  ]
34
36
 
35
37
  @staticmethod
@@ -46,16 +48,19 @@ class Adapter:
46
48
  return [
47
49
  item["instId"]
48
50
  for item in raw_data["data"]
49
- if only_usdt or item["instId"].endswith("-USDT-SWAP")
51
+ if item["instId"].endswith("-USDT-SWAP") or not only_usdt
50
52
  ]
51
53
 
52
54
  @staticmethod
53
55
  def ticker_24hr(raw_data: dict) -> TickerDailyDict:
54
- """Преобразует статистику 24ч в унифицированный формат."""
56
+ """Преобразует статистику 24ч в унифицированный формат.
57
+
58
+ # NOTE: Обратите внимание, изменение цены в случае с OKX возвращается относительно открытия 1 day свечи.
59
+ """
55
60
  return {
56
61
  item["instId"]: TickerDailyItem(
57
62
  p=round(
58
- (float(item["last"]) - float(item["open24h"]) / float(item["open24h"])) * 100, 2
63
+ (float(item["last"]) - float(item["open24h"])) / float(item["open24h"]) * 100, 2
59
64
  ),
60
65
  v=float(item["vol24h"]),
61
66
  q=float(item["volCcy24h"]),
@@ -65,14 +70,17 @@ class Adapter:
65
70
 
66
71
  @staticmethod
67
72
  def futures_ticker_24hr(raw_data: dict) -> TickerDailyDict:
68
- """Преобразует статистику 24ч в унифицированный формат."""
73
+ """Преобразует статистику 24ч в унифицированный формат.
74
+
75
+ # NOTE: Обратите внимание, изменение цены в случае с OKX возвращается относительно открытия 1 day свечи.
76
+ """
69
77
  return {
70
78
  item["instId"]: TickerDailyItem(
71
79
  p=round(
72
- (float(item["last"]) - float(item["open24h"]) / float(item["open24h"])) * 100, 2
80
+ (float(item["last"]) - float(item["open24h"])) / float(item["open24h"]) * 100, 2
73
81
  ),
74
82
  v=float(item["volCcy24h"]),
75
- q=float(item["vol24h"]),
83
+ q=float(item["volCcy24h"]) * float(item["last"]),
76
84
  )
77
85
  for item in raw_data["data"]
78
86
  }
@@ -120,3 +128,11 @@ class Adapter:
120
128
  )
121
129
  for item in raw_data["data"]
122
130
  }
131
+
132
+ @staticmethod
133
+ def _get_contract_size(symbol: str) -> float:
134
+ """Возвращает размер контракта для указанного символа тикера."""
135
+ try:
136
+ return ExchangeInfo.get_futures_ticker_info(symbol)["contract_size"] or 1
137
+ except: # noqa
138
+ return 1
@@ -0,0 +1,50 @@
1
+ __all__ = ["ExchangeInfo"]
2
+
3
+ import aiohttp
4
+
5
+ from unicex._abc import IExchangeInfo
6
+ from unicex.types import TickerInfoItem
7
+
8
+
9
+ class ExchangeInfo(IExchangeInfo):
10
+ """Предзагружает информацию о тикерах для биржи Okx."""
11
+
12
+ @classmethod
13
+ async def _load_exchange_info(cls) -> None:
14
+ """Загружает информацию о бирже."""
15
+ async with aiohttp.ClientSession() as session:
16
+ tickers_info = {}
17
+ url = "https://www.okx.com/api/v5/public/instruments?instType=SPOT"
18
+ async with session.get(url) as response:
19
+ data = await response.json()
20
+ for el in data["data"]:
21
+ tickers_info[el["instId"]] = TickerInfoItem(
22
+ tick_precision=cls._step_size_to_precision(el["tickSz"]),
23
+ size_precision=cls._step_size_to_precision(el["lotSz"]),
24
+ contract_size=1,
25
+ min_market_size=float(el["minSz"]),
26
+ max_market_size=float(el["maxMktSz"]),
27
+ min_limit_size=float(el["minSz"]),
28
+ max_limit_size=float(el["maxLmtSz"]),
29
+ )
30
+
31
+ cls._tickers_info = tickers_info
32
+ cls._logger.debug("Okx spot exchange info loaded")
33
+
34
+ futures_tickers_info = {}
35
+ url = "https://www.okx.com/api/v5/public/instruments?instType=SWAP"
36
+ async with session.get(url) as response:
37
+ data = await response.json()
38
+ for el in data["data"]:
39
+ futures_tickers_info[el["instId"]] = TickerInfoItem(
40
+ tick_precision=cls._step_size_to_precision(el["tickSz"]),
41
+ size_precision=cls._step_size_to_precision(el["lotSz"]),
42
+ contract_size=float(el["ctVal"]),
43
+ min_market_size=el["minSz"],
44
+ max_market_size=el["maxMktSz"],
45
+ min_limit_size=el["minSz"],
46
+ max_limit_size=el["maxLmtSz"],
47
+ )
48
+
49
+ cls._futures_tickers_info = futures_tickers_info
50
+ cls._logger.debug("Okx futures exchange info loaded")
unicex/okx/uni_client.py CHANGED
@@ -45,7 +45,7 @@ class UniClient(IUniClient[Client]):
45
45
  list[str]: Список тикеров.
46
46
  """
47
47
  raw_data = await self._client.tickers(inst_type="SWAP")
48
- return Adapter.tickers(raw_data=raw_data, only_usdt=only_usdt)
48
+ return Adapter.futures_tickers(raw_data=raw_data, only_usdt=only_usdt)
49
49
 
50
50
  async def last_price(self) -> dict[str, float]:
51
51
  """Возвращает последнюю цену для каждого тикера.
@@ -81,7 +81,7 @@ class UniClient(IUniClient[Client]):
81
81
  TickerDailyDict: Словарь с статистикой за последние 24 часа для каждого тикера.
82
82
  """
83
83
  raw_data = await self._client.tickers(inst_type="SWAP")
84
- return Adapter.ticker_24hr(raw_data=raw_data)
84
+ return Adapter.futures_ticker_24hr(raw_data=raw_data)
85
85
 
86
86
  async def klines(
87
87
  self,
unicex/types.py CHANGED
@@ -11,6 +11,8 @@ __all__ = [
11
11
  "AccountType",
12
12
  "OpenInterestDict",
13
13
  "OpenInterestItem",
14
+ "TickerInfoItem",
15
+ "TickersInfoDict",
14
16
  ]
15
17
 
16
18
  from logging import Logger as LoggingLogger
@@ -117,3 +119,32 @@ type OpenInterestDict = dict[str, OpenInterestItem]
117
119
 
118
120
  type AccountType = Literal["SPOT", "FUTURES"]
119
121
  """Тип аккаунта."""
122
+
123
+
124
+ class TickerInfoItem(TypedDict):
125
+ """Информация о размерах тиков, ступеней цены и множителя контракта (если есть) для тикера."""
126
+
127
+ tick_precision: int
128
+ """Количество знаков после запятой для цены."""
129
+
130
+ size_precision: int
131
+ """Количество знаков после запятой для объема."""
132
+
133
+ contract_size: float | None
134
+ """Множитель контракта (если есть)."""
135
+
136
+ min_market_size: float | None
137
+ """Минимальный размер рыночного ордера в монетах (если есть)."""
138
+
139
+ max_market_size: float | None
140
+ """Максимальный размер рыночного ордера в монетах (если есть)."""
141
+
142
+ min_limit_size: float | None
143
+ """Минимальный размер лимитного ордера в монетах (если есть)."""
144
+
145
+ max_limit_size: float | None
146
+ """Максимальный размер лимитного ордера в монетах (если есть)."""
147
+
148
+
149
+ type TickersInfoDict = dict[str, TickerInfoItem]
150
+ """Информация о размерах тиков, ступеней цены и множителя контракта (если есть) для всех тикеров."""
@@ -0,0 +1,192 @@
1
+ Metadata-Version: 2.4
2
+ Name: unicex
3
+ Version: 0.7.0
4
+ Summary: Unified Crypto Exchange API
5
+ Author-email: LoveBloodAndDiamonds <ayazshakirzyanov27@gmail.com>
6
+ License: BSD 3-Clause License
7
+
8
+ Copyright (c) 2025, LoveBloodAndDiamonds
9
+
10
+ Redistribution and use in source and binary forms, with or without
11
+ modification, are permitted provided that the following conditions are met:
12
+
13
+ 1. Redistributions of source code must retain the above copyright notice, this
14
+ list of conditions and the following disclaimer.
15
+
16
+ 2. Redistributions in binary form must reproduce the above copyright notice,
17
+ this list of conditions and the following disclaimer in the documentation
18
+ and/or other materials provided with the distribution.
19
+
20
+ 3. Neither the name of the copyright holder nor the names of its
21
+ contributors may be used to endorse or promote products derived from
22
+ this software without specific prior written permission.
23
+
24
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
28
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+
35
+ Project-URL: Github, https://github.com/LoveBloodAndDiamonds/uni-cex-api
36
+ Project-URL: Author, https://t.me/LoveBloodAndDiamonds
37
+ Project-URL: Readthedocs, https://unicex.readthedocs.io/ru/latest/
38
+ Requires-Python: >=3.12
39
+ Description-Content-Type: text/markdown
40
+ License-File: LICENSE
41
+ Requires-Dist: aiohttp>=3.12.15
42
+ Requires-Dist: eth-account>=0.13.7
43
+ Requires-Dist: loguru>=0.7.3
44
+ Requires-Dist: msgpack>=1.1.1
45
+ Requires-Dist: orjson>=3.11.3
46
+ Requires-Dist: websockets>=15.0.1
47
+ Dynamic: license-file
48
+
49
+ # Unified Crypto Exchange API
50
+
51
+ `unicex` — асинхронная библиотека для работы с криптовалютными биржами, реализующая унифицированный интерфейс поверх «сырых» REST и WebSocket API разных бирж.
52
+
53
+ ## ✅ Статус реализации
54
+
55
+ | Exchange | Client | Auth | WS Manager | User WS | Uni Client | Uni WS Manager | ExchangeInfo |
56
+ |-----------------|--------|------|------------|---------|------------|----------------|--------------|
57
+ | **Binance** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | |
58
+ | **Bitget** | ✓ | ✓ | ✓ | | ✓ | | |
59
+ | **Bybit** | ✓ | ✓ | | | ✓ | | |
60
+ | **Gateio** | ✓ | ✓ | | | ✓ | | |
61
+ | **Hyperliquid** | ✓ | ✓ | | | ✓ | | |
62
+ | **Mexc** | ✓ | ✓ | | | ✓ | | |
63
+ | **Okx** | | ✓ | | | ✓ | | ✓ |
64
+ ---
65
+
66
+
67
+ ### 📖 Описание колонок
68
+
69
+ - **Client** – Обертки над HTTP методами следующих разделов: market, order, position, account.
70
+ - **Auth** – Поддержка авторизации и приватных эндпоинтов.
71
+ - **WS Manager** – Обертки над вебсокетами биржи.
72
+ - **User WS** – Поддержка пользовательских вебсокетов.
73
+ - **UniClient** –Унифированный клиент.
74
+ - **UniWebsocketManager** – Унифированный менеджер вебсокетов.
75
+ - **ExchangeInfo** - Информация о бирже для округления цен и объемов
76
+ ---
77
+
78
+ ## 🚀 Быстрый старт
79
+
80
+ - Установка: `pip install unicex` или из исходников: `pip install -e .`
81
+ - Библиотека полностью асинхронная. Примеры импорта:
82
+ - Сырые клиенты: `from unicex.binance import Client`
83
+ - Унифицированные клиенты: `from unicex.binance import UniClient`
84
+ - Вебсокет менеджеры: `from unicex.binance import WebsocketManager, UniWebsocketManager`
85
+
86
+ ### Пример: Получение рыночных данных через API
87
+
88
+ ```python
89
+ import asyncio
90
+
91
+ from unicex import Exchange, Timeframe, get_uni_client
92
+
93
+ # Выбираем биржу, с которой хотим работать.
94
+ # Поддерживаются: Binance, Bybit, Bitget, Mexc, Gateio, Hyperliquid и другие.
95
+ exchange = Exchange.BYBIT
96
+
97
+
98
+ async def main() -> None:
99
+ """Пример простого использования унифицированного клиента unicex."""
100
+ # 1️⃣ Создаём клиент для выбранной биржи
101
+ client = await get_uni_client(exchange).create()
102
+
103
+ # 2️⃣ Получаем открытый интерес по всем контрактам
104
+ open_interest = await client.open_interest()
105
+ print(open_interest)
106
+
107
+ # Пример вывода:
108
+ # {
109
+ # "BTCUSDT": {"t": 1759669833728, "v": 61099320.0},
110
+ # "ETHUSDT": {"t": 1759669833728, "v": 16302340.0},
111
+ # "SOLUSDT": {"t": 1759669833728, "v": 3427780.0},
112
+ # ...
113
+ # }
114
+
115
+ # 3️⃣ Можно точно так же получать другие данные в едином формате:
116
+ await client.tickers() # список всех тикеров
117
+ await client.futures_tickers() # тикеры фьючерсов
118
+ await client.ticker_24hr() # статистика за 24 часа (spot)
119
+ await client.futures_ticker_24hr() # статистика за 24 часа (futures)
120
+ await client.klines("BTCUSDT", Timeframe.MIN_5) # свечи спота
121
+ await client.futures_klines("BTCUSDT", Timeframe.HOUR_1) # свечи фьючерсов
122
+ await client.funding_rate() # ставка финансирования
123
+
124
+
125
+ if __name__ == "__main__":
126
+ asyncio.run(main())
127
+
128
+ ```
129
+
130
+ ### Пример: Получение данных в реальном времени через Websocket API
131
+
132
+ ```python
133
+ import asyncio
134
+ from unicex import Exchange, TradeDict, get_uni_websocket_manager
135
+ from unicex.enums import Timeframe
136
+
137
+ # Выбираем биржу, с которой хотим работать.
138
+ # Поддерживаются: Binance, Bybit, Bitget, Mexc, Gateio, Hyperliquid и другие.
139
+ exchange = Exchange.BITGET
140
+
141
+
142
+ async def main() -> None:
143
+ """Пример простого использования унифицированного менеджера Websocket от UniCEX."""
144
+
145
+ # 1️⃣ Создаём WebSocket-менеджер для выбранной биржи
146
+ ws_manager = get_uni_websocket_manager(exchange)()
147
+
148
+ # 2️⃣ Подключаемся к потоку сделок (aggTrades)
149
+ aggtrades_ws = ws_manager.aggtrades(
150
+ callback=callback,
151
+ symbols=["BTCUSDT", "ETHUSDT"],
152
+ )
153
+
154
+ # Запускаем получение данных
155
+ await aggtrades_ws.start()
156
+
157
+ # 3️⃣ Примеры других типов потоков:
158
+ futures_aggtrades_ws = ws_manager.futures_aggtrades(
159
+ callback=callback,
160
+ symbols=["BTCUSDT", "ETHUSDT"],
161
+ )
162
+
163
+ klines_ws = ws_manager.klines(
164
+ callback=callback,
165
+ symbols=["BTCUSDT", "ETHUSDT"],
166
+ timeframe=Timeframe.MIN_5,
167
+ )
168
+
169
+ futures_klines_ws = ws_manager.futures_klines(
170
+ callback=callback,
171
+ symbols=["BTCUSDT", "ETHUSDT"],
172
+ timeframe=Timeframe.MIN_1,
173
+ )
174
+
175
+ # 💡 Также у каждой биржи есть свой WebsocketManager:
176
+ # unicex.<exchange>.websocket_manager.WebsocketManager
177
+ # В нём реализованы остальные методы для работы с WS API.
178
+
179
+
180
+ async def callback(trade: TradeDict) -> None:
181
+ """Обработка входящих данных из Websocket."""
182
+ print(trade)
183
+ # Пример вывода:
184
+ # {'t': 1759670527594, 's': 'BTCUSDT', 'S': 'BUY', 'p': 123238.87, 'v': 0.05}
185
+ # {'t': 1759670527594, 's': 'BTCUSDT', 'S': 'BUY', 'p': 123238.87, 'v': 0.04}
186
+ # {'t': 1759670346828, 's': 'ETHUSDT', 'S': 'SELL', 'p': 4535.0, 'v': 0.0044}
187
+ # {'t': 1759670347087, 's': 'ETHUSDT', 'S': 'BUY', 'p': 4534.91, 'v': 0.2712}
188
+
189
+
190
+ if __name__ == "__main__":
191
+ asyncio.run(main())
192
+ ```
@@ -0,0 +1,75 @@
1
+ unicex/__init__.py,sha256=J9UDSWyAS8ozDxSWFXgN4002fqHVUb1Ol-YtafNtJgs,5533
2
+ unicex/enums.py,sha256=8E_Nb57kriOif57XSLnW8joFufbthZTJ7tcExKWf1Wg,9633
3
+ unicex/exceptions.py,sha256=r-xZzX78VuxVnI5pe99AM8FIiGcdIUDcF5CaTkQ4NE0,2213
4
+ unicex/extra.py,sha256=fy_SS5vXtMIu9i_NdiD-XFHOnT0QC6PApHUGj6SrmPU,10376
5
+ unicex/mapper.py,sha256=zOuInRQGJnSnwRI5yJ_axx-0svGn-nOqgLr7XXSlq14,4915
6
+ unicex/types.py,sha256=NMitdbyD-_pRvR_gm2aapHU7wwL8CZCaqWFIuCAYMgA,4291
7
+ unicex/utils.py,sha256=dwU1VYuP2xcMpzaETtNQerL1V8Y_JH8H8EsLJ__-M4s,8050
8
+ unicex/_abc/__init__.py,sha256=Sg0VW0vHByBX4mh8SnKFbP31yVrSJ8cqHZHM8S_KZiE,214
9
+ unicex/_abc/exchange_info.py,sha256=j0nbTeF9-_LwHGDZLBHYF2Dd1DYBCQ-DlUO5N5tZgEY,7541
10
+ unicex/_abc/uni_client.py,sha256=3jikCsByZBF126inSIY9j1lyq55K5Ro-N-rWfP0xbkw,13774
11
+ unicex/_abc/uni_websocket_manager.py,sha256=yYKypPkIe3rKfWBuTsS8rkwIPljpd1588CYDkeTOYqE,9905
12
+ unicex/_base/__init__.py,sha256=ckqI6LVP3DHuIiLXKx67rJyG0SEEsPnBr8uk1Dw9cwg,114
13
+ unicex/_base/client.py,sha256=asIIQLZlRwwmUDvxveSv7aCvth54iiSRJdz19bxGorI,8904
14
+ unicex/_base/websocket.py,sha256=LEOmO5U4YmF3NjbCRdeiawGBkP_TCTjASU49civMT_c,10186
15
+ unicex/binance/__init__.py,sha256=sDk4ZjakRdpFMaMSpOCfqjf6ZPfAS9tlrt4WlDHtDkw,932
16
+ unicex/binance/adapter.py,sha256=J5l2wuGOa33WdVOwFJobvPp5BNgW-Jt06q-YVM3oubo,8806
17
+ unicex/binance/client.py,sha256=DuHpcQn3XfRBF5-8uFlY6gXr4QWPfeOte9vqQsCC7lI,61016
18
+ unicex/binance/exchange_info.py,sha256=_1AEa9B8Id4aj4-0VPDBL3rWE0yqwj0KFwT9N97OSeA,353
19
+ unicex/binance/uni_client.py,sha256=W4yxiU0kkJKPJjimhv4KAWreuEBwt7GgrWXefcw5BEA,8365
20
+ unicex/binance/uni_websocket_manager.py,sha256=FywEuUt3CiDcAQPo3ItdW2pPgbLDvVpVYFM2D_QscPU,8679
21
+ unicex/binance/user_websocket.py,sha256=HJ_3VZV0Bil0vfsdLrZElXm-gUqd8mGzXzfQ0_sHjFc,7861
22
+ unicex/binance/websocket_manager.py,sha256=Idrq0Qzi14QgpIG5d77S-r1h_BvRXphcxY2BT2IOQXA,45511
23
+ unicex/bitget/__init__.py,sha256=8govSOEyWjA62js-ZTQIiSYWSmcEUFSC9hVTpS8eosk,929
24
+ unicex/bitget/adapter.py,sha256=frQBOKFsIB8mXc_Ime2-Iby_nRQlSpzXjshC5AxoDTA,7741
25
+ unicex/bitget/client.py,sha256=PNjozV_qRXU2_OZVWhfHao0jJcaBO4X66ysvMdShQ-s,89687
26
+ unicex/bitget/exchange_info.py,sha256=oR0L60Ew4QDkNle5-e7CBmWBwTj-5IbW57CkEeTGDMw,352
27
+ unicex/bitget/uni_client.py,sha256=MrXAmthTDTEQZ1ZY3LuqkCKL1bw_mKHMdiV4XiRFO-M,8641
28
+ unicex/bitget/uni_websocket_manager.py,sha256=y-HXrREy_ruNeUzdRv5nHwBZQgbupxzp1UV-IFOpN_8,9793
29
+ unicex/bitget/user_websocket.py,sha256=tlkv7Rmsw_FSfCJnEMOK_9jRsXRk2Ah_slqG8C-uhuo,129
30
+ unicex/bitget/websocket_manager.py,sha256=RmJmEWwdzi05cfsmwOI2jybjIHBgt6qyYipJZ5BUw94,9916
31
+ unicex/bybit/__init__.py,sha256=SrMBh6K5zUt4JheWUpNUYNb1NCDr2ujTFv4IDguaGZI,926
32
+ unicex/bybit/adapter.py,sha256=vRtAuZ4WMRJUttCwfm5pwMmvrsdfg2_AG9lsBtf4lrU,5210
33
+ unicex/bybit/client.py,sha256=FRISjOcGqR4AM02TF16fUUY54o4GUvaarWKZdy61MYQ,60971
34
+ unicex/bybit/exchange_info.py,sha256=36WiJ49TxpSdZpeMwZdjQIkRdf2wJXahdkNxVaNjNRE,351
35
+ unicex/bybit/uni_client.py,sha256=YXoz5lPfGDQnZNz48rqJ0hXnRMrl1wgvmJgsqy0_JAg,8360
36
+ unicex/bybit/uni_websocket_manager.py,sha256=JQgZ_rl5qe30BOOMtzUYY2ZaAeQ-gYZj3HixQ1DiSpg,9328
37
+ unicex/bybit/user_websocket.py,sha256=IGGEnwyWs5jOppgK_R7SisBDvsiF1_piTswBrdQOgDg,128
38
+ unicex/bybit/websocket_manager.py,sha256=7edh7C0mMgYBRQx-_IcL21rdy-AvNfUyaZ9dL30syfI,269
39
+ unicex/gateio/__init__.py,sha256=dsKvhQhDcw4_w0S4c9IjKkCoOg4DCUtSecEUOlfstug,929
40
+ unicex/gateio/adapter.py,sha256=BqQLo8KrImwbO_89NwaPRAQ4Kx14m3YI1uFluE2ZL_U,6856
41
+ unicex/gateio/client.py,sha256=slwVygRBZT5dW8te6ysLJAbr56nVURwpB8eLc2xVoaA,53717
42
+ unicex/gateio/exchange_info.py,sha256=x_jZT14vMt2Cv4AtGhkFiAuCv43t4hwqQ4tiEjJ-HPY,352
43
+ unicex/gateio/uni_client.py,sha256=btfI-dozBRsbjEMvSlUX8zp6SOvIS1xDxk_5hNqRfz4,9648
44
+ unicex/gateio/uni_websocket_manager.py,sha256=3KAHhgJNISTOZRBTwbZPigc8iq_IHS19NwnuAsnfeXg,9329
45
+ unicex/gateio/user_websocket.py,sha256=4qZX9N2RjlJ-e25Eszz12OeCM17j5DdXVimBVaLj53w,129
46
+ unicex/gateio/websocket_manager.py,sha256=l2gGGHrJTNAth15pURjDkzn36gplqlhsfslPPvdypIw,270
47
+ unicex/hyperliquid/__init__.py,sha256=qGTAkwfXLvknvHET_iA7Qml3jkxxxA0moU_98nGTcVU,944
48
+ unicex/hyperliquid/adapter.py,sha256=ga5Sv6niukvn8YM8Mw4TsQbdiVc0YiTmW51KoOTviTI,9706
49
+ unicex/hyperliquid/client.py,sha256=8YnPh_6Hj8vFYcPbMaPae6OfroFzn28VI6iL3FQfa84,84255
50
+ unicex/hyperliquid/exchange_info.py,sha256=OBaBpAEij2Fv6q10rbQwGzBEU-5xGkCze_slEo9Q2N0,4128
51
+ unicex/hyperliquid/uni_client.py,sha256=4jv2uC076PBeq-EzCKvwaEEMk_M3HpsWA6iwNepJg7E,15674
52
+ unicex/hyperliquid/uni_websocket_manager.py,sha256=AzR_8Aq98NkCA1oc2IiH02ysMOYLaisEmcuuaafeaJw,9334
53
+ unicex/hyperliquid/user_websocket.py,sha256=BKD9ap2bx5DwpkkwwecfOTVedrZBR9eMAITgCBgg02w,134
54
+ unicex/hyperliquid/websocket_manager.py,sha256=ijs5bDX6pdQmCKvCsRowhxVf6Su_PuW58NTblwxeeXw,275
55
+ unicex/mexc/__init__.py,sha256=lltANqM_2P-fmF5j8o5-pjmORPuK6C5sVjcQhuUU_R0,923
56
+ unicex/mexc/adapter.py,sha256=zTnSMhr_ZkBy1teGWCNsmxoKs15iZczbgLkuCn-1-5o,9824
57
+ unicex/mexc/client.py,sha256=RZffkRARyViEPv_LeUEc7Tni4ZGJkvoyHrG25a1UANQ,30936
58
+ unicex/mexc/exchange_info.py,sha256=Nqzn4b791H3DLXsI6YZiY34VOZgHTG7evfRNWQnd9Nk,1329
59
+ unicex/mexc/uni_client.py,sha256=0unhbU3WfqrMsV_y15VnIFjwUzQLVtp1tx6spV6cHKI,9477
60
+ unicex/mexc/uni_websocket_manager.py,sha256=MAKyGyit8Ve4doM7y8cTQbVVbpzlJx2HcHaSkLG4Jp4,9327
61
+ unicex/mexc/user_websocket.py,sha256=l77-e6i0B2btd7a5IcCytbgswnV171NqOhunTcbaq48,127
62
+ unicex/mexc/websocket_manager.py,sha256=6bl0LHPS7bDOeUkrRKVEpFBfrfHAUIG_SA_2P2beD3o,268
63
+ unicex/okx/__init__.py,sha256=Ljbw3AP0YrPF5bIPJi_3JP3B_czR9xurYHI24rgWk9M,920
64
+ unicex/okx/adapter.py,sha256=aD9yAP7AXJDffiUxl9czuvPZvzQIcoFmaPAVVszOCaQ,5365
65
+ unicex/okx/client.py,sha256=KjjoPhpneIeBvOq7JwLFZwLAhaWYnFKMaQO5W2uD7Bc,11264
66
+ unicex/okx/exchange_info.py,sha256=q2DSVDq-JYN72LA5d_RX0C73Ha_xAxjeOobX8IxjpkM,2257
67
+ unicex/okx/uni_client.py,sha256=cxyknyu3vSAryhLN47W5CktfFU_xUzPZbrpWpyNh7As,8652
68
+ unicex/okx/uni_websocket_manager.py,sha256=b4f_QjA64DJmENQdIGb5IOVc7kvit7KMCdWeCmRbxGY,9326
69
+ unicex/okx/user_websocket.py,sha256=8c9kpm-xVa729pW93OKUGLHaE9MY0uzEpjIgNIFRF80,126
70
+ unicex/okx/websocket_manager.py,sha256=Bw25A592zTuIJQu8yR55eG7V37RwntA2LWLeMY8Popo,267
71
+ unicex-0.7.0.dist-info/licenses/LICENSE,sha256=lNNK4Vqak9cXm6qVJLhbqS7iR_BMj6k7fd7XQ6l1k54,1507
72
+ unicex-0.7.0.dist-info/METADATA,sha256=ryytEiZ7sPKu6cBAumLjEz9ft04Cp82JznFhcFnKsnA,9099
73
+ unicex-0.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
74
+ unicex-0.7.0.dist-info/top_level.txt,sha256=_7rar-0OENIg4KRy6cgjWiebFYAJhjKEcMggAocGWG4,7
75
+ unicex-0.7.0.dist-info/RECORD,,
unicex/bitrue/__init__.py DELETED
@@ -1,15 +0,0 @@
1
- """Пакет, содержащий реализации клиентов и менеджеров для работы с биржей Bitrue."""
2
-
3
- __all__ = [
4
- "Client",
5
- "UniClient",
6
- "UserWebsocket",
7
- "WebsocketManager",
8
- "UniWebsocketManager",
9
- ]
10
-
11
- from .client import Client
12
- from .uni_client import UniClient
13
- from .uni_websocket_manager import UniWebsocketManager
14
- from .user_websocket import UserWebsocket
15
- from .websocket_manager import WebsocketManager
unicex/bitrue/adapter.py DELETED
@@ -1,8 +0,0 @@
1
- __all__ = ["Adapter"]
2
-
3
- from unicex.utils import catch_adapter_errors, decorate_all_methods
4
-
5
-
6
- @decorate_all_methods(catch_adapter_errors)
7
- class Adapter:
8
- """Адаптер для унификации данных с Bitrue API."""
unicex/bitrue/client.py DELETED
@@ -1,128 +0,0 @@
1
- __all__ = ["Client"]
2
-
3
- import time
4
- from typing import Any
5
-
6
- from unicex._base import BaseClient
7
- from unicex.exceptions import NotAuthorized
8
- from unicex.types import RequestMethod
9
- from unicex.utils import dict_to_query_string, filter_params, generate_hmac_sha256_signature
10
-
11
-
12
- class Client(BaseClient):
13
- """Клиент для работы с Bitrue API."""
14
-
15
- _BASE_URL: str = "https://www.bitrue.com"
16
- """Базовый URL для REST API Bitrue."""
17
-
18
- _RECV_WINDOW: int = 5000
19
- """Стандартный интервал времени для получения ответа от сервера."""
20
-
21
- def _get_headers(self) -> dict[str, str]:
22
- """Возвращает заголовки для авторизованных запросов."""
23
- headers = {"Accept": "application/json"}
24
- if self._api_key: # type: ignore[attr-defined]
25
- headers["X-MBX-APIKEY"] = self._api_key # type: ignore[attr-defined]
26
- return headers
27
-
28
- def _prepare_payload(
29
- self,
30
- *,
31
- signed: bool,
32
- params: dict[str, Any] | None,
33
- data: dict[str, Any] | None,
34
- ) -> tuple[dict[str, Any], dict[str, str] | None]:
35
- """Подготавливает payload и заголовки для запроса."""
36
- params = filter_params(params) if params else {}
37
- data = filter_params(data) if data else {}
38
-
39
- if not signed:
40
- return {"params": params, "data": data}, None
41
-
42
- if not self.is_authorized():
43
- raise NotAuthorized("Api key is required to private endpoints")
44
-
45
- payload = {**params, **data}
46
- payload["timestamp"] = int(time.time() * 1000)
47
- payload["recvWindow"] = self._RECV_WINDOW
48
-
49
- query_string = dict_to_query_string(payload)
50
- payload["signature"] = generate_hmac_sha256_signature(
51
- self._api_secret, # type: ignore[attr-defined]
52
- query_string,
53
- )
54
-
55
- headers = self._get_headers()
56
- return payload, headers
57
-
58
- async def _make_request(
59
- self,
60
- method: RequestMethod,
61
- endpoint: str,
62
- signed: bool = False,
63
- *,
64
- params: dict[str, Any] | None = None,
65
- data: dict[str, Any] | None = None,
66
- ) -> Any:
67
- """Выполняет HTTP-запрос к Bitrue API."""
68
- url = f"{self._BASE_URL}{endpoint}"
69
- has_body = bool(data)
70
- payload, headers = self._prepare_payload(signed=signed, params=params, data=data)
71
-
72
- if not signed:
73
- return await super()._make_request(
74
- method=method,
75
- url=url,
76
- params=payload["params"],
77
- data=payload["data"],
78
- headers=headers,
79
- )
80
-
81
- if has_body:
82
- return await super()._make_request(
83
- method=method,
84
- url=url,
85
- data=payload,
86
- headers=headers,
87
- )
88
- return await super()._make_request(
89
- method=method,
90
- url=url,
91
- params=payload,
92
- headers=headers,
93
- )
94
-
95
- async def request(
96
- self,
97
- method: RequestMethod,
98
- endpoint: str,
99
- params: dict[str, Any] | None,
100
- data: dict[str, Any] | None,
101
- signed: bool,
102
- ) -> dict:
103
- """Специальный метод для выполнения произвольных REST-запросов.
104
-
105
- Параметры:
106
- method (`RequestMethod`): HTTP-метод запроса ("GET", "POST" и т.д.).
107
- endpoint (`str`): Относительный путь эндпоинта Bitrue API.
108
- params (`dict | None`): Query-параметры запроса.
109
- data (`dict | None`): Тело запроса.
110
- signed (`bool`): Нужно ли подписывать запрос.
111
-
112
- Возвращает:
113
- `dict`: Ответ Bitrue API.
114
- """
115
- return await self._make_request(
116
- method=method,
117
- endpoint=endpoint,
118
- params=params,
119
- data=data,
120
- signed=signed,
121
- )
122
-
123
- async def server_time(self) -> dict:
124
- """Получение серверного времени.
125
-
126
- https://www.bitrue.com/api_docs_includes_file/spot/index.html#general-endpoints
127
- """
128
- return await self._make_request("GET", "/api/v1/time")