unicex 0.5.0__py3-none-any.whl → 0.8.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.
- unicex/__init__.py +56 -124
- unicex/_abc/__init__.py +2 -0
- unicex/_abc/exchange_info.py +176 -0
- unicex/_abc/uni_client.py +2 -2
- unicex/_base/client.py +2 -2
- unicex/binance/__init__.py +12 -0
- unicex/binance/adapter.py +2 -2
- unicex/binance/exchange_info.py +12 -0
- unicex/bitget/__init__.py +14 -4
- unicex/bitget/adapter.py +1 -1
- unicex/bitget/exchange_info.py +12 -0
- unicex/bitget/uni_websocket_manager.py +1 -1
- unicex/bybit/__init__.py +12 -0
- unicex/bybit/adapter.py +2 -2
- unicex/bybit/exchange_info.py +12 -0
- unicex/bybit/uni_client.py +2 -2
- unicex/enums.py +16 -5
- unicex/extra.py +84 -6
- unicex/gateio/__init__.py +12 -0
- unicex/gateio/adapter.py +4 -4
- unicex/gateio/exchange_info.py +12 -0
- unicex/hyperliquid/__init__.py +12 -0
- unicex/hyperliquid/adapter.py +151 -30
- unicex/hyperliquid/client.py +2208 -125
- unicex/hyperliquid/exchange_info.py +100 -0
- unicex/hyperliquid/uni_client.py +176 -22
- unicex/mapper.py +35 -33
- unicex/mexc/__init__.py +12 -0
- unicex/mexc/adapter.py +30 -13
- unicex/mexc/exchange_info.py +32 -0
- unicex/mexc/uni_client.py +6 -0
- unicex/okx/__init__.py +12 -0
- unicex/okx/adapter.py +25 -9
- unicex/okx/client.py +2599 -26
- unicex/okx/exchange_info.py +50 -0
- unicex/okx/uni_client.py +10 -10
- unicex/types.py +31 -0
- unicex-0.8.0.dist-info/METADATA +192 -0
- unicex-0.8.0.dist-info/RECORD +75 -0
- unicex/bitrue/__init__.py +0 -15
- unicex/bitrue/adapter.py +0 -8
- unicex/bitrue/client.py +0 -128
- unicex/bitrue/uni_client.py +0 -151
- unicex/bitrue/uni_websocket_manager.py +0 -269
- unicex/bitrue/user_websocket.py +0 -7
- unicex/bitrue/websocket_manager.py +0 -11
- unicex/bitunix/__init__.py +0 -15
- unicex/bitunix/adapter.py +0 -8
- unicex/bitunix/client.py +0 -8
- unicex/bitunix/uni_client.py +0 -151
- unicex/bitunix/uni_websocket_manager.py +0 -269
- unicex/bitunix/user_websocket.py +0 -7
- unicex/bitunix/websocket_manager.py +0 -11
- unicex/btse/__init__.py +0 -15
- unicex/btse/adapter.py +0 -8
- unicex/btse/client.py +0 -123
- unicex/btse/uni_client.py +0 -151
- unicex/btse/uni_websocket_manager.py +0 -269
- unicex/btse/user_websocket.py +0 -7
- unicex/btse/websocket_manager.py +0 -11
- unicex/kcex/__init__.py +0 -15
- unicex/kcex/adapter.py +0 -8
- unicex/kcex/client.py +0 -8
- unicex/kcex/uni_client.py +0 -151
- unicex/kcex/uni_websocket_manager.py +0 -269
- unicex/kcex/user_websocket.py +0 -7
- unicex/kcex/websocket_manager.py +0 -11
- unicex/kraken/__init__.py +0 -15
- unicex/kraken/adapter.py +0 -8
- unicex/kraken/client.py +0 -165
- unicex/kraken/uni_client.py +0 -151
- unicex/kraken/uni_websocket_manager.py +0 -269
- unicex/kraken/user_websocket.py +0 -7
- unicex/kraken/websocket_manager.py +0 -11
- unicex/kucoin/__init__.py +0 -15
- unicex/kucoin/adapter.py +0 -8
- unicex/kucoin/client.py +0 -120
- unicex/kucoin/uni_client.py +0 -151
- unicex/kucoin/uni_websocket_manager.py +0 -269
- unicex/kucoin/user_websocket.py +0 -7
- unicex/kucoin/websocket_manager.py +0 -11
- unicex/weex/__init__.py +0 -15
- unicex/weex/adapter.py +0 -8
- unicex/weex/client.py +0 -8
- unicex/weex/uni_client.py +0 -151
- unicex/weex/uni_websocket_manager.py +0 -269
- unicex/weex/user_websocket.py +0 -7
- unicex/weex/websocket_manager.py +0 -11
- unicex/xt/__init__.py +0 -15
- unicex/xt/adapter.py +0 -8
- unicex/xt/client.py +0 -8
- unicex/xt/uni_client.py +0 -151
- unicex/xt/uni_websocket_manager.py +0 -269
- unicex/xt/user_websocket.py +0 -7
- unicex/xt/websocket_manager.py +0 -11
- unicex-0.5.0.dist-info/METADATA +0 -170
- unicex-0.5.0.dist-info/RECORD +0 -123
- {unicex-0.5.0.dist-info → unicex-0.8.0.dist-info}/WHEEL +0 -0
- {unicex-0.5.0.dist-info → unicex-0.8.0.dist-info}/licenses/LICENSE +0 -0
- {unicex-0.5.0.dist-info → unicex-0.8.0.dist-info}/top_level.txt +0 -0
|
@@ -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
|
@@ -32,7 +32,7 @@ class UniClient(IUniClient[Client]):
|
|
|
32
32
|
Возвращает:
|
|
33
33
|
list[str]: Список тикеров.
|
|
34
34
|
"""
|
|
35
|
-
raw_data = await self._client.
|
|
35
|
+
raw_data = await self._client.get_tickers(inst_type="SPOT")
|
|
36
36
|
return Adapter.tickers(raw_data=raw_data, only_usdt=only_usdt)
|
|
37
37
|
|
|
38
38
|
async def futures_tickers(self, only_usdt: bool = True) -> list[str]:
|
|
@@ -44,8 +44,8 @@ class UniClient(IUniClient[Client]):
|
|
|
44
44
|
Возвращает:
|
|
45
45
|
list[str]: Список тикеров.
|
|
46
46
|
"""
|
|
47
|
-
raw_data = await self._client.
|
|
48
|
-
return Adapter.
|
|
47
|
+
raw_data = await self._client.get_tickers(inst_type="SWAP")
|
|
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
|
"""Возвращает последнюю цену для каждого тикера.
|
|
@@ -53,7 +53,7 @@ class UniClient(IUniClient[Client]):
|
|
|
53
53
|
Возвращает:
|
|
54
54
|
dict[str, float]: Словарь с последними ценами для каждого тикера.
|
|
55
55
|
"""
|
|
56
|
-
raw_data = await self._client.
|
|
56
|
+
raw_data = await self._client.get_tickers(inst_type="SPOT")
|
|
57
57
|
return Adapter.last_price(raw_data)
|
|
58
58
|
|
|
59
59
|
async def futures_last_price(self) -> dict[str, float]:
|
|
@@ -62,7 +62,7 @@ class UniClient(IUniClient[Client]):
|
|
|
62
62
|
Возвращает:
|
|
63
63
|
dict[str, float]: Словарь с последними ценами для каждого тикера.
|
|
64
64
|
"""
|
|
65
|
-
raw_data = await self._client.
|
|
65
|
+
raw_data = await self._client.get_tickers(inst_type="SWAP")
|
|
66
66
|
return Adapter.last_price(raw_data)
|
|
67
67
|
|
|
68
68
|
async def ticker_24hr(self) -> TickerDailyDict:
|
|
@@ -71,7 +71,7 @@ class UniClient(IUniClient[Client]):
|
|
|
71
71
|
Возвращает:
|
|
72
72
|
TickerDailyDict: Словарь с статистикой за последние 24 часа для каждого тикера.
|
|
73
73
|
"""
|
|
74
|
-
raw_data = await self._client.
|
|
74
|
+
raw_data = await self._client.get_tickers(inst_type="SPOT")
|
|
75
75
|
return Adapter.ticker_24hr(raw_data=raw_data)
|
|
76
76
|
|
|
77
77
|
async def futures_ticker_24hr(self) -> TickerDailyDict:
|
|
@@ -80,8 +80,8 @@ class UniClient(IUniClient[Client]):
|
|
|
80
80
|
Возвращает:
|
|
81
81
|
TickerDailyDict: Словарь с статистикой за последние 24 часа для каждого тикера.
|
|
82
82
|
"""
|
|
83
|
-
raw_data = await self._client.
|
|
84
|
-
return Adapter.
|
|
83
|
+
raw_data = await self._client.get_tickers(inst_type="SWAP")
|
|
84
|
+
return Adapter.futures_ticker_24hr(raw_data=raw_data)
|
|
85
85
|
|
|
86
86
|
async def klines(
|
|
87
87
|
self,
|
|
@@ -108,7 +108,7 @@ class UniClient(IUniClient[Client]):
|
|
|
108
108
|
if isinstance(interval, Timeframe)
|
|
109
109
|
else interval
|
|
110
110
|
)
|
|
111
|
-
raw_data = await self._client.
|
|
111
|
+
raw_data = await self._client.get_candlesticks(
|
|
112
112
|
inst_id=symbol,
|
|
113
113
|
bar=interval,
|
|
114
114
|
after=start_time,
|
|
@@ -142,7 +142,7 @@ class UniClient(IUniClient[Client]):
|
|
|
142
142
|
if isinstance(interval, Timeframe)
|
|
143
143
|
else interval
|
|
144
144
|
)
|
|
145
|
-
raw_data = await self._client.
|
|
145
|
+
raw_data = await self._client.get_candlesticks(
|
|
146
146
|
inst_id=symbol,
|
|
147
147
|
bar=interval,
|
|
148
148
|
after=start_time,
|
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.8.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=MZRSsDRok05KZCqKur-hjOexZuoZ-tC9J6e-EIZr_lw,13824
|
|
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=JbUFyjnDAFtyuYYrh90YeOvQOZQ6faim0nWS6U0NxXw,8799
|
|
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=ZgsH8jyR-fbMHSTJO6e4stT-7l5MszGuZqMJIAEOnXY,5203
|
|
33
|
+
unicex/bybit/client.py,sha256=FRISjOcGqR4AM02TF16fUUY54o4GUvaarWKZdy61MYQ,60971
|
|
34
|
+
unicex/bybit/exchange_info.py,sha256=36WiJ49TxpSdZpeMwZdjQIkRdf2wJXahdkNxVaNjNRE,351
|
|
35
|
+
unicex/bybit/uni_client.py,sha256=0wmIRRgofuJXWvZMB1gHwuIEfoWuPhLJXthmK1f9w40,8382
|
|
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=PE5lXQORAKLhzFZBtxcarNIt2xEcDrPrphxWS0k0wEk,6842
|
|
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=Q45OLQBhnXAvjIhNWLMrRgzOZTGmgbZhIdIT6lVAVIg,10595
|
|
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=GzFwUsdabh90_pRf1u2AipxJeDW7g1-2BVvSC2xvN_E,9810
|
|
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=zXSLieRC80KY56ey1X-ss51uQ0QzouDxXppjcofLNRQ,5351
|
|
65
|
+
unicex/okx/client.py,sha256=uWcmVV9M5LpQvs89p0NFzHYytOUBt63naR9dYm6cwyM,90727
|
|
66
|
+
unicex/okx/exchange_info.py,sha256=q2DSVDq-JYN72LA5d_RX0C73Ha_xAxjeOobX8IxjpkM,2257
|
|
67
|
+
unicex/okx/uni_client.py,sha256=E_Wod0JSGt1K6k1mAIWnOv350pELbv-nic7g1KgOuos,8694
|
|
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.8.0.dist-info/licenses/LICENSE,sha256=lNNK4Vqak9cXm6qVJLhbqS7iR_BMj6k7fd7XQ6l1k54,1507
|
|
72
|
+
unicex-0.8.0.dist-info/METADATA,sha256=IFx_SwqjV2p8BhedS3vTrwsJi5oyyCBS4XXzfybueIY,9101
|
|
73
|
+
unicex-0.8.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
74
|
+
unicex-0.8.0.dist-info/top_level.txt,sha256=_7rar-0OENIg4KRy6cgjWiebFYAJhjKEcMggAocGWG4,7
|
|
75
|
+
unicex-0.8.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
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")
|