unicex 0.13.18__py3-none-any.whl → 0.14.1__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 +18 -0
- unicex/enums.py +1 -3
- unicex/extra.py +12 -17
- unicex/kucoin/__init__.py +27 -0
- unicex/kucoin/adapter.py +81 -0
- unicex/kucoin/client.py +38 -0
- unicex/kucoin/exchange_info.py +22 -0
- unicex/kucoin/uni_client.py +160 -0
- unicex/kucoin/uni_websocket_manager.py +269 -0
- unicex/kucoin/user_websocket.py +7 -0
- unicex/kucoin/websocket_manager.py +11 -0
- unicex/mapper.py +6 -0
- {unicex-0.13.18.dist-info → unicex-0.14.1.dist-info}/METADATA +1 -1
- {unicex-0.13.18.dist-info → unicex-0.14.1.dist-info}/RECORD +17 -9
- {unicex-0.13.18.dist-info → unicex-0.14.1.dist-info}/WHEEL +0 -0
- {unicex-0.13.18.dist-info → unicex-0.14.1.dist-info}/licenses/LICENSE +0 -0
- {unicex-0.13.18.dist-info → unicex-0.14.1.dist-info}/top_level.txt +0 -0
unicex/__init__.py
CHANGED
|
@@ -81,6 +81,13 @@ __all__ = [
|
|
|
81
81
|
"GateioWebsocketManager",
|
|
82
82
|
"GateioUserWebsocket",
|
|
83
83
|
"GateioExchangeInfo",
|
|
84
|
+
# Kucoin
|
|
85
|
+
"KucoinClient",
|
|
86
|
+
"KucoinUniClient",
|
|
87
|
+
"KucoinUniWebsocketManager",
|
|
88
|
+
"KucoinWebsocketManager",
|
|
89
|
+
"KucoinUserWebsocket",
|
|
90
|
+
"KucoinExchangeInfo",
|
|
84
91
|
]
|
|
85
92
|
|
|
86
93
|
# ruff: noqa
|
|
@@ -173,6 +180,15 @@ from .okx import (
|
|
|
173
180
|
ExchangeInfo as OkxExchangeInfo,
|
|
174
181
|
)
|
|
175
182
|
|
|
183
|
+
from .kucoin import (
|
|
184
|
+
Client as KucoinClient,
|
|
185
|
+
UniClient as KucoinUniClient,
|
|
186
|
+
UniWebsocketManager as KucoinUniWebsocketManager,
|
|
187
|
+
UserWebsocket as KucoinUserWebsocket,
|
|
188
|
+
WebsocketManager as KucoinWebsocketManager,
|
|
189
|
+
ExchangeInfo as KucoinExchangeInfo,
|
|
190
|
+
)
|
|
191
|
+
|
|
176
192
|
|
|
177
193
|
async def load_exchanges_info() -> None:
|
|
178
194
|
"""Единожды загружает информацию о тикерах на всех биржах."""
|
|
@@ -184,6 +200,7 @@ async def load_exchanges_info() -> None:
|
|
|
184
200
|
HyperliquidExchangeInfo.load_exchange_info(),
|
|
185
201
|
MexcExchangeInfo.load_exchange_info(),
|
|
186
202
|
OkxExchangeInfo.load_exchange_info(),
|
|
203
|
+
KucoinExchangeInfo.load_exchange_info(),
|
|
187
204
|
)
|
|
188
205
|
|
|
189
206
|
|
|
@@ -197,4 +214,5 @@ async def start_exchanges_info(parse_interval_seconds: int = 60 * 60) -> None:
|
|
|
197
214
|
HyperliquidExchangeInfo.start(parse_interval_seconds),
|
|
198
215
|
MexcExchangeInfo.start(parse_interval_seconds),
|
|
199
216
|
OkxExchangeInfo.start(parse_interval_seconds),
|
|
217
|
+
KucoinExchangeInfo.start(parse_interval_seconds),
|
|
200
218
|
)
|
unicex/enums.py
CHANGED
|
@@ -26,14 +26,12 @@ class Exchange(StrEnum):
|
|
|
26
26
|
|
|
27
27
|
BINANCE = "BINANCE"
|
|
28
28
|
BITGET = "BITGET"
|
|
29
|
-
BITUNIX = "BITUNIX"
|
|
30
29
|
BYBIT = "BYBIT"
|
|
31
30
|
GATE = "GATE"
|
|
32
31
|
HYPERLIQUID = "HYPERLIQUID"
|
|
33
|
-
KCEX = "KCEX"
|
|
34
32
|
MEXC = "MEXC"
|
|
35
33
|
OKX = "OKX"
|
|
36
|
-
|
|
34
|
+
KUCOIN = "KUCOIN"
|
|
37
35
|
|
|
38
36
|
def __add__(self, market_type: "MarketType") -> tuple["Exchange", "MarketType"]:
|
|
39
37
|
"""Возвращает кортеж из биржи и типа рынка."""
|
unicex/extra.py
CHANGED
|
@@ -125,6 +125,10 @@ def normalize_ticker(raw_ticker: str) -> str:
|
|
|
125
125
|
.removesuffix("SWAP")
|
|
126
126
|
)
|
|
127
127
|
|
|
128
|
+
# Меняем постфикс USDTM на USDT
|
|
129
|
+
if ticker.endswith("USDTM"):
|
|
130
|
+
ticker = ticker.removesuffix("USDTM") + "USDT"
|
|
131
|
+
|
|
128
132
|
# Удаляем разделители
|
|
129
133
|
ticker = ticker.translate(str.maketrans("", "", "-_."))
|
|
130
134
|
|
|
@@ -149,6 +153,7 @@ def normalize_symbol(raw_ticker: str, quote: Literal["USDT", "USDC"] = "USDT") -
|
|
|
149
153
|
normalize_symbol("BTC") # "BTCUSDT"
|
|
150
154
|
normalize_symbol("btc_usdt_swap") # "BTCUSDT"
|
|
151
155
|
normalize_symbol("ETH", "USDC") # "ETHUSDC"
|
|
156
|
+
normalize_symbol("BTCUSDTM") # "BTCUSDT"
|
|
152
157
|
```
|
|
153
158
|
|
|
154
159
|
Параметры:
|
|
@@ -206,26 +211,16 @@ def generate_ex_link(exchange: Exchange, market_type: MarketType, symbol: str):
|
|
|
206
211
|
return f"https://www.gate.com/ru/futures/USDT/{ticker}_USDT"
|
|
207
212
|
else:
|
|
208
213
|
return f"https://www.gate.com/ru/trade/{ticker}_USDT"
|
|
209
|
-
elif exchange == Exchange.XT:
|
|
210
|
-
if market_type == MarketType.FUTURES:
|
|
211
|
-
return f"https://www.xt.com/ru/futures/trade/{ticker.lower()}_usdt"
|
|
212
|
-
else:
|
|
213
|
-
return f"https://www.xt.com/ru/trade/{ticker.lower()}_usdt"
|
|
214
|
-
elif exchange == Exchange.BITUNIX:
|
|
215
|
-
if market_type == MarketType.FUTURES:
|
|
216
|
-
return f"https://www.bitunix.com/ru-ru/contract-trade/{ticker.upper()}USDT"
|
|
217
|
-
else:
|
|
218
|
-
return f"https://www.bitunix.com/ru-ru/spot-trade/{ticker.upper()}USDT"
|
|
219
|
-
elif exchange == Exchange.KCEX:
|
|
220
|
-
if market_type == MarketType.FUTURES:
|
|
221
|
-
return f"https://www.kcex.com/ru-RU/futures/exchange/{ticker.upper()}_USDT"
|
|
222
|
-
else:
|
|
223
|
-
return f"https://www.kcex.com/ru-RU/exchange/{ticker.upper()}_USDT"
|
|
224
214
|
elif exchange == Exchange.HYPERLIQUID:
|
|
225
215
|
if market_type == MarketType.FUTURES:
|
|
226
216
|
return f"https://app.hyperliquid.xyz/trade/{ticker}"
|
|
227
217
|
else:
|
|
228
218
|
return f"https://app.hyperliquid.xyz/trade/{ticker}/USDC"
|
|
219
|
+
elif exchange == Exchange.KUCOIN:
|
|
220
|
+
if market_type == MarketType.FUTURES:
|
|
221
|
+
return f"https://www.kucoin.com/trade/futures/{symbol}M"
|
|
222
|
+
else:
|
|
223
|
+
return f"https://www.kucoin.com/trade/{ticker}-USDT"
|
|
229
224
|
else:
|
|
230
225
|
raise NotSupported(f"Exchange {exchange} is not supported")
|
|
231
226
|
|
|
@@ -274,10 +269,10 @@ def generate_cg_link(exchange: Exchange, market_type: MarketType, symbol: str) -
|
|
|
274
269
|
return f"{base_url}/Bitget_{symbol}_UMCBL"
|
|
275
270
|
case Exchange.GATE:
|
|
276
271
|
return f"{base_url}/Gate_{symbol.replace('USDT', '_USDT')}"
|
|
277
|
-
case Exchange.BITUNIX:
|
|
278
|
-
return f"{base_url}/Bitunix_{symbol}"
|
|
279
272
|
case Exchange.HYPERLIQUID:
|
|
280
273
|
return f"{base_url}/Hyperliquid_{symbol.replace('USDT', '-USD')}"
|
|
274
|
+
case Exchange.KUCOIN:
|
|
275
|
+
return f"https://www.coinglass.com/tv/ru/KuCoin_{symbol}M"
|
|
281
276
|
case _:
|
|
282
277
|
return f"{base_url}/{exchange.capitalize()}_{symbol}"
|
|
283
278
|
else:
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Пакет, содержащий реализации клиентов и менеджеров для работы с биржей Kucoin."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"Client",
|
|
5
|
+
"UniClient",
|
|
6
|
+
"UserWebsocket",
|
|
7
|
+
"WebsocketManager",
|
|
8
|
+
"UniWebsocketManager",
|
|
9
|
+
"ExchangeInfo",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
from .client import Client
|
|
13
|
+
from .exchange_info import ExchangeInfo
|
|
14
|
+
from .uni_client import UniClient
|
|
15
|
+
from .uni_websocket_manager import UniWebsocketManager
|
|
16
|
+
from .user_websocket import UserWebsocket
|
|
17
|
+
from .websocket_manager import WebsocketManager
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async def load_exchange_info() -> None:
|
|
21
|
+
"""Загружает информацию о бирже Kucoin."""
|
|
22
|
+
await ExchangeInfo.load_exchange_info()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def start_exchange_info(parse_interval_seconds: int = 60 * 60) -> None:
|
|
26
|
+
"""Запускает процесс обновления информации о бирже Kucoin."""
|
|
27
|
+
await ExchangeInfo.start(parse_interval_seconds)
|
unicex/kucoin/adapter.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
__all__ = ["Adapter"]
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from unicex.types import OpenInterestDict, OpenInterestItem, TickerDailyDict, TickerDailyItem
|
|
6
|
+
from unicex.utils import catch_adapter_errors, decorate_all_methods
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@decorate_all_methods(catch_adapter_errors)
|
|
10
|
+
class Adapter:
|
|
11
|
+
"""Адаптер для унификации данных с Kucoin API."""
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def tickers(raw_data: dict, only_usdt: bool) -> list[str]:
|
|
15
|
+
"""Преобразует сырой ответ, в котором содержатся данные о тикерах в список тикеров.
|
|
16
|
+
|
|
17
|
+
Параметры:
|
|
18
|
+
raw_data (dict): Сырой ответ с биржи.
|
|
19
|
+
only_usdt (bool): Флаг, указывающий, нужно ли включать только тикеры в паре к USDT.
|
|
20
|
+
|
|
21
|
+
Возвращает:
|
|
22
|
+
list[str]: Список тикеров.
|
|
23
|
+
"""
|
|
24
|
+
return [
|
|
25
|
+
item["symbol"]
|
|
26
|
+
for item in raw_data["data"]["list"]
|
|
27
|
+
if item["symbol"].endswith("USDTM") or not only_usdt
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def ticker_24hr(raw_data: dict) -> TickerDailyDict:
|
|
32
|
+
"""Преобразует сырой ответ, в котором содержатся данные о тикере за последние 24 часа в унифицированный формат.
|
|
33
|
+
|
|
34
|
+
Параметры:
|
|
35
|
+
raw_data (dict): Сырой ответ с биржи.
|
|
36
|
+
|
|
37
|
+
Возвращает:
|
|
38
|
+
TickerDailyDict: Словарь, где ключ - тикер, а значение - статистика за последние 24 часа.
|
|
39
|
+
"""
|
|
40
|
+
return {
|
|
41
|
+
item["symbol"]: TickerDailyItem(
|
|
42
|
+
p=(
|
|
43
|
+
round((float(item["lastPrice"]) / float(item["open"]) - 1) * 100, 2)
|
|
44
|
+
if float(item["open"]) != 0
|
|
45
|
+
else 0.0
|
|
46
|
+
),
|
|
47
|
+
v=float(item["baseVolume"]),
|
|
48
|
+
q=float(item["quoteVolume"]),
|
|
49
|
+
)
|
|
50
|
+
for item in raw_data["data"]["list"]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def last_price(raw_data: dict) -> dict[str, float]:
|
|
55
|
+
"""Преобразует сырой ответ, в котором содержатся данные о последней цене, в унифицированный формат.
|
|
56
|
+
|
|
57
|
+
Параметры:
|
|
58
|
+
raw_data (dict): Сырой ответ с биржи.
|
|
59
|
+
|
|
60
|
+
Возвращает:
|
|
61
|
+
dict[str, float]: Словарь, где ключ - тикер, а значение - последняя цена.
|
|
62
|
+
"""
|
|
63
|
+
return {item["symbol"]: float(item["lastPrice"]) for item in raw_data["data"]["list"]}
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def open_interest(raw_data: dict[str, Any]) -> OpenInterestDict:
|
|
67
|
+
"""Преобразует сырой ответ, в котором содержатся данные об открытом интересе, в унифицированный формат.
|
|
68
|
+
|
|
69
|
+
Параметры:
|
|
70
|
+
raw_data (dict): Сырой ответ с биржи.
|
|
71
|
+
|
|
72
|
+
Возвращает:
|
|
73
|
+
OpenInterestDict: Словарь, где ключ - тикер, а значение - агрегированные данные открытого интереса.
|
|
74
|
+
"""
|
|
75
|
+
return {
|
|
76
|
+
item["symbol"]: OpenInterestItem(
|
|
77
|
+
t=item["ts"],
|
|
78
|
+
v=float(item["openInterest"]),
|
|
79
|
+
)
|
|
80
|
+
for item in raw_data["data"]
|
|
81
|
+
}
|
unicex/kucoin/client.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
__all__ = ["Client"]
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from typing import Any, Literal
|
|
5
|
+
|
|
6
|
+
from unicex._base import BaseClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Client(BaseClient):
|
|
10
|
+
"""Клиент для работы с Kucoin API."""
|
|
11
|
+
|
|
12
|
+
_BASE_URL: str = "https://api.kucoin.com"
|
|
13
|
+
"""Базовый URL для запросов."""
|
|
14
|
+
|
|
15
|
+
async def open_interest(self) -> dict[str, Any]:
|
|
16
|
+
"""Получение открытого интереса.
|
|
17
|
+
|
|
18
|
+
https://www.kucoin.com/docs-new/3476287e0
|
|
19
|
+
"""
|
|
20
|
+
url = self._BASE_URL + "/api/ua/v1/market/open-interest"
|
|
21
|
+
|
|
22
|
+
return await self._make_request("GET", url)
|
|
23
|
+
|
|
24
|
+
async def ticker(
|
|
25
|
+
self,
|
|
26
|
+
trade_type: Literal["SPOT", "FUTURES"],
|
|
27
|
+
symbol: str | None = None,
|
|
28
|
+
) -> dict[str, Any]:
|
|
29
|
+
"""Получение тикеров и информации о них.
|
|
30
|
+
|
|
31
|
+
https://www.kucoin.com/docs-new/rest/ua/get-ticker
|
|
32
|
+
"""
|
|
33
|
+
url = self._BASE_URL + "/api/ua/v1/market/ticker"
|
|
34
|
+
params = {"tradeType": trade_type}
|
|
35
|
+
if symbol:
|
|
36
|
+
params["symbol"] = symbol
|
|
37
|
+
|
|
38
|
+
return await self._make_request("GET", url, params=params)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
__all__ = ["ExchangeInfo"]
|
|
2
|
+
|
|
3
|
+
import aiohttp
|
|
4
|
+
|
|
5
|
+
from unicex._abc import IExchangeInfo
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ExchangeInfo(IExchangeInfo):
|
|
9
|
+
"""Предзагружает информацию о тикерах для биржи Kucoin."""
|
|
10
|
+
|
|
11
|
+
exchange_name = "Kucoin"
|
|
12
|
+
"""Название биржи, на которой работает класс."""
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
async def _load_spot_exchange_info(cls, session: aiohttp.ClientSession) -> None:
|
|
16
|
+
"""Загружает информацию о бирже для спотового рынка."""
|
|
17
|
+
...
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
async def _load_futures_exchange_info(cls, session: aiohttp.ClientSession) -> None:
|
|
21
|
+
"""Загружает информацию о бирже для фьючерсного рынка."""
|
|
22
|
+
...
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
__all__ = ["UniClient"]
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from typing import overload
|
|
5
|
+
|
|
6
|
+
from unicex._abc import IUniClient
|
|
7
|
+
from unicex.enums import Timeframe
|
|
8
|
+
from unicex.types import KlineDict, OpenInterestDict, OpenInterestItem, TickerDailyDict
|
|
9
|
+
|
|
10
|
+
from .adapter import Adapter
|
|
11
|
+
from .client import Client
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class UniClient(IUniClient[Client]):
|
|
15
|
+
"""Унифицированный клиент для работы с Kucoin API."""
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def _client_cls(self) -> type[Client]:
|
|
19
|
+
"""Возвращает класс клиента для Kucoin.
|
|
20
|
+
|
|
21
|
+
Возвращает:
|
|
22
|
+
type[Client]: Класс клиента для Kucoin.
|
|
23
|
+
"""
|
|
24
|
+
return Client
|
|
25
|
+
|
|
26
|
+
async def tickers(self, only_usdt: bool = True) -> list[str]:
|
|
27
|
+
"""Возвращает список тикеров.
|
|
28
|
+
|
|
29
|
+
Параметры:
|
|
30
|
+
only_usdt (bool): Если True, возвращает только тикеры в паре к USDT.
|
|
31
|
+
|
|
32
|
+
Возвращает:
|
|
33
|
+
list[str]: Список тикеров.
|
|
34
|
+
"""
|
|
35
|
+
raw_data = await self._client.ticker("SPOT")
|
|
36
|
+
return Adapter.tickers(raw_data, only_usdt)
|
|
37
|
+
|
|
38
|
+
async def futures_tickers(self, only_usdt: bool = True) -> list[str]:
|
|
39
|
+
"""Возвращает список тикеров.
|
|
40
|
+
|
|
41
|
+
Параметры:
|
|
42
|
+
only_usdt (bool): Если True, возвращает только тикеры в паре к USDT.
|
|
43
|
+
|
|
44
|
+
Возвращает:
|
|
45
|
+
list[str]: Список тикеров.
|
|
46
|
+
"""
|
|
47
|
+
raw_data = await self._client.ticker("FUTURES")
|
|
48
|
+
return Adapter.tickers(raw_data, only_usdt)
|
|
49
|
+
|
|
50
|
+
async def last_price(self) -> dict[str, float]:
|
|
51
|
+
"""Возвращает последнюю цену для каждого тикера.
|
|
52
|
+
|
|
53
|
+
Возвращает:
|
|
54
|
+
dict[str, float]: Словарь с последними ценами для каждого тикера.
|
|
55
|
+
"""
|
|
56
|
+
raw_data = await self._client.ticker("SPOT")
|
|
57
|
+
return Adapter.last_price(raw_data)
|
|
58
|
+
|
|
59
|
+
async def futures_last_price(self) -> dict[str, float]:
|
|
60
|
+
"""Возвращает последнюю цену для каждого тикера.
|
|
61
|
+
|
|
62
|
+
Возвращает:
|
|
63
|
+
dict[str, float]: Словарь с последними ценами для каждого тикера.
|
|
64
|
+
"""
|
|
65
|
+
raw_data = await self._client.ticker("FUTURES")
|
|
66
|
+
return Adapter.last_price(raw_data)
|
|
67
|
+
|
|
68
|
+
async def ticker_24hr(self) -> TickerDailyDict:
|
|
69
|
+
"""Возвращает статистику за последние 24 часа для каждого тикера.
|
|
70
|
+
|
|
71
|
+
Возвращает:
|
|
72
|
+
TickerDailyDict: Словарь с статистикой за последние 24 часа для каждого тикера.
|
|
73
|
+
"""
|
|
74
|
+
raw_data = await self._client.ticker("SPOT")
|
|
75
|
+
return Adapter.ticker_24hr(raw_data)
|
|
76
|
+
|
|
77
|
+
async def futures_ticker_24hr(self) -> TickerDailyDict:
|
|
78
|
+
"""Возвращает статистику за последние 24 часа для каждого тикера.
|
|
79
|
+
|
|
80
|
+
Возвращает:
|
|
81
|
+
TickerDailyDict: Словарь с статистикой за последние 24 часа для каждого тикера.
|
|
82
|
+
"""
|
|
83
|
+
raw_data = await self._client.ticker("FUTURES")
|
|
84
|
+
return Adapter.ticker_24hr(raw_data)
|
|
85
|
+
|
|
86
|
+
async def klines(
|
|
87
|
+
self,
|
|
88
|
+
symbol: str,
|
|
89
|
+
interval: Timeframe | str,
|
|
90
|
+
limit: int | None = None,
|
|
91
|
+
start_time: int | None = None,
|
|
92
|
+
end_time: int | None = None,
|
|
93
|
+
) -> list[KlineDict]:
|
|
94
|
+
"""Возвращает список свечей для тикера.
|
|
95
|
+
|
|
96
|
+
Параметры:
|
|
97
|
+
symbol (str): Название тикера.
|
|
98
|
+
limit (int | None): Количество свечей.
|
|
99
|
+
interval (Timeframe | str): Таймфрейм свечей.
|
|
100
|
+
start_time (int | None): Время начала периода в миллисекундах.
|
|
101
|
+
end_time (int | None): Время окончания периода в миллисекундах.
|
|
102
|
+
|
|
103
|
+
Возвращает:
|
|
104
|
+
list[KlineDict]: Список свечей для тикера.
|
|
105
|
+
"""
|
|
106
|
+
raise NotImplementedError()
|
|
107
|
+
|
|
108
|
+
async def futures_klines(
|
|
109
|
+
self,
|
|
110
|
+
symbol: str,
|
|
111
|
+
interval: Timeframe | str,
|
|
112
|
+
limit: int | None = None,
|
|
113
|
+
start_time: int | None = None,
|
|
114
|
+
end_time: int | None = None,
|
|
115
|
+
) -> list[KlineDict]:
|
|
116
|
+
"""Возвращает список свечей для тикера.
|
|
117
|
+
|
|
118
|
+
Параметры:
|
|
119
|
+
symbol (str): Название тикера.
|
|
120
|
+
limit (int | None): Количество свечей.
|
|
121
|
+
interval (Timeframe | str): Таймфрейм свечей.
|
|
122
|
+
start_time (int | None): Время начала периода в миллисекундах.
|
|
123
|
+
end_time (int | None): Время окончания периода в миллисекундах.
|
|
124
|
+
|
|
125
|
+
Возвращает:
|
|
126
|
+
list[KlineDict]: Список свечей для тикера.
|
|
127
|
+
"""
|
|
128
|
+
raise NotImplementedError()
|
|
129
|
+
|
|
130
|
+
async def funding_rate(self) -> dict[str, float]:
|
|
131
|
+
"""Возвращает ставку финансирования для всех тикеров.
|
|
132
|
+
|
|
133
|
+
Возвращает:
|
|
134
|
+
dict[str, float]: Ставка финансирования для каждого тикера.
|
|
135
|
+
"""
|
|
136
|
+
raise NotImplementedError()
|
|
137
|
+
|
|
138
|
+
@overload
|
|
139
|
+
async def open_interest(self, symbol: str) -> OpenInterestItem: ...
|
|
140
|
+
|
|
141
|
+
@overload
|
|
142
|
+
async def open_interest(self, symbol: None) -> OpenInterestDict: ...
|
|
143
|
+
|
|
144
|
+
@overload
|
|
145
|
+
async def open_interest(self) -> OpenInterestDict: ...
|
|
146
|
+
|
|
147
|
+
async def open_interest(self, symbol: str | None = None) -> OpenInterestItem | OpenInterestDict:
|
|
148
|
+
"""Возвращает объем открытого интереса для тикера или всех тикеров, если тикер не указан.
|
|
149
|
+
|
|
150
|
+
Параметры:
|
|
151
|
+
symbol (`str | None`): Название тикера. (Опционально, но обязателен для следующих бирж: BINANCE).
|
|
152
|
+
|
|
153
|
+
Возвращает:
|
|
154
|
+
`OpenInterestItem | OpenInterestDict`: Если тикер передан - словарь со временем и объемом
|
|
155
|
+
открытого интереса в монетах. Если нет передан - то словарь, в котором ключ - тикер,
|
|
156
|
+
а значение - словарь с временем и объемом открытого интереса в монетах.
|
|
157
|
+
"""
|
|
158
|
+
raw_data = await self._client.open_interest()
|
|
159
|
+
adapted_data = Adapter.open_interest(raw_data)
|
|
160
|
+
return adapted_data[symbol] if symbol else adapted_data
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
__all__ = ["IUniWebsocketManager"]
|
|
2
|
+
|
|
3
|
+
from collections.abc import Awaitable, Callable, Sequence
|
|
4
|
+
from typing import Any, overload
|
|
5
|
+
|
|
6
|
+
from unicex._abc import IUniWebsocketManager
|
|
7
|
+
from unicex._base import Websocket
|
|
8
|
+
from unicex.enums import Timeframe
|
|
9
|
+
from unicex.types import LoggerLike
|
|
10
|
+
|
|
11
|
+
from .adapter import Adapter
|
|
12
|
+
from .client import Client
|
|
13
|
+
from .uni_client import UniClient
|
|
14
|
+
from .websocket_manager import WebsocketManager
|
|
15
|
+
|
|
16
|
+
type CallbackType = Callable[[Any], Awaitable[None]]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class UniWebsocketManager(IUniWebsocketManager):
|
|
20
|
+
"""Реализация менеджера асинхронных унифицированных вебсокетов."""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self, client: Client | UniClient | None = None, logger: LoggerLike | None = None
|
|
24
|
+
) -> None:
|
|
25
|
+
"""Инициализирует унифицированный менеджер вебсокетов.
|
|
26
|
+
|
|
27
|
+
Параметры:
|
|
28
|
+
client (`Client | UniClient | None`): Клиент Kucoin или унифицированный клиент. Нужен для подключения к приватным топикам.
|
|
29
|
+
logger (`LoggerLike | None`): Логгер для записи логов.
|
|
30
|
+
"""
|
|
31
|
+
super().__init__(client=client, logger=logger)
|
|
32
|
+
self._websocket_manager = WebsocketManager(self._client) # type: ignore
|
|
33
|
+
self._adapter = Adapter()
|
|
34
|
+
|
|
35
|
+
@overload
|
|
36
|
+
def klines(
|
|
37
|
+
self,
|
|
38
|
+
callback: CallbackType,
|
|
39
|
+
timeframe: Timeframe,
|
|
40
|
+
*,
|
|
41
|
+
symbol: str,
|
|
42
|
+
symbols: None = None,
|
|
43
|
+
) -> Websocket: ...
|
|
44
|
+
|
|
45
|
+
@overload
|
|
46
|
+
def klines(
|
|
47
|
+
self,
|
|
48
|
+
callback: CallbackType,
|
|
49
|
+
timeframe: Timeframe,
|
|
50
|
+
*,
|
|
51
|
+
symbol: None = None,
|
|
52
|
+
symbols: Sequence[str],
|
|
53
|
+
) -> Websocket: ...
|
|
54
|
+
|
|
55
|
+
def klines(
|
|
56
|
+
self,
|
|
57
|
+
callback: CallbackType,
|
|
58
|
+
timeframe: Timeframe,
|
|
59
|
+
symbol: str | None = None,
|
|
60
|
+
symbols: Sequence[str] | None = None,
|
|
61
|
+
) -> Websocket:
|
|
62
|
+
"""Открывает стрим свечей (spot) с унификацией сообщений.
|
|
63
|
+
|
|
64
|
+
Параметры:
|
|
65
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
66
|
+
timeframe (`Timeframe`): Временной интервал свечей.
|
|
67
|
+
symbol (`str | None`): Один символ для подписки.
|
|
68
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
69
|
+
|
|
70
|
+
Должен быть указан либо `symbol`, либо `symbols`.
|
|
71
|
+
|
|
72
|
+
Возвращает:
|
|
73
|
+
`Websocket`: Экземпляр вебсокета для управления соединением.
|
|
74
|
+
"""
|
|
75
|
+
raise NotImplementedError()
|
|
76
|
+
|
|
77
|
+
@overload
|
|
78
|
+
def futures_klines(
|
|
79
|
+
self,
|
|
80
|
+
callback: CallbackType,
|
|
81
|
+
timeframe: Timeframe,
|
|
82
|
+
*,
|
|
83
|
+
symbol: str,
|
|
84
|
+
symbols: None = None,
|
|
85
|
+
) -> Websocket: ...
|
|
86
|
+
|
|
87
|
+
@overload
|
|
88
|
+
def futures_klines(
|
|
89
|
+
self,
|
|
90
|
+
callback: CallbackType,
|
|
91
|
+
timeframe: Timeframe,
|
|
92
|
+
*,
|
|
93
|
+
symbol: None = None,
|
|
94
|
+
symbols: Sequence[str],
|
|
95
|
+
) -> Websocket: ...
|
|
96
|
+
|
|
97
|
+
def futures_klines(
|
|
98
|
+
self,
|
|
99
|
+
callback: CallbackType,
|
|
100
|
+
timeframe: Timeframe,
|
|
101
|
+
symbol: str | None = None,
|
|
102
|
+
symbols: Sequence[str] | None = None,
|
|
103
|
+
) -> Websocket:
|
|
104
|
+
"""Открывает стрим свечей (futures) с унификацией сообщений.
|
|
105
|
+
|
|
106
|
+
Параметры:
|
|
107
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
108
|
+
timeframe (`Timeframe`): Временной интервал свечей.
|
|
109
|
+
symbol (`str | None`): Один символ для подписки.
|
|
110
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
111
|
+
|
|
112
|
+
Должен быть указан либо `symbol`, либо `symbols`.
|
|
113
|
+
|
|
114
|
+
Возвращает:
|
|
115
|
+
`Websocket`: Экземпляр вебсокета.
|
|
116
|
+
"""
|
|
117
|
+
raise NotImplementedError()
|
|
118
|
+
|
|
119
|
+
@overload
|
|
120
|
+
def trades(
|
|
121
|
+
self,
|
|
122
|
+
callback: CallbackType,
|
|
123
|
+
*,
|
|
124
|
+
symbol: str,
|
|
125
|
+
symbols: None = None,
|
|
126
|
+
) -> Websocket: ...
|
|
127
|
+
|
|
128
|
+
@overload
|
|
129
|
+
def trades(
|
|
130
|
+
self,
|
|
131
|
+
callback: CallbackType,
|
|
132
|
+
*,
|
|
133
|
+
symbol: None = None,
|
|
134
|
+
symbols: Sequence[str],
|
|
135
|
+
) -> Websocket: ...
|
|
136
|
+
|
|
137
|
+
def trades(
|
|
138
|
+
self,
|
|
139
|
+
callback: CallbackType,
|
|
140
|
+
symbol: str | None = None,
|
|
141
|
+
symbols: Sequence[str] | None = None,
|
|
142
|
+
) -> Websocket:
|
|
143
|
+
"""Открывает стрим сделок (spot) с унификацией сообщений.
|
|
144
|
+
|
|
145
|
+
Параметры:
|
|
146
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
147
|
+
symbol (`str | None`): Один символ для подписки.
|
|
148
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
149
|
+
|
|
150
|
+
Должен быть указан либо `symbol`, либо `symbols`.
|
|
151
|
+
|
|
152
|
+
Возвращает:
|
|
153
|
+
`Websocket`: Экземпляр вебсокета.
|
|
154
|
+
"""
|
|
155
|
+
raise NotImplementedError()
|
|
156
|
+
|
|
157
|
+
@overload
|
|
158
|
+
def aggtrades(
|
|
159
|
+
self,
|
|
160
|
+
callback: CallbackType,
|
|
161
|
+
*,
|
|
162
|
+
symbol: str,
|
|
163
|
+
symbols: None = None,
|
|
164
|
+
) -> Websocket: ...
|
|
165
|
+
|
|
166
|
+
@overload
|
|
167
|
+
def aggtrades(
|
|
168
|
+
self,
|
|
169
|
+
callback: CallbackType,
|
|
170
|
+
*,
|
|
171
|
+
symbol: None = None,
|
|
172
|
+
symbols: Sequence[str],
|
|
173
|
+
) -> Websocket: ...
|
|
174
|
+
|
|
175
|
+
def aggtrades(
|
|
176
|
+
self,
|
|
177
|
+
callback: CallbackType,
|
|
178
|
+
symbol: str | None = None,
|
|
179
|
+
symbols: Sequence[str] | None = None,
|
|
180
|
+
) -> Websocket:
|
|
181
|
+
"""Открывает стрим агрегированных сделок (spot) с унификацией сообщений.
|
|
182
|
+
|
|
183
|
+
Параметры:
|
|
184
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
185
|
+
symbol (`str | None`): Один символ для подписки.
|
|
186
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
187
|
+
|
|
188
|
+
Должен быть указан либо `symbol`, либо `symbols`.
|
|
189
|
+
|
|
190
|
+
Возвращает:
|
|
191
|
+
`Websocket`: Экземпляр вебсокета.
|
|
192
|
+
"""
|
|
193
|
+
raise NotImplementedError()
|
|
194
|
+
|
|
195
|
+
@overload
|
|
196
|
+
def futures_trades(
|
|
197
|
+
self,
|
|
198
|
+
callback: CallbackType,
|
|
199
|
+
*,
|
|
200
|
+
symbol: str,
|
|
201
|
+
symbols: None = None,
|
|
202
|
+
) -> Websocket: ...
|
|
203
|
+
|
|
204
|
+
@overload
|
|
205
|
+
def futures_trades(
|
|
206
|
+
self,
|
|
207
|
+
callback: CallbackType,
|
|
208
|
+
*,
|
|
209
|
+
symbol: None = None,
|
|
210
|
+
symbols: Sequence[str],
|
|
211
|
+
) -> Websocket: ...
|
|
212
|
+
|
|
213
|
+
def futures_trades(
|
|
214
|
+
self,
|
|
215
|
+
callback: CallbackType,
|
|
216
|
+
symbol: str | None = None,
|
|
217
|
+
symbols: Sequence[str] | None = None,
|
|
218
|
+
) -> Websocket:
|
|
219
|
+
"""Открывает стрим сделок (futures) с унификацией сообщений.
|
|
220
|
+
|
|
221
|
+
Параметры:
|
|
222
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
223
|
+
symbol (`str | None`): Один символ для подписки.
|
|
224
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
225
|
+
|
|
226
|
+
Должен быть указан либо `symbol`, либо `symbols`.
|
|
227
|
+
|
|
228
|
+
Возвращает:
|
|
229
|
+
`Websocket`: Экземпляр вебсокета.
|
|
230
|
+
"""
|
|
231
|
+
raise NotImplementedError()
|
|
232
|
+
|
|
233
|
+
@overload
|
|
234
|
+
def futures_aggtrades(
|
|
235
|
+
self,
|
|
236
|
+
callback: CallbackType,
|
|
237
|
+
*,
|
|
238
|
+
symbol: str,
|
|
239
|
+
symbols: None = None,
|
|
240
|
+
) -> Websocket: ...
|
|
241
|
+
|
|
242
|
+
@overload
|
|
243
|
+
def futures_aggtrades(
|
|
244
|
+
self,
|
|
245
|
+
callback: CallbackType,
|
|
246
|
+
*,
|
|
247
|
+
symbol: None = None,
|
|
248
|
+
symbols: Sequence[str],
|
|
249
|
+
) -> Websocket: ...
|
|
250
|
+
|
|
251
|
+
def futures_aggtrades(
|
|
252
|
+
self,
|
|
253
|
+
callback: CallbackType,
|
|
254
|
+
symbol: str | None = None,
|
|
255
|
+
symbols: Sequence[str] | None = None,
|
|
256
|
+
) -> Websocket:
|
|
257
|
+
"""Открывает стрим агрегированных сделок (futures) с унификацией сообщений.
|
|
258
|
+
|
|
259
|
+
Параметры:
|
|
260
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
261
|
+
symbol (`str | None`): Один символ для подписки.
|
|
262
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
263
|
+
|
|
264
|
+
Должен быть указан либо `symbol`, либо `symbols`.
|
|
265
|
+
|
|
266
|
+
Возвращает:
|
|
267
|
+
`Websocket`: Экземпляр вебсокета.
|
|
268
|
+
"""
|
|
269
|
+
raise NotImplementedError()
|
unicex/mapper.py
CHANGED
|
@@ -25,6 +25,9 @@ from .gate import UniWebsocketManager as GateioUniWebsocketManager
|
|
|
25
25
|
from .hyperliquid import ExchangeInfo as HyperliquidExchangeInfo
|
|
26
26
|
from .hyperliquid import UniClient as HyperliquidUniClient
|
|
27
27
|
from .hyperliquid import UniWebsocketManager as HyperliquidUniWebsocketManager
|
|
28
|
+
from .kucoin import ExchangeInfo as KucoinExchangeInfo
|
|
29
|
+
from .kucoin import UniClient as KucoinUniClient
|
|
30
|
+
from .kucoin import UniWebsocketManager as KucoinUniWebsocketManager
|
|
28
31
|
from .mexc import ExchangeInfo as MexcExchangeInfo
|
|
29
32
|
from .mexc import UniClient as MexcUniClient
|
|
30
33
|
from .mexc import UniWebsocketManager as MexcUniWebsocketManager
|
|
@@ -40,6 +43,7 @@ _UNI_CLIENT_MAPPER: dict[Exchange, type[IUniClient]] = {
|
|
|
40
43
|
Exchange.HYPERLIQUID: HyperliquidUniClient,
|
|
41
44
|
Exchange.MEXC: MexcUniClient,
|
|
42
45
|
Exchange.OKX: OkxUniClient,
|
|
46
|
+
Exchange.KUCOIN: KucoinUniClient,
|
|
43
47
|
}
|
|
44
48
|
"""Маппер, который связывает биржу и реализацию унифицированного клиента."""
|
|
45
49
|
|
|
@@ -51,6 +55,7 @@ _UNI_WS_MANAGER_MAPPER: dict[Exchange, type[IUniWebsocketManager]] = {
|
|
|
51
55
|
Exchange.HYPERLIQUID: HyperliquidUniWebsocketManager,
|
|
52
56
|
Exchange.MEXC: MexcUniWebsocketManager,
|
|
53
57
|
Exchange.OKX: OkxUniWebsocketManager,
|
|
58
|
+
Exchange.KUCOIN: KucoinUniWebsocketManager,
|
|
54
59
|
}
|
|
55
60
|
"""Маппер, который связывает биржу и реализацию унифицированного вебсокет-менеджера."""
|
|
56
61
|
|
|
@@ -62,6 +67,7 @@ _EXCHANGE_INFO_MAPPER: dict[Exchange, type[IExchangeInfo]] = {
|
|
|
62
67
|
Exchange.HYPERLIQUID: HyperliquidExchangeInfo,
|
|
63
68
|
Exchange.MEXC: MexcExchangeInfo,
|
|
64
69
|
Exchange.OKX: OkxExchangeInfo,
|
|
70
|
+
Exchange.KUCOIN: KucoinExchangeInfo,
|
|
65
71
|
}
|
|
66
72
|
"""Маппер, который связывает биржу и реализацию сборщика информации о тикерах на бирже."""
|
|
67
73
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
unicex/__init__.py,sha256=
|
|
2
|
-
unicex/enums.py,sha256=
|
|
1
|
+
unicex/__init__.py,sha256=todVh-ePhe2HSqndToGZtpIk5fUow4pbzTFmL3X98tQ,6318
|
|
2
|
+
unicex/enums.py,sha256=agHr6nhfTm1emLzvOxkwRxBUQBZ_7N1xS684hvm8ym4,9591
|
|
3
3
|
unicex/exceptions.py,sha256=r-xZzX78VuxVnI5pe99AM8FIiGcdIUDcF5CaTkQ4NE0,2213
|
|
4
|
-
unicex/extra.py,sha256=
|
|
5
|
-
unicex/mapper.py,sha256=
|
|
4
|
+
unicex/extra.py,sha256=Ki0ZvF88BNpnt-k-c-AgdUig80f-Yi1gTt8qqO7M5ss,13492
|
|
5
|
+
unicex/mapper.py,sha256=yncbMCecZ3nvGtUyvWaiQAQ3RWGAAmm3MEi6Y7YJUG4,5203
|
|
6
6
|
unicex/types.py,sha256=QmhBTfSXX0Ie5E_wDJJmVDLZFMocg6mzh3yBjDAr96Y,4630
|
|
7
7
|
unicex/utils.py,sha256=xzeP8E2bBoie8GvxW97FPEu_QPYMxiNgc9zX7yrdsfk,8048
|
|
8
8
|
unicex/_abc/__init__.py,sha256=fxZjNFJFeFwWTXz8iSDe7eCWwE6xfFwFwAuG6l-TI8A,289
|
|
@@ -52,6 +52,14 @@ unicex/hyperliquid/uni_client.py,sha256=4jv2uC076PBeq-EzCKvwaEEMk_M3HpsWA6iwNepJ
|
|
|
52
52
|
unicex/hyperliquid/uni_websocket_manager.py,sha256=AzR_8Aq98NkCA1oc2IiH02ysMOYLaisEmcuuaafeaJw,9334
|
|
53
53
|
unicex/hyperliquid/user_websocket.py,sha256=BKD9ap2bx5DwpkkwwecfOTVedrZBR9eMAITgCBgg02w,134
|
|
54
54
|
unicex/hyperliquid/websocket_manager.py,sha256=mlsQYykFHfQr-k6AZ3b6JfK7DnnhGtt4MRemZg4O9X4,17991
|
|
55
|
+
unicex/kucoin/__init__.py,sha256=7RSMUDiqjmj8T1cvotlRu8LKH-Ok9UlajSrlXyq1mnc,929
|
|
56
|
+
unicex/kucoin/adapter.py,sha256=dPUFJFIbNmxXfB9qsYlGIYjDttVjX7YSGGoYAJ6jY5w,3531
|
|
57
|
+
unicex/kucoin/client.py,sha256=z37R2vOt4YsJFMI0sG-lRJgrPmN3hSkkYFccErqzH4o,1104
|
|
58
|
+
unicex/kucoin/exchange_info.py,sha256=FjazsBe0_M5vhJer9hL-GrKxsuRqz73IOQ7clXFjuH0,788
|
|
59
|
+
unicex/kucoin/uni_client.py,sha256=yCYgBv6nTYvn0Wt0guFVYZyh6OXbmQMDr4r81D-UqBY,6924
|
|
60
|
+
unicex/kucoin/uni_websocket_manager.py,sha256=B9LZxp7KgitSq8k0Gk2lFcZr51mwStRWFfksz-5DL14,9329
|
|
61
|
+
unicex/kucoin/user_websocket.py,sha256=Tx0yuGpf2HrPbcOHHZioYjVp6LBfagOKF-zzD3UM0Ok,129
|
|
62
|
+
unicex/kucoin/websocket_manager.py,sha256=GNNpzxONgTuTByrsGk1MNMlj5-zx5vs_WBTgssYSYDQ,270
|
|
55
63
|
unicex/mexc/__init__.py,sha256=lltANqM_2P-fmF5j8o5-pjmORPuK6C5sVjcQhuUU_R0,923
|
|
56
64
|
unicex/mexc/adapter.py,sha256=6Ioy6IvXHDRKVBTXnofUYWubVvGx5lUCOxKA8R_ynxY,9862
|
|
57
65
|
unicex/mexc/client.py,sha256=oJSpz-8hvA1WMCN2B7oqI2lXEx1_VJLAn8jbzhW8mF0,30950
|
|
@@ -86,8 +94,8 @@ unicex/okx/uni_client.py,sha256=E_Wod0JSGt1K6k1mAIWnOv350pELbv-nic7g1KgOuos,8694
|
|
|
86
94
|
unicex/okx/uni_websocket_manager.py,sha256=b4f_QjA64DJmENQdIGb5IOVc7kvit7KMCdWeCmRbxGY,9326
|
|
87
95
|
unicex/okx/user_websocket.py,sha256=8c9kpm-xVa729pW93OKUGLHaE9MY0uzEpjIgNIFRF80,126
|
|
88
96
|
unicex/okx/websocket_manager.py,sha256=wROXTUDqKzOE-wDnCtXso_MC4SzfPuPols5aPg_Z3y4,26027
|
|
89
|
-
unicex-0.
|
|
90
|
-
unicex-0.
|
|
91
|
-
unicex-0.
|
|
92
|
-
unicex-0.
|
|
93
|
-
unicex-0.
|
|
97
|
+
unicex-0.14.1.dist-info/licenses/LICENSE,sha256=lNNK4Vqak9cXm6qVJLhbqS7iR_BMj6k7fd7XQ6l1k54,1507
|
|
98
|
+
unicex-0.14.1.dist-info/METADATA,sha256=BUTZCNtVx_QXjkLrvmyCAgvOjLEU_MsiPuj-14-XO84,11752
|
|
99
|
+
unicex-0.14.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
100
|
+
unicex-0.14.1.dist-info/top_level.txt,sha256=_7rar-0OENIg4KRy6cgjWiebFYAJhjKEcMggAocGWG4,7
|
|
101
|
+
unicex-0.14.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|