unicex 0.1.16__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unicex-0.2.0/PKG-INFO +122 -0
- unicex-0.2.0/README.md +76 -0
- {unicex-0.1.16 → unicex-0.2.0}/pyproject.toml +4 -3
- {unicex-0.1.16 → unicex-0.2.0}/unicex/__init__.py +18 -8
- {unicex-0.1.16 → unicex-0.2.0}/unicex/_abc/__init__.py +2 -1
- {unicex-0.1.16 → unicex-0.2.0}/unicex/_abc/adapter.py +2 -0
- {unicex-0.1.16/unicex/_abc/asyncio → unicex-0.2.0/unicex/_abc}/uni_client.py +2 -2
- {unicex-0.1.16/unicex/_abc/asyncio → unicex-0.2.0/unicex/_abc}/uni_websocket_manager.py +1 -1
- {unicex-0.1.16/unicex/_base/asyncio → unicex-0.2.0/unicex/_base}/websocket.py +6 -1
- {unicex-0.1.16/unicex/binance/asyncio → unicex-0.2.0/unicex/binance}/__init__.py +1 -1
- {unicex-0.1.16 → unicex-0.2.0}/unicex/binance/adapter.py +82 -113
- {unicex-0.1.16/unicex/binance/asyncio → unicex-0.2.0/unicex/binance}/client.py +118 -72
- {unicex-0.1.16/unicex/binance/asyncio → unicex-0.2.0/unicex/binance}/uni_client.py +2 -2
- {unicex-0.1.16/unicex/binance/asyncio → unicex-0.2.0/unicex/binance}/uni_websocket_manager.py +3 -3
- {unicex-0.1.16/unicex/binance/asyncio → unicex-0.2.0/unicex/binance}/user_websocket.py +23 -7
- {unicex-0.1.16/unicex/binance/asyncio → unicex-0.2.0/unicex/binance}/websocket_manager.py +238 -53
- {unicex-0.1.16/unicex/bitget/asyncio → unicex-0.2.0/unicex/bitget}/__init__.py +4 -1
- {unicex-0.1.16 → unicex-0.2.0}/unicex/bitget/adapter.py +42 -41
- {unicex-0.1.16/unicex/bitget/asyncio → unicex-0.2.0/unicex/bitget}/client.py +168 -44
- {unicex-0.1.16/unicex/bitget/asyncio → unicex-0.2.0/unicex/bitget}/uni_client.py +2 -2
- {unicex-0.1.16/unicex/bitget/asyncio → unicex-0.2.0/unicex/bitget}/uni_websocket_manager.py +3 -3
- {unicex-0.1.16/unicex/bitget/asyncio → unicex-0.2.0/unicex/bitget}/websocket_manager.py +44 -4
- unicex-0.2.0/unicex/mapper.py +57 -0
- {unicex-0.1.16 → unicex-0.2.0}/unicex/types.py +5 -0
- {unicex-0.1.16 → unicex-0.2.0}/unicex/utils.py +43 -1
- unicex-0.2.0/unicex.egg-info/PKG-INFO +122 -0
- unicex-0.2.0/unicex.egg-info/SOURCES.txt +35 -0
- {unicex-0.1.16 → unicex-0.2.0}/unicex.egg-info/requires.txt +0 -2
- unicex-0.1.16/PKG-INFO +0 -110
- unicex-0.1.16/README.md +0 -63
- unicex-0.1.16/unicex/_abc/asyncio/__init__.py +0 -4
- unicex-0.1.16/unicex/_abc/sync/__init__.py +0 -4
- unicex-0.1.16/unicex/_abc/sync/uni_client.py +0 -275
- unicex-0.1.16/unicex/_abc/sync/uni_websocket_manager.py +0 -294
- unicex-0.1.16/unicex/_base/__init__.py +0 -6
- unicex-0.1.16/unicex/_base/sync/__init__.py +0 -7
- unicex-0.1.16/unicex/_base/sync/client.py +0 -186
- unicex-0.1.16/unicex/_base/sync/websocket.py +0 -239
- unicex-0.1.16/unicex/binance/__init__.py +0 -13
- unicex-0.1.16/unicex/binance/_mixins/__init__.py +0 -7
- unicex-0.1.16/unicex/binance/_mixins/client.py +0 -78
- unicex-0.1.16/unicex/binance/_mixins/user_websocket.py +0 -28
- unicex-0.1.16/unicex/binance/_mixins/websocket_manager.py +0 -45
- unicex-0.1.16/unicex/binance/sync/__init__.py +0 -9
- unicex-0.1.16/unicex/binance/sync/client.py +0 -1558
- unicex-0.1.16/unicex/binance/sync/uni_client.py +0 -176
- unicex-0.1.16/unicex/binance/sync/uni_websocket_manager.py +0 -166
- unicex-0.1.16/unicex/binance/sync/user_websocket.py +0 -166
- unicex-0.1.16/unicex/binance/sync/websocket_manager.py +0 -726
- unicex-0.1.16/unicex/binance/types.py +0 -365
- unicex-0.1.16/unicex/bitget/__init__.py +0 -1
- unicex-0.1.16/unicex/bitget/_mixins/__init__.py +0 -7
- unicex-0.1.16/unicex/bitget/_mixins/client.py +0 -125
- unicex-0.1.16/unicex/bitget/_mixins/user_websocket.py +0 -0
- unicex-0.1.16/unicex/bitget/_mixins/websocket_manager.py +0 -47
- unicex-0.1.16/unicex/bitget/sync/__init__.py +0 -0
- unicex-0.1.16/unicex/bitget/types.py +0 -72
- unicex-0.1.16/unicex/bybit/__init__.py +0 -9
- unicex-0.1.16/unicex/bybit/adapter.py +0 -170
- unicex-0.1.16/unicex/bybit/client.py +0 -1164
- unicex-0.1.16/unicex/bybit/types.py +0 -266
- unicex-0.1.16/unicex/bybit/uni_client.py +0 -166
- unicex-0.1.16/unicex/bybit/websocket.py +0 -7
- unicex-0.1.16/unicex/bybit/websocket_manager.py +0 -27
- unicex-0.1.16/unicex/mapper.py +0 -110
- unicex-0.1.16/unicex.egg-info/PKG-INFO +0 -110
- unicex-0.1.16/unicex.egg-info/SOURCES.txt +0 -69
- {unicex-0.1.16 → unicex-0.2.0}/LICENSE +0 -0
- {unicex-0.1.16 → unicex-0.2.0}/setup.cfg +0 -0
- {unicex-0.1.16/unicex/_base/asyncio → unicex-0.2.0/unicex/_base}/__init__.py +0 -0
- {unicex-0.1.16/unicex/_base/asyncio → unicex-0.2.0/unicex/_base}/client.py +0 -0
- {unicex-0.1.16 → unicex-0.2.0}/unicex/enums.py +0 -0
- {unicex-0.1.16 → unicex-0.2.0}/unicex/exceptions.py +0 -0
- {unicex-0.1.16 → unicex-0.2.0}/unicex/extra.py +0 -0
- {unicex-0.1.16 → unicex-0.2.0}/unicex.egg-info/dependency_links.txt +0 -0
- {unicex-0.1.16 → unicex-0.2.0}/unicex.egg-info/top_level.txt +0 -0
unicex-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: unicex
|
|
3
|
+
Version: 0.2.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: loguru>=0.7.3
|
|
43
|
+
Requires-Dist: orjson>=3.11.3
|
|
44
|
+
Requires-Dist: websockets>=15.0.1
|
|
45
|
+
Dynamic: license-file
|
|
46
|
+
|
|
47
|
+
# Unified Crypto Exchange API
|
|
48
|
+
|
|
49
|
+
`unicex` — асинхронная библиотека для работы с криптовалютными биржами, реализующая унифицированный интерфейс поверх «сырых» REST и WebSocket API разных бирж.
|
|
50
|
+
|
|
51
|
+
## ✅ Статус реализации
|
|
52
|
+
|
|
53
|
+
| Exchange | Client | UniClient | Adapter | WebsocketManager | UniWebsocketManager | UserWebsocket |
|
|
54
|
+
|----------|--------|-----------|---------|------------------|---------------------|---------------|
|
|
55
|
+
| Binance | [x] | [x] | [x] | [x] | [x] | [x] |
|
|
56
|
+
| Bybit | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
|
|
57
|
+
| Bitget | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
|
|
58
|
+
| Okx | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
|
|
59
|
+
| Mexc | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
|
|
60
|
+
| Gate | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 🚀 Быстрый старт
|
|
65
|
+
|
|
66
|
+
- Установка: `pip install unicex` или из исходников: `pip install -e .`
|
|
67
|
+
- Библиотека полностью асинхронная. Примеры импорта:
|
|
68
|
+
- Сырые клиенты: `from unicex.binance import Client`
|
|
69
|
+
- Унифицированные клиенты: `from unicex.binance import UniClient`
|
|
70
|
+
- Менеджеры WS: `from unicex.binance import WebsocketManager, UniWebsocketManager`
|
|
71
|
+
|
|
72
|
+
Пример: получить последние цены через унифицированный клиент Binance
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
import asyncio
|
|
76
|
+
from unicex.binance import UniClient
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
async def main():
|
|
80
|
+
client = await UniClient.create()
|
|
81
|
+
prices = await client.last_price()
|
|
82
|
+
print(prices["BTCUSDT"])
|
|
83
|
+
await client.close()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
asyncio.run(main())
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Пример: подписаться на трейды через унифицированный WS‑менеджер Bitget
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
import asyncio
|
|
94
|
+
from unicex.bitget import UniWebsocketManager
|
|
95
|
+
from unicex import TradeDict
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
async def on_trade(msg: TradeDict):
|
|
99
|
+
print(msg)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
async def main():
|
|
103
|
+
uwm = UniWebsocketManager()
|
|
104
|
+
socket = uwm.trades(callback=on_trade, symbol="BTCUSDT")
|
|
105
|
+
await socket.start()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
asyncio.run(main())
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 🧑💻 Блок для разработчика
|
|
115
|
+
|
|
116
|
+
### 📋 Todo
|
|
117
|
+
- Добавить веса и рейт‑лимиты в документацию клиентов
|
|
118
|
+
- Пересмотреть вопрос: должен ли быть адаптер интерфейсом?
|
|
119
|
+
- Доделать BitgetClient и проверить типы
|
|
120
|
+
- Добавить overload к методам с `None, None`
|
|
121
|
+
- Определить порядок полей, возвращаемых адаптером
|
|
122
|
+
- Написать 1–2 примера
|
unicex-0.2.0/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Unified Crypto Exchange API
|
|
2
|
+
|
|
3
|
+
`unicex` — асинхронная библиотека для работы с криптовалютными биржами, реализующая унифицированный интерфейс поверх «сырых» REST и WebSocket API разных бирж.
|
|
4
|
+
|
|
5
|
+
## ✅ Статус реализации
|
|
6
|
+
|
|
7
|
+
| Exchange | Client | UniClient | Adapter | WebsocketManager | UniWebsocketManager | UserWebsocket |
|
|
8
|
+
|----------|--------|-----------|---------|------------------|---------------------|---------------|
|
|
9
|
+
| Binance | [x] | [x] | [x] | [x] | [x] | [x] |
|
|
10
|
+
| Bybit | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
|
|
11
|
+
| Bitget | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
|
|
12
|
+
| Okx | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
|
|
13
|
+
| Mexc | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
|
|
14
|
+
| Gate | [ ] | [ ] | [ ] | [ ] | [ ] | [ ] |
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🚀 Быстрый старт
|
|
19
|
+
|
|
20
|
+
- Установка: `pip install unicex` или из исходников: `pip install -e .`
|
|
21
|
+
- Библиотека полностью асинхронная. Примеры импорта:
|
|
22
|
+
- Сырые клиенты: `from unicex.binance import Client`
|
|
23
|
+
- Унифицированные клиенты: `from unicex.binance import UniClient`
|
|
24
|
+
- Менеджеры WS: `from unicex.binance import WebsocketManager, UniWebsocketManager`
|
|
25
|
+
|
|
26
|
+
Пример: получить последние цены через унифицированный клиент Binance
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
import asyncio
|
|
30
|
+
from unicex.binance import UniClient
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
async def main():
|
|
34
|
+
client = await UniClient.create()
|
|
35
|
+
prices = await client.last_price()
|
|
36
|
+
print(prices["BTCUSDT"])
|
|
37
|
+
await client.close()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
asyncio.run(main())
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Пример: подписаться на трейды через унифицированный WS‑менеджер Bitget
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
import asyncio
|
|
48
|
+
from unicex.bitget import UniWebsocketManager
|
|
49
|
+
from unicex import TradeDict
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
async def on_trade(msg: TradeDict):
|
|
53
|
+
print(msg)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
async def main():
|
|
57
|
+
uwm = UniWebsocketManager()
|
|
58
|
+
socket = uwm.trades(callback=on_trade, symbol="BTCUSDT")
|
|
59
|
+
await socket.start()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
if __name__ == "__main__":
|
|
63
|
+
asyncio.run(main())
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 🧑💻 Блок для разработчика
|
|
69
|
+
|
|
70
|
+
### 📋 Todo
|
|
71
|
+
- Добавить веса и рейт‑лимиты в документацию клиентов
|
|
72
|
+
- Пересмотреть вопрос: должен ли быть адаптер интерфейсом?
|
|
73
|
+
- Доделать BitgetClient и проверить типы
|
|
74
|
+
- Добавить overload к методам с `None, None`
|
|
75
|
+
- Определить порядок полей, возвращаемых адаптером
|
|
76
|
+
- Написать 1–2 примера
|
|
@@ -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.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
|
|
9
9
|
description = "Unified Crypto Exchange API "
|
|
10
10
|
readme = "README.md"
|
|
@@ -17,18 +17,19 @@ dependencies = [
|
|
|
17
17
|
"aiohttp>=3.12.15",
|
|
18
18
|
"loguru>=0.7.3",
|
|
19
19
|
"orjson>=3.11.3",
|
|
20
|
-
"requests>=2.32.5",
|
|
21
|
-
"websocket-client>=1.8.0",
|
|
22
20
|
"websockets>=15.0.1",
|
|
23
21
|
]
|
|
24
22
|
|
|
25
23
|
[project.urls]
|
|
26
24
|
Github = "https://github.com/LoveBloodAndDiamonds/uni-cex-api"
|
|
27
25
|
Author = "https://t.me/LoveBloodAndDiamonds"
|
|
26
|
+
Readthedocs = "https://unicex.readthedocs.io/ru/latest/"
|
|
28
27
|
|
|
29
28
|
[dependency-groups]
|
|
30
29
|
dev = [
|
|
31
30
|
"build>=1.3.0",
|
|
32
31
|
"pre-commit>=4.3.0",
|
|
32
|
+
"sphinx>=8.2.3",
|
|
33
|
+
"sphinx-rtd-theme>=3.0.2",
|
|
33
34
|
"twine>=6.2.0",
|
|
34
35
|
]
|
|
@@ -20,21 +20,31 @@ __all__ = [
|
|
|
20
20
|
"IUniClient",
|
|
21
21
|
"IUniWebsocketManager",
|
|
22
22
|
"IAdapter",
|
|
23
|
-
"IUniAsyncClient",
|
|
24
|
-
"IUniAsyncWebsocketManager",
|
|
25
23
|
# Base clients and websockets
|
|
26
24
|
"Websocket",
|
|
27
25
|
"BaseClient",
|
|
28
|
-
|
|
29
|
-
"
|
|
26
|
+
# Binance
|
|
27
|
+
"BinanceClient",
|
|
28
|
+
"BinanceUniClient",
|
|
29
|
+
"BinanceWebsocketManager",
|
|
30
|
+
"BinanceUniWebsocketManager",
|
|
31
|
+
# Bitget
|
|
32
|
+
"BitgetClient",
|
|
33
|
+
"BitgetUniClient",
|
|
34
|
+
"BitgetUniWebsocketManager",
|
|
35
|
+
"BitgetWebsocketManager",
|
|
30
36
|
]
|
|
31
37
|
|
|
32
38
|
from ._abc import IAdapter, IUniClient, IUniWebsocketManager
|
|
33
|
-
from ._abc.asyncio import IUniClient as IUniAsyncClient
|
|
34
|
-
from ._abc.asyncio import IUniWebsocketManager as IUniAsyncWebsocketManager
|
|
35
39
|
from ._base import BaseClient, Websocket
|
|
36
|
-
from .
|
|
37
|
-
from .
|
|
40
|
+
from .binance import Client as BinanceClient
|
|
41
|
+
from .binance import UniClient as BinanceUniClient
|
|
42
|
+
from .binance import UniWebsocketManager as BinanceUniWebsocketManager
|
|
43
|
+
from .binance import WebsocketManager as BinanceWebsocketManager
|
|
44
|
+
from .bitget import Client as BitgetClient
|
|
45
|
+
from .bitget import UniClient as BitgetUniClient
|
|
46
|
+
from .bitget import UniWebsocketManager as BitgetUniWebsocketManager
|
|
47
|
+
from .bitget import WebsocketManager as BitgetWebsocketManager
|
|
38
48
|
from .enums import Exchange, MarketType, Side, Timeframe
|
|
39
49
|
from .mapper import get_uni_client, get_uni_websocket_manager
|
|
40
50
|
from .types import AggTradeDict, KlineDict, LoggerLike, RequestMethod, TickerDailyDict, TradeDict
|
|
@@ -6,12 +6,12 @@ from typing import Generic, Self, TypeVar, overload
|
|
|
6
6
|
|
|
7
7
|
import aiohttp
|
|
8
8
|
|
|
9
|
-
from unicex._base
|
|
9
|
+
from unicex._base import BaseClient
|
|
10
10
|
from unicex.enums import Timeframe
|
|
11
11
|
from unicex.types import KlineDict, LoggerLike, TickerDailyDict
|
|
12
12
|
from unicex.utils import batched_list
|
|
13
13
|
|
|
14
|
-
from
|
|
14
|
+
from .adapter import IAdapter
|
|
15
15
|
|
|
16
16
|
TClient = TypeVar("TClient", bound="BaseClient")
|
|
17
17
|
|
|
@@ -183,7 +183,12 @@ class Websocket:
|
|
|
183
183
|
break
|
|
184
184
|
except Exception as e:
|
|
185
185
|
self._logger.error(f"{self} Error({type(e)}) while processing message: {e}")
|
|
186
|
-
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
self._queue.task_done()
|
|
189
|
+
except Exception as e:
|
|
190
|
+
if self._running:
|
|
191
|
+
self._logger.error(f"{self} Error({type(e)}) while marking task done: {e}")
|
|
187
192
|
|
|
188
193
|
def _generate_ws_kwargs(self) -> dict:
|
|
189
194
|
"""Генерирует аргументы для запуска вебсокета."""
|
|
@@ -2,14 +2,15 @@ __all__ = ["Adapter"]
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
from unicex._abc import IAdapter
|
|
5
|
-
from unicex.exceptions import AdapterError
|
|
6
5
|
from unicex.types import AggTradeDict, KlineDict, TickerDailyDict, TradeDict
|
|
6
|
+
from unicex.utils import catch_adapter_errors
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class Adapter(IAdapter):
|
|
10
10
|
"""Адаптер для унификации данных с Binance API."""
|
|
11
11
|
|
|
12
12
|
@staticmethod
|
|
13
|
+
@catch_adapter_errors
|
|
13
14
|
def tickers(raw_data: list[dict], only_usdt: bool = True) -> list[str]:
|
|
14
15
|
"""Преобразует сырой ответ, в котором содержатся данные о тикерах в список тикеров.
|
|
15
16
|
|
|
@@ -20,18 +21,12 @@ class Adapter(IAdapter):
|
|
|
20
21
|
Возвращает:
|
|
21
22
|
list[str]: Список тикеров.
|
|
22
23
|
"""
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
for item in raw_data
|
|
27
|
-
if not only_usdt or item["symbol"].endswith("USDT")
|
|
28
|
-
]
|
|
29
|
-
except Exception as e:
|
|
30
|
-
raise AdapterError(
|
|
31
|
-
f"({type(e)}): {e}. Can not convert {raw_data} to unified tickers."
|
|
32
|
-
) from e
|
|
24
|
+
return [
|
|
25
|
+
item["symbol"] for item in raw_data if not only_usdt or item["symbol"].endswith("USDT")
|
|
26
|
+
]
|
|
33
27
|
|
|
34
28
|
@staticmethod
|
|
29
|
+
@catch_adapter_errors
|
|
35
30
|
def futures_tickers(raw_data: list[dict], only_usdt: bool = True) -> list[str]:
|
|
36
31
|
"""Преобразует сырой ответ, в котором содержатся данные о тикерах в список тикеров.
|
|
37
32
|
|
|
@@ -45,6 +40,7 @@ class Adapter(IAdapter):
|
|
|
45
40
|
return Adapter.tickers(raw_data, only_usdt)
|
|
46
41
|
|
|
47
42
|
@staticmethod
|
|
43
|
+
@catch_adapter_errors
|
|
48
44
|
def ticker_24h(raw_data: list[dict]) -> dict[str, TickerDailyDict]:
|
|
49
45
|
"""Преобразует сырой ответ, в котором содержатся данные о тикере за последние 24 часа в унифицированный формат.
|
|
50
46
|
|
|
@@ -54,21 +50,17 @@ class Adapter(IAdapter):
|
|
|
54
50
|
Возвращает:
|
|
55
51
|
dict[str, TickerDailyDict]: Словарь, где ключ - тикер, а значение - статистика за последние 24 часа.
|
|
56
52
|
"""
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
item["
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
except Exception as e:
|
|
67
|
-
raise AdapterError(
|
|
68
|
-
f"({type(e)}): {e}. Can not convert {raw_data} to unified ticker 24h."
|
|
69
|
-
) from e
|
|
53
|
+
return {
|
|
54
|
+
item["symbol"]: TickerDailyDict(
|
|
55
|
+
p=float(item["priceChangePercent"]),
|
|
56
|
+
q=float(item["quoteVolume"]), # объём в долларах
|
|
57
|
+
v=float(item["volume"]), # объём в монетах
|
|
58
|
+
)
|
|
59
|
+
for item in raw_data
|
|
60
|
+
}
|
|
70
61
|
|
|
71
62
|
@staticmethod
|
|
63
|
+
@catch_adapter_errors
|
|
72
64
|
def futures_ticker_24h(raw_data: list[dict]) -> dict[str, TickerDailyDict]:
|
|
73
65
|
"""Преобразует сырой ответ, в котором содержатся данные о тикере за последние 24 часа в унифицированный формат.
|
|
74
66
|
|
|
@@ -81,6 +73,7 @@ class Adapter(IAdapter):
|
|
|
81
73
|
return Adapter.ticker_24h(raw_data)
|
|
82
74
|
|
|
83
75
|
@staticmethod
|
|
76
|
+
@catch_adapter_errors
|
|
84
77
|
def last_price(raw_data: list[dict]) -> dict[str, float]:
|
|
85
78
|
"""Преобразует сырой ответ, в котором содержатся данные о тикере за последние 24 часа в унифицированный формат.
|
|
86
79
|
|
|
@@ -90,14 +83,10 @@ class Adapter(IAdapter):
|
|
|
90
83
|
Возвращает:
|
|
91
84
|
dict[str, float]: Словарь, где ключ - тикер, а значение - последняя цена.
|
|
92
85
|
"""
|
|
93
|
-
|
|
94
|
-
return {item["symbol"]: float(item["price"]) for item in raw_data}
|
|
95
|
-
except Exception as e:
|
|
96
|
-
raise AdapterError(
|
|
97
|
-
f"({type(e)}): {e}. Can not convert {raw_data} to unified last price."
|
|
98
|
-
) from e
|
|
86
|
+
return {item["symbol"]: float(item["price"]) for item in raw_data}
|
|
99
87
|
|
|
100
88
|
@staticmethod
|
|
89
|
+
@catch_adapter_errors
|
|
101
90
|
def futures_last_price(raw_data: list[dict]) -> dict[str, float]:
|
|
102
91
|
"""Преобразует сырой ответ, в котором содержатся данные о тикере за последние 24 часа в унифицированный формат.
|
|
103
92
|
|
|
@@ -110,6 +99,7 @@ class Adapter(IAdapter):
|
|
|
110
99
|
return Adapter.last_price(raw_data)
|
|
111
100
|
|
|
112
101
|
@staticmethod
|
|
102
|
+
@catch_adapter_errors
|
|
113
103
|
def klines(raw_data: list[list]) -> list[KlineDict]:
|
|
114
104
|
"""Преобразует сырой ответ, в котором содержатся данные о котировках тикеров в унифицированный формат.
|
|
115
105
|
|
|
@@ -119,28 +109,24 @@ class Adapter(IAdapter):
|
|
|
119
109
|
Возвращает:
|
|
120
110
|
list[KlineDict]: Список словарей, где каждый словарь содержит данные о свече.
|
|
121
111
|
"""
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
]
|
|
138
|
-
except Exception as e:
|
|
139
|
-
raise AdapterError(
|
|
140
|
-
f"({type(e)}): {e}. Can not convert {raw_data} to unified kline."
|
|
141
|
-
) from e
|
|
112
|
+
return [
|
|
113
|
+
KlineDict(
|
|
114
|
+
s="",
|
|
115
|
+
t=item[0],
|
|
116
|
+
o=float(item[1]),
|
|
117
|
+
h=float(item[2]),
|
|
118
|
+
l=float(item[3]),
|
|
119
|
+
c=float(item[4]),
|
|
120
|
+
v=float(item[5]),
|
|
121
|
+
q=float(item[7]),
|
|
122
|
+
T=item[6],
|
|
123
|
+
x=None,
|
|
124
|
+
)
|
|
125
|
+
for item in raw_data
|
|
126
|
+
]
|
|
142
127
|
|
|
143
128
|
@staticmethod
|
|
129
|
+
@catch_adapter_errors
|
|
144
130
|
def futures_klines(raw_data: list[list]) -> list[KlineDict]:
|
|
145
131
|
"""Преобразует сырой ответ, в котором содержатся данные о котировках тикеров в унифицированный формат.
|
|
146
132
|
|
|
@@ -153,6 +139,7 @@ class Adapter(IAdapter):
|
|
|
153
139
|
return Adapter.klines(raw_data)
|
|
154
140
|
|
|
155
141
|
@staticmethod
|
|
142
|
+
@catch_adapter_errors
|
|
156
143
|
def funding_rate(raw_data: list[dict]) -> dict[str, float]:
|
|
157
144
|
"""Преобразует сырой ответ, в котором содержатся данные о ставках финансирования тикеров в унифицированный формат.
|
|
158
145
|
|
|
@@ -162,14 +149,10 @@ class Adapter(IAdapter):
|
|
|
162
149
|
Возвращает:
|
|
163
150
|
dict[str, float]: Словарь, где ключ - тикер, а значение - ставка финансирования.
|
|
164
151
|
"""
|
|
165
|
-
|
|
166
|
-
return {item["symbol"]: float(item["lastFundingRate"]) * 100 for item in raw_data}
|
|
167
|
-
except Exception as e:
|
|
168
|
-
raise AdapterError(
|
|
169
|
-
f"({type(e)}): {e}. Can not convert {raw_data} to unified funding rate."
|
|
170
|
-
) from e
|
|
152
|
+
return {item["symbol"]: float(item["lastFundingRate"]) * 100 for item in raw_data}
|
|
171
153
|
|
|
172
154
|
@staticmethod
|
|
155
|
+
@catch_adapter_errors
|
|
173
156
|
def klines_message(raw_msg: dict) -> list[KlineDict]:
|
|
174
157
|
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
175
158
|
свече/свечах в унифицированный вид.
|
|
@@ -180,29 +163,25 @@ class Adapter(IAdapter):
|
|
|
180
163
|
Возвращает:
|
|
181
164
|
list[KlineDict]: Список словарей, где каждый словарь содержит данные о свече.
|
|
182
165
|
"""
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
]
|
|
200
|
-
except Exception as e:
|
|
201
|
-
raise AdapterError(
|
|
202
|
-
f"({type(e)}): {e}. Can not convert {raw_msg} to unified kline."
|
|
203
|
-
) from e
|
|
166
|
+
# Обрабатываем обертку в случае с multiplex stream
|
|
167
|
+
kline = raw_msg.get("data", raw_msg)["k"]
|
|
168
|
+
return [
|
|
169
|
+
KlineDict(
|
|
170
|
+
s=kline["s"],
|
|
171
|
+
t=kline["t"],
|
|
172
|
+
o=float(kline["o"]),
|
|
173
|
+
h=float(kline["h"]),
|
|
174
|
+
l=float(kline["l"]),
|
|
175
|
+
c=float(kline["c"]),
|
|
176
|
+
v=float(kline["v"]), # Используем quote volume (в USDT)
|
|
177
|
+
q=float(kline["q"]), # Используем quote volume (в USDT)
|
|
178
|
+
T=kline["T"],
|
|
179
|
+
x=kline["x"],
|
|
180
|
+
)
|
|
181
|
+
]
|
|
204
182
|
|
|
205
183
|
@staticmethod
|
|
184
|
+
@catch_adapter_errors
|
|
206
185
|
def futures_klines_message(raw_msg: dict) -> list[KlineDict]:
|
|
207
186
|
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
208
187
|
свече/свечах в унифицированный вид.
|
|
@@ -216,6 +195,7 @@ class Adapter(IAdapter):
|
|
|
216
195
|
return Adapter.klines_message(raw_msg)
|
|
217
196
|
|
|
218
197
|
@staticmethod
|
|
198
|
+
@catch_adapter_errors
|
|
219
199
|
def aggtrades_message(raw_msg: dict) -> list[AggTradeDict]:
|
|
220
200
|
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
221
201
|
аггрегированных сделке/сделках в унифицированный вид.
|
|
@@ -226,23 +206,19 @@ class Adapter(IAdapter):
|
|
|
226
206
|
Возвращает:
|
|
227
207
|
list[KlineDict]: Список словарей, где каждый словарь содержит данные о сделке.
|
|
228
208
|
"""
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
]
|
|
240
|
-
except Exception as e:
|
|
241
|
-
raise AdapterError(
|
|
242
|
-
f"({type(e)}): {e}. Can not convert {raw_msg} to unified aggtrade."
|
|
243
|
-
) from e
|
|
209
|
+
msg = raw_msg.get("data", raw_msg)
|
|
210
|
+
return [
|
|
211
|
+
AggTradeDict(
|
|
212
|
+
t=int(msg["T"]),
|
|
213
|
+
s=str(msg["s"]),
|
|
214
|
+
S="SELL" if bool(msg["m"]) else "BUY",
|
|
215
|
+
p=float(msg["p"]),
|
|
216
|
+
v=float(msg["q"]),
|
|
217
|
+
)
|
|
218
|
+
]
|
|
244
219
|
|
|
245
220
|
@staticmethod
|
|
221
|
+
@catch_adapter_errors
|
|
246
222
|
def futures_aggtrades_message(raw_msg: dict) -> list[AggTradeDict]:
|
|
247
223
|
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
248
224
|
аггрегированных сделке/сделках в унифицированный вид.
|
|
@@ -256,6 +232,7 @@ class Adapter(IAdapter):
|
|
|
256
232
|
return Adapter.aggtrades_message(raw_msg)
|
|
257
233
|
|
|
258
234
|
@staticmethod
|
|
235
|
+
@catch_adapter_errors
|
|
259
236
|
def trades_message(raw_msg: dict) -> list[TradeDict]:
|
|
260
237
|
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
261
238
|
сделке/сделках в унифицированный вид.
|
|
@@ -266,23 +243,19 @@ class Adapter(IAdapter):
|
|
|
266
243
|
Возвращает:
|
|
267
244
|
list[KlineDict]: Список словарей, где каждый словарь содержит данные о сделке.
|
|
268
245
|
"""
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
]
|
|
280
|
-
except Exception as e:
|
|
281
|
-
raise AdapterError(
|
|
282
|
-
f"({type(e)}): {e}. Can not convert {raw_msg} to unified trade."
|
|
283
|
-
) from e
|
|
246
|
+
msg = raw_msg.get("data", raw_msg)
|
|
247
|
+
return [
|
|
248
|
+
TradeDict(
|
|
249
|
+
t=int(msg["T"]),
|
|
250
|
+
s=str(msg["s"]),
|
|
251
|
+
S="SELL" if bool(msg["m"]) else "BUY",
|
|
252
|
+
p=float(msg["p"]),
|
|
253
|
+
v=float(msg["q"]),
|
|
254
|
+
)
|
|
255
|
+
]
|
|
284
256
|
|
|
285
257
|
@staticmethod
|
|
258
|
+
@catch_adapter_errors
|
|
286
259
|
def futures_trades_message(raw_msg: dict) -> list[TradeDict]:
|
|
287
260
|
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
288
261
|
сделке/сделках в унифицированный вид.
|
|
@@ -296,6 +269,7 @@ class Adapter(IAdapter):
|
|
|
296
269
|
return Adapter.trades_message(raw_msg)
|
|
297
270
|
|
|
298
271
|
@staticmethod
|
|
272
|
+
@catch_adapter_errors
|
|
299
273
|
def open_interest(raw_data: dict) -> float:
|
|
300
274
|
"""Преобразует сырое сообщение с вебсокета, в котором содержится информация о
|
|
301
275
|
объеме открытых позиций в унифицированный вид.
|
|
@@ -306,9 +280,4 @@ class Adapter(IAdapter):
|
|
|
306
280
|
Возвращает:
|
|
307
281
|
float: Объем открытых позиций в монетах.
|
|
308
282
|
"""
|
|
309
|
-
|
|
310
|
-
return float(raw_data["openInterest"])
|
|
311
|
-
except Exception as e:
|
|
312
|
-
raise AdapterError(
|
|
313
|
-
f"{type(e)}: {e}. Can not convert {raw_data} to unified open interest."
|
|
314
|
-
) from e
|
|
283
|
+
return float(raw_data["openInterest"])
|