unicex 0.13.17__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 (93) hide show
  1. unicex/__init__.py +200 -0
  2. unicex/_abc/__init__.py +11 -0
  3. unicex/_abc/exchange_info.py +216 -0
  4. unicex/_abc/uni_client.py +329 -0
  5. unicex/_abc/uni_websocket_manager.py +294 -0
  6. unicex/_base/__init__.py +9 -0
  7. unicex/_base/client.py +214 -0
  8. unicex/_base/websocket.py +261 -0
  9. unicex/binance/__init__.py +27 -0
  10. unicex/binance/adapter.py +202 -0
  11. unicex/binance/client.py +1577 -0
  12. unicex/binance/exchange_info.py +62 -0
  13. unicex/binance/uni_client.py +188 -0
  14. unicex/binance/uni_websocket_manager.py +166 -0
  15. unicex/binance/user_websocket.py +186 -0
  16. unicex/binance/websocket_manager.py +912 -0
  17. unicex/bitget/__init__.py +27 -0
  18. unicex/bitget/adapter.py +188 -0
  19. unicex/bitget/client.py +2514 -0
  20. unicex/bitget/exchange_info.py +48 -0
  21. unicex/bitget/uni_client.py +198 -0
  22. unicex/bitget/uni_websocket_manager.py +275 -0
  23. unicex/bitget/user_websocket.py +7 -0
  24. unicex/bitget/websocket_manager.py +232 -0
  25. unicex/bybit/__init__.py +27 -0
  26. unicex/bybit/adapter.py +208 -0
  27. unicex/bybit/client.py +1876 -0
  28. unicex/bybit/exchange_info.py +53 -0
  29. unicex/bybit/uni_client.py +200 -0
  30. unicex/bybit/uni_websocket_manager.py +291 -0
  31. unicex/bybit/user_websocket.py +7 -0
  32. unicex/bybit/websocket_manager.py +339 -0
  33. unicex/enums.py +273 -0
  34. unicex/exceptions.py +64 -0
  35. unicex/extra.py +335 -0
  36. unicex/gate/__init__.py +27 -0
  37. unicex/gate/adapter.py +178 -0
  38. unicex/gate/client.py +1667 -0
  39. unicex/gate/exchange_info.py +55 -0
  40. unicex/gate/uni_client.py +214 -0
  41. unicex/gate/uni_websocket_manager.py +269 -0
  42. unicex/gate/user_websocket.py +7 -0
  43. unicex/gate/websocket_manager.py +513 -0
  44. unicex/hyperliquid/__init__.py +27 -0
  45. unicex/hyperliquid/adapter.py +261 -0
  46. unicex/hyperliquid/client.py +2315 -0
  47. unicex/hyperliquid/exchange_info.py +119 -0
  48. unicex/hyperliquid/uni_client.py +325 -0
  49. unicex/hyperliquid/uni_websocket_manager.py +269 -0
  50. unicex/hyperliquid/user_websocket.py +7 -0
  51. unicex/hyperliquid/websocket_manager.py +393 -0
  52. unicex/mapper.py +111 -0
  53. unicex/mexc/__init__.py +27 -0
  54. unicex/mexc/_spot_ws_proto/PrivateAccountV3Api_pb2.py +38 -0
  55. unicex/mexc/_spot_ws_proto/PrivateDealsV3Api_pb2.py +38 -0
  56. unicex/mexc/_spot_ws_proto/PrivateOrdersV3Api_pb2.py +38 -0
  57. unicex/mexc/_spot_ws_proto/PublicAggreBookTickerV3Api_pb2.py +38 -0
  58. unicex/mexc/_spot_ws_proto/PublicAggreDealsV3Api_pb2.py +40 -0
  59. unicex/mexc/_spot_ws_proto/PublicAggreDepthsV3Api_pb2.py +40 -0
  60. unicex/mexc/_spot_ws_proto/PublicBookTickerBatchV3Api_pb2.py +38 -0
  61. unicex/mexc/_spot_ws_proto/PublicBookTickerV3Api_pb2.py +38 -0
  62. unicex/mexc/_spot_ws_proto/PublicDealsV3Api_pb2.py +40 -0
  63. unicex/mexc/_spot_ws_proto/PublicFuture_pb2.py +103 -0
  64. unicex/mexc/_spot_ws_proto/PublicIncreaseDepthsBatchV3Api_pb2.py +38 -0
  65. unicex/mexc/_spot_ws_proto/PublicIncreaseDepthsV3Api_pb2.py +40 -0
  66. unicex/mexc/_spot_ws_proto/PublicLimitDepthsV3Api_pb2.py +40 -0
  67. unicex/mexc/_spot_ws_proto/PublicMiniTickerV3Api_pb2.py +38 -0
  68. unicex/mexc/_spot_ws_proto/PublicMiniTickersV3Api_pb2.py +38 -0
  69. unicex/mexc/_spot_ws_proto/PublicSpotKlineV3Api_pb2.py +38 -0
  70. unicex/mexc/_spot_ws_proto/PushDataV3ApiWrapper_pb2.py +38 -0
  71. unicex/mexc/_spot_ws_proto/__init__.py +335 -0
  72. unicex/mexc/adapter.py +239 -0
  73. unicex/mexc/client.py +846 -0
  74. unicex/mexc/exchange_info.py +47 -0
  75. unicex/mexc/uni_client.py +211 -0
  76. unicex/mexc/uni_websocket_manager.py +269 -0
  77. unicex/mexc/user_websocket.py +7 -0
  78. unicex/mexc/websocket_manager.py +456 -0
  79. unicex/okx/__init__.py +27 -0
  80. unicex/okx/adapter.py +150 -0
  81. unicex/okx/client.py +2864 -0
  82. unicex/okx/exchange_info.py +47 -0
  83. unicex/okx/uni_client.py +202 -0
  84. unicex/okx/uni_websocket_manager.py +269 -0
  85. unicex/okx/user_websocket.py +7 -0
  86. unicex/okx/websocket_manager.py +743 -0
  87. unicex/types.py +164 -0
  88. unicex/utils.py +218 -0
  89. unicex-0.13.17.dist-info/METADATA +243 -0
  90. unicex-0.13.17.dist-info/RECORD +93 -0
  91. unicex-0.13.17.dist-info/WHEEL +5 -0
  92. unicex-0.13.17.dist-info/licenses/LICENSE +28 -0
  93. unicex-0.13.17.dist-info/top_level.txt +1 -0
unicex/types.py ADDED
@@ -0,0 +1,164 @@
1
+ """Модуль, который предоставляет типы данных для работы с библиотекой."""
2
+
3
+ __all__ = [
4
+ "TickerDailyDict",
5
+ "TickerDailyItem",
6
+ "KlineDict",
7
+ "TradeDict",
8
+ "AggTradeDict",
9
+ "RequestMethod",
10
+ "LoggerLike",
11
+ "OpenInterestDict",
12
+ "OpenInterestItem",
13
+ "TickerInfoItem",
14
+ "TickersInfoDict",
15
+ "LiquidationDict",
16
+ ]
17
+
18
+ from logging import Logger as LoggingLogger
19
+ from typing import Literal, TypedDict
20
+
21
+ import loguru
22
+
23
+ type LoggerLike = LoggingLogger | loguru.Logger
24
+ """Объединение логгеров: loguru._logger.Logger или logging.Logger."""
25
+
26
+ type RequestMethod = Literal["GET", "POST", "PUT", "DELETE", "PATCH"]
27
+ """Типы методов HTTP запросов."""
28
+
29
+
30
+ class TickerDailyItem(TypedDict):
31
+ """Статистика одного тикера за последние 24 часа."""
32
+
33
+ p: float
34
+ """Изменение цены за 24 ч."""
35
+
36
+ v: float
37
+ """Объем торгов за 24 ч. в монетах."""
38
+
39
+ q: float
40
+ """Объем торгов за 24 ч. в долларах."""
41
+
42
+
43
+ type TickerDailyDict = dict[str, TickerDailyItem]
44
+ """Статистика тикеров за последние 24 часа."""
45
+
46
+
47
+ class KlineDict(TypedDict):
48
+ """Модель свечи."""
49
+
50
+ s: str
51
+ """Символ."""
52
+
53
+ t: int
54
+ """Время открытия. В миллисекундах."""
55
+
56
+ o: float
57
+ """Цена открытия свечи."""
58
+
59
+ h: float
60
+ """Верхняя точка свечи."""
61
+
62
+ l: float # noqa
63
+ """Нижняя точка свечи."""
64
+
65
+ c: float
66
+ """Цена закрытия свечи."""
67
+
68
+ v: float
69
+ """Объем свечи. В монетах."""
70
+
71
+ q: float
72
+ """Объем свечи. В долларах."""
73
+
74
+ T: int | None
75
+ """Время закрытия. В миллисекундах."""
76
+
77
+ x: bool | None
78
+ """Флаг закрыта ли свеча."""
79
+
80
+
81
+ class TradeDict(TypedDict):
82
+ """Модель сделки."""
83
+
84
+ t: int
85
+ """Время сделки. В миллисекундах."""
86
+
87
+ s: str
88
+ """Символ."""
89
+
90
+ S: Literal["BUY", "SELL"]
91
+ """Направление сделки."""
92
+
93
+ p: float
94
+ """Цена сделки."""
95
+
96
+ v: float
97
+ """Объем сделки. В монетах."""
98
+
99
+
100
+ class AggTradeDict(TradeDict):
101
+ """Модель агрегированной сделки."""
102
+
103
+ pass
104
+
105
+
106
+ class OpenInterestItem(TypedDict):
107
+ """Модель одного элемента открытого интереса."""
108
+
109
+ t: int
110
+ """Время. В миллисекундах."""
111
+
112
+ v: float
113
+ """Открытый интерес. В монетах."""
114
+
115
+
116
+ type OpenInterestDict = dict[str, OpenInterestItem]
117
+ """Модель открытого интереса."""
118
+
119
+
120
+ class LiquidationDict(TypedDict):
121
+ """Модель ликвидации."""
122
+
123
+ t: int
124
+ """Время. В миллисекундах."""
125
+
126
+ s: str
127
+ """Символ."""
128
+
129
+ S: Literal["BUY", "SELL"]
130
+ """Сторона."""
131
+
132
+ v: float
133
+ """Объем ликвидации. В монетах."""
134
+
135
+ p: float
136
+ """Цена ликвидации."""
137
+
138
+
139
+ class TickerInfoItem(TypedDict):
140
+ """Информация о размерах тиков, ступеней цены и множителя контракта (если есть) для тикера.
141
+
142
+ На некоторых биржах удобнее делать округление через precisions, на некоторых через step,
143
+ потому что иногда встречаются шаги, которые не являются степенью 10. Поэтому обязательно
144
+ должны быть определены tick_precision ИЛИ tick_step, а так же size_precision ИЛИ size_step.
145
+ """
146
+
147
+ tick_precision: int | None
148
+ """Количество знаков после запятой для цены."""
149
+
150
+ tick_step: float | None
151
+ """Шаг одного деления для цены."""
152
+
153
+ size_precision: int | None
154
+ """Количество знаков после запятой для объема."""
155
+
156
+ size_step: float | None
157
+ """Шаг одного деления для объема."""
158
+
159
+ contract_size: float | None
160
+ """Множитель контракта (если есть)."""
161
+
162
+
163
+ type TickersInfoDict = dict[str, TickerInfoItem]
164
+ """Информация о размерах тиков, ступеней цены и множителя контракта (если есть) для всех тикеров."""
unicex/utils.py ADDED
@@ -0,0 +1,218 @@
1
+ """Модуль, который предоставляет дополнительные функции, которые нужны для внутренного использования в библиотеке."""
2
+
3
+ __all__ = [
4
+ "dict_to_query_string",
5
+ "generate_hmac_sha256_signature",
6
+ "sort_params_by_alphabetical_order",
7
+ "filter_params",
8
+ "batched_list",
9
+ "catch_adapter_errors",
10
+ "decorate_all_methods",
11
+ "symbol_to_exchange_format",
12
+ ]
13
+
14
+ import base64
15
+ import hashlib
16
+ import hmac
17
+ import json
18
+ from collections.abc import Callable, Iterable
19
+ from functools import wraps
20
+ from typing import Any, Literal
21
+ from urllib.parse import urlencode
22
+
23
+ from unicex.enums import Exchange, MarketType
24
+ from unicex.exceptions import AdapterError
25
+
26
+
27
+ def filter_params(params: dict) -> dict:
28
+ """Фильтрует параметры запроса, удаляя None-значения.
29
+
30
+ Параметры:
31
+ params (`dict`): Словарь параметров запроса.
32
+
33
+ Возвращает:
34
+ `dict`: Отфильтрованный словарь параметров запроса.
35
+ """
36
+ return {k: v for k, v in params.items() if v is not None}
37
+
38
+
39
+ def sort_params_by_alphabetical_order(params: dict) -> dict:
40
+ """Сортирует параметры запроса по алфавиту.
41
+
42
+ Параметры:
43
+ params (`dict`): Словарь параметров запроса.
44
+
45
+ Возвращает:
46
+ `dict`: Отсортированный словарь параметров запроса.
47
+ """
48
+ return dict(sorted(params.items()))
49
+
50
+
51
+ def dict_to_query_string(params: dict) -> str:
52
+ """Преобразует словарь параметров в query string для URL.
53
+
54
+ - Списки и словари автоматически сериализуются в JSON.
55
+ - Используется стандартная urlencode кодировка.
56
+
57
+ Параметры:
58
+ params (`dict`): Словарь параметров запроса.
59
+
60
+ Возвращает:
61
+ `str`: Строка параметров, готовая для использования в URL.
62
+ """
63
+ processed = {
64
+ k: json.dumps(v, separators=(",", ":")) if isinstance(v, list | dict) else v
65
+ for k, v in params.items()
66
+ }
67
+ return urlencode(processed, doseq=True)
68
+
69
+
70
+ def generate_hmac_sha256_signature(
71
+ secret_key: str,
72
+ payload: str,
73
+ encoding: Literal["hex", "base64"] = "hex",
74
+ ) -> str:
75
+ """Генерирует HMAC-SHA256 подпись.
76
+
77
+ encoding:
78
+ - "hex" → шестнадцатеричная строка
79
+ - "base64" → base64-строка
80
+ """
81
+ digest = hmac.new(secret_key.encode("utf-8"), payload.encode("utf-8"), hashlib.sha256).digest()
82
+ if encoding == "hex":
83
+ return digest.hex()
84
+ elif encoding == "base64":
85
+ return base64.b64encode(digest).decode()
86
+ else:
87
+ raise ValueError("encoding must be 'hex' or 'base64'")
88
+
89
+
90
+ def batched_list[T](iterable: Iterable[T], n: int) -> list[list[T]]:
91
+ """Разбивает последовательность на чанки фиксированного размера.
92
+
93
+ Всегда возвращает список списков (list[list[T]]).
94
+ """
95
+ if n <= 0:
96
+ raise ValueError("n must be greater than 0")
97
+
98
+ result: list[list[T]] = []
99
+ batch: list[T] = []
100
+ for item in iterable:
101
+ batch.append(item)
102
+ if len(batch) == n:
103
+ result.append(batch)
104
+ batch = []
105
+ if batch:
106
+ result.append(batch)
107
+ return result
108
+
109
+
110
+ def catch_adapter_errors(func: Callable):
111
+ """Декоратор для унификации обработки ошибок в адаптерах.
112
+
113
+ Перехватывает все исключения внутри функции и выбрасывает AdapterError
114
+ с подробным сообщением, включающим тип и текст исходного исключения,
115
+ а также имя функции.
116
+
117
+ Параметры:
118
+ func (Callable): Декорируемая функция.
119
+
120
+ Возвращает:
121
+ Callable: Обёрнутую функцию, выбрасывающую AdapterError при ошибке.
122
+ """
123
+
124
+ @wraps(func)
125
+ def wrapper(*args, **kwargs):
126
+ try:
127
+ return func(*args, **kwargs)
128
+ except Exception as e:
129
+ args_repr = repr(args)
130
+ if len(args_repr) > 400:
131
+ args_preview = args_repr[:400] + "... (truncated)"
132
+ else:
133
+ args_preview = args_repr
134
+
135
+ kwargs_repr = repr(kwargs)
136
+ if len(kwargs_repr) > 400:
137
+ kwargs_preview = kwargs_repr[:400] + "... (truncated)"
138
+ else:
139
+ kwargs_preview = kwargs_repr
140
+
141
+ raise AdapterError(
142
+ f"({type(e).__name__}): {e}. Can not convert input (args={args_preview}, kwargs={kwargs_preview}) in function `{func.__name__}`."
143
+ ) from None
144
+
145
+ return wrapper
146
+
147
+
148
+ def decorate_all_methods(decorator: Callable[[Callable[..., Any]], Callable[..., Any]]) -> Callable:
149
+ """Класс-декоратор, который оборачивает все методы класса указанным декоратором.
150
+
151
+ Декоратор применяется только к методам/функциям, не начинающимся с "__".
152
+
153
+ Парамтеры:
154
+ decorator: Декоратор, который нужно применить ко всем методам.
155
+
156
+ Возвращает:
157
+ Callable: Декоратор для классов.
158
+
159
+ Пример:
160
+ >>> def debug(func):
161
+ ... def wrapper(*args, **kwargs):
162
+ ... print(f"Call {func.__name__}")
163
+ ... return func(*args, **kwargs)
164
+ ...
165
+ ... return wrapper
166
+ >>> @decorate_all_methods(debug)
167
+ ... class Test:
168
+ ... def hello(self):
169
+ ... return "hi"
170
+ >>> Test().hello()
171
+ Call hello
172
+ 'hi'
173
+
174
+ """
175
+
176
+ def wrapper(cls: type) -> type:
177
+ for k, v in cls.__dict__.items():
178
+ if isinstance(v, staticmethod):
179
+ func = v.__func__
180
+ setattr(cls, k, staticmethod(decorator(func)))
181
+ elif isinstance(v, classmethod):
182
+ func = v.__func__
183
+ setattr(cls, k, classmethod(decorator(func)))
184
+ elif callable(v) and not k.startswith("__"):
185
+ setattr(cls, k, decorator(v))
186
+ return cls
187
+
188
+ return wrapper
189
+
190
+
191
+ def symbol_to_exchange_format(
192
+ symbol: str, exchange: Exchange, market_type: MarketType | None = None
193
+ ) -> str:
194
+ """Преобразует символ в формат, который используется на бирже.
195
+
196
+ Параметры:
197
+ symbol (str): Символ, обязательно в формате 'BTCUSDT' или 'btcusdt'.
198
+
199
+ Возвращает:
200
+ str: Символ в формате, который используется на бирже, заглавными буквами.
201
+ """
202
+ symbol_upper = symbol.upper()
203
+ if exchange == Exchange.MEXC:
204
+ if market_type == MarketType.FUTURES:
205
+ return symbol_upper.replace("USDT", "_USDT")
206
+ elif exchange == Exchange.OKX:
207
+ if market_type == MarketType.FUTURES:
208
+ return symbol_upper.replace("USDT", "-USDT-SWAP")
209
+ elif market_type == MarketType.SPOT:
210
+ return symbol_upper.replace("USDT", "-USDT")
211
+ elif exchange == Exchange.GATE:
212
+ return symbol_upper.replace("USDT", "_USDT")
213
+ elif exchange == Exchange.HYPERLIQUID:
214
+ if market_type == MarketType.FUTURES:
215
+ return symbol.removesuffix("USDT") # Вот тут мб и не так, там вроде что-то к USDC
216
+ else:
217
+ return symbol.removesuffix("USDT") # Вот тут мб и не так, там вроде что-то к USDC
218
+ return symbol_upper
@@ -0,0 +1,243 @@
1
+ Metadata-Version: 2.4
2
+ Name: unicex
3
+ Version: 0.13.17
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: protobuf>=6.32.1
47
+ Requires-Dist: websockets>=15.0.1
48
+ Dynamic: license-file
49
+
50
+ # Unified Crypto Exchange API
51
+
52
+ `unicex` — асинхронная библиотека для работы с криптовалютными биржами, реализующая унифицированный интерфейс поверх «сырых» REST и WebSocket API разных бирж. Поддерживает спотовый и USDT-фьючерсный рынки.
53
+
54
+ ## ✅ Статус реализации
55
+
56
+ | Exchange | Client | Auth | WS Manager | User WS | Uni Client | Uni WS Manager | ExchangeInfo |
57
+ |-----------------|--------|------|------------|---------|------------|----------------|--------------|
58
+ | **Binance** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
59
+ | **Bitget** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
60
+ | **Bybit** | ✓ | ✓ | ✓ | | ✓ | ✓ | ✓ |
61
+ | **Gateio** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
62
+ | **Hyperliquid** | ✓ | ✓ | ✓ | ✓ | ✓ | | |
63
+ | **Mexc** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
64
+ | **Okx** | ✓ | ✓ | ✓ | | ✓ | | ✓ |
65
+ ---
66
+
67
+
68
+ ### 📖 Описание колонок
69
+
70
+ - **Client** – Обертки над HTTP методами следующих разделов: market, order, position, account.
71
+ - **Auth** – Поддержка авторизации и приватных эндпоинтов.
72
+ - **WS Manager** – Обертки над вебсокетами биржи.
73
+ - **User WS** – Поддержка пользовательских вебсокетов.
74
+ - **UniClient** –Унифированный клиент.
75
+ - **UniWebsocketManager** – Унифированный менеджер вебсокетов.
76
+ - **ExchangeInfo** - Информация о бирже для округления цен и объемов
77
+ ---
78
+
79
+ ## 🚀 Быстрый старт
80
+
81
+ - Установка: `pip install unicex` или из исходников: `pip install -e .`
82
+ - Библиотека полностью асинхронная. Примеры импорта:
83
+ - Сырые клиенты: `from unicex.binance import Client`
84
+ - Унифицированные клиенты: `from unicex.binance import UniClient`
85
+ - Вебсокет менеджеры: `from unicex.binance import WebsocketManager, UniWebsocketManager`
86
+
87
+ ### Пример: Получение рыночных данных через API
88
+
89
+ ```python
90
+ import asyncio
91
+
92
+ from unicex import Exchange, Timeframe, get_uni_client
93
+
94
+ # Выбираем биржу, с которой хотим работать.
95
+ # Поддерживаются: Binance, Bybit, Bitget, Mexc, Gateio, Hyperliquid и другие.
96
+ exchange = Exchange.BYBIT
97
+
98
+
99
+ async def main() -> None:
100
+ """Пример простого использования унифицированного клиента unicex."""
101
+ # 1️⃣ Создаём клиент для выбранной биржи
102
+ client = await get_uni_client(exchange).create()
103
+
104
+ # 2️⃣ Получаем открытый интерес по всем контрактам
105
+ open_interest = await client.open_interest()
106
+ print(open_interest)
107
+
108
+ # Пример вывода:
109
+ # {
110
+ # "BTCUSDT": {"t": 1759669833728, "v": 61099320.0},
111
+ # "ETHUSDT": {"t": 1759669833728, "v": 16302340.0},
112
+ # "SOLUSDT": {"t": 1759669833728, "v": 3427780.0},
113
+ # ...
114
+ # }
115
+
116
+ # 3️⃣ Можно точно так же получать другие данные в едином формате:
117
+ await client.tickers() # список всех тикеров
118
+ await client.futures_tickers() # тикеры фьючерсов
119
+ await client.ticker_24hr() # статистика за 24 часа (spot)
120
+ await client.futures_ticker_24hr() # статистика за 24 часа (futures)
121
+ await client.klines("BTCUSDT", Timeframe.MIN_5) # свечи спота
122
+ await client.futures_klines("BTCUSDT", Timeframe.HOUR_1) # свечи фьючерсов
123
+ await client.funding_rate() # ставка финансирования
124
+
125
+
126
+ if __name__ == "__main__":
127
+ asyncio.run(main())
128
+
129
+ ```
130
+
131
+ ### Пример: Получение данных в реальном времени через Websocket API
132
+
133
+ ```python
134
+ import asyncio
135
+ from unicex import Exchange, TradeDict, get_uni_websocket_manager
136
+ from unicex.enums import Timeframe
137
+
138
+ # Выбираем биржу, с которой хотим работать.
139
+ # Поддерживаются: Binance, Bybit, Bitget, Mexc, Gateio, Hyperliquid и другие.
140
+ exchange = Exchange.BITGET
141
+
142
+
143
+ async def main() -> None:
144
+ """Пример простого использования унифицированного менеджера Websocket от UniCEX."""
145
+
146
+ # 1️⃣ Создаём WebSocket-менеджер для выбранной биржи
147
+ ws_manager = get_uni_websocket_manager(exchange)()
148
+
149
+ # 2️⃣ Подключаемся к потоку сделок (aggTrades)
150
+ aggtrades_ws = ws_manager.aggtrades(
151
+ callback=callback,
152
+ symbols=["BTCUSDT", "ETHUSDT"],
153
+ )
154
+
155
+ # Запускаем получение данных
156
+ await aggtrades_ws.start()
157
+
158
+ # 3️⃣ Примеры других типов потоков:
159
+ futures_aggtrades_ws = ws_manager.futures_aggtrades(
160
+ callback=callback,
161
+ symbols=["BTCUSDT", "ETHUSDT"],
162
+ )
163
+
164
+ klines_ws = ws_manager.klines(
165
+ callback=callback,
166
+ symbols=["BTCUSDT", "ETHUSDT"],
167
+ timeframe=Timeframe.MIN_5,
168
+ )
169
+
170
+ futures_klines_ws = ws_manager.futures_klines(
171
+ callback=callback,
172
+ symbols=["BTCUSDT", "ETHUSDT"],
173
+ timeframe=Timeframe.MIN_1,
174
+ )
175
+
176
+ # 💡 Также у каждой биржи есть свой WebsocketManager:
177
+ # unicex.<exchange>.websocket_manager.WebsocketManager
178
+ # В нём реализованы остальные методы для работы с WS API.
179
+
180
+
181
+ async def callback(trade: TradeDict) -> None:
182
+ """Обработка входящих данных из Websocket."""
183
+ print(trade)
184
+ # Пример вывода:
185
+ # {'t': 1759670527594, 's': 'BTCUSDT', 'S': 'BUY', 'p': 123238.87, 'v': 0.05}
186
+ # {'t': 1759670527594, 's': 'BTCUSDT', 'S': 'BUY', 'p': 123238.87, 'v': 0.04}
187
+ # {'t': 1759670346828, 's': 'ETHUSDT', 'S': 'SELL', 'p': 4535.0, 'v': 0.0044}
188
+ # {'t': 1759670347087, 's': 'ETHUSDT', 'S': 'BUY', 'p': 4534.91, 'v': 0.2712}
189
+
190
+
191
+ if __name__ == "__main__":
192
+ asyncio.run(main())
193
+ ```
194
+
195
+
196
+ ### Пример: Округление цен используя фоновый класс ExchangeInfo
197
+
198
+
199
+ ```python
200
+ import asyncio
201
+ from unicex import start_exchanges_info, get_exchange_info, Exchange
202
+
203
+
204
+ async def main() -> None:
205
+ # ⏳ Запускаем фоновые процессы, которые собирают рыночные параметры всех бирж:
206
+ # - количество знаков после точки для цены и объема
207
+ # - множители контрактов для фьючерсов
208
+ await start_exchanges_info()
209
+
210
+ # Небольшая пауза, чтобы данные успели подгрузиться
211
+ await asyncio.sleep(1)
212
+
213
+ # 1️⃣ Пример 1: Округление цены для фьючерсов OKX
214
+ okx_exchange_info = get_exchange_info(Exchange.OKX)
215
+ okx_rounded_price = okx_exchange_info.round_futures_price("BTC-USDT-SWAP", 123456.1234567890)
216
+ print(okx_rounded_price) # >> 123456.1
217
+
218
+ # 2️⃣ Пример 2: Округление объема для спота Binance
219
+ binance_exchange_info = get_exchange_info(Exchange.BINANCE)
220
+ binance_rounded_quantity = binance_exchange_info.round_quantity("BTCUSDT", 1.123456789)
221
+ print(binance_rounded_quantity) # >> 1.12345
222
+
223
+ # 3️⃣ Пример 3: Получение множителя контракта (например, Mexc Futures)
224
+ mexc_exchange_info = get_exchange_info(Exchange.MEXC)
225
+ mexc_contract_multiplier = mexc_exchange_info.get_futures_ticker_info("BTC_USDT")["contract_size"]
226
+ print(mexc_contract_multiplier) # >> 0.0001
227
+
228
+ # 4️⃣ Пример 4: Реальное применение — вычисляем тейк-профит вручную
229
+ # Допустим, позиция открыта по 123123.1 USDT, хотим +3.5% тейк-профит:
230
+ take_profit_raw = 123123.1 * 1.035
231
+ print("До округления:", take_profit_raw) # >> 127432.40849999999
232
+
233
+ # Биржа требует цену в допустимом формате — округляем:
234
+ take_profit = okx_exchange_info.round_futures_price("BTC-USDT-SWAP", take_profit_raw)
235
+ print("После округления:", take_profit) # >> 127432.4
236
+
237
+ # Теперь это число можно безопасно передать в API без ошибок:
238
+ # await client.create_order(symbol="BTC-USDT-SWAP", price=take_profit, ...)
239
+
240
+
241
+ if __name__ == "__main__":
242
+ asyncio.run(main())
243
+ ```