unicex 0.7.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/binance/adapter.py CHANGED
@@ -17,7 +17,7 @@ class Adapter:
17
17
  """Адаптер для унификации данных с Binance API."""
18
18
 
19
19
  @staticmethod
20
- def tickers(raw_data: list[dict], only_usdt: bool = True) -> list[str]:
20
+ def tickers(raw_data: list[dict], only_usdt: bool) -> list[str]:
21
21
  """Преобразует сырой ответ, в котором содержатся данные о тикерах в список тикеров.
22
22
 
23
23
  Параметры:
unicex/bybit/adapter.py CHANGED
@@ -16,7 +16,7 @@ class Adapter:
16
16
  """Адаптер для унификации данных с Bybit API."""
17
17
 
18
18
  @staticmethod
19
- def tickers(raw_data: dict, only_usdt: bool = True) -> list[str]:
19
+ def tickers(raw_data: dict, only_usdt: bool) -> list[str]:
20
20
  """Преобразует сырой ответ, в котором содержатся данные о тикерах в список тикеров.
21
21
 
22
22
  Параметры:
@@ -33,7 +33,7 @@ class UniClient(IUniClient[Client]):
33
33
  list[str]: Список тикеров.
34
34
  """
35
35
  raw_data = await self._client.tickers("spot")
36
- return Adapter.tickers(raw_data)
36
+ return Adapter.tickers(raw_data, only_usdt)
37
37
 
38
38
  async def futures_tickers(self, only_usdt: bool = True) -> list[str]:
39
39
  """Возвращает список тикеров.
@@ -45,7 +45,7 @@ class UniClient(IUniClient[Client]):
45
45
  list[str]: Список тикеров.
46
46
  """
47
47
  raw_data = await self._client.tickers("linear")
48
- return Adapter.tickers(raw_data)
48
+ return Adapter.tickers(raw_data, only_usdt)
49
49
 
50
50
  async def last_price(self) -> dict[str, float]:
51
51
  """Возвращает последнюю цену для каждого тикера.
unicex/extra.py CHANGED
@@ -8,6 +8,8 @@ __all__ = [
8
8
  "generate_tv_link",
9
9
  "generate_cg_link",
10
10
  "make_humanreadable",
11
+ "normalize_ticker",
12
+ "normalize_symbol",
11
13
  ]
12
14
 
13
15
  import time
@@ -32,7 +34,7 @@ def percent_greater(higher: float, lower: float) -> float:
32
34
  `float`: На сколько процентов `higher` больше `lower`.
33
35
  """
34
36
  if lower == 0:
35
- return float("inf")
37
+ return 0.0 # Не будем возвращать float('inf'), чтобы не ломать логику приложения
36
38
  return (higher / lower - 1) * 100
37
39
 
38
40
 
@@ -51,7 +53,7 @@ def percent_less(higher: float, lower: float) -> float:
51
53
  `float`: На сколько процентов `lower` меньше `higher`.
52
54
  """
53
55
  if lower == 0:
54
- return float("inf")
56
+ return 0.0 # Не будем возвращать float('inf'), чтобы не ломать логику приложения
55
57
  return (1 - lower / higher) * 100
56
58
 
57
59
 
@@ -91,6 +93,76 @@ class TimeoutTracker[T]:
91
93
  self._blocked_items[item] = time.time() + duration
92
94
 
93
95
 
96
+ def normalize_ticker(raw_ticker: str) -> str:
97
+ """Нормализует тикер и возвращает базовую валюту (например, `BTC`).
98
+
99
+ Эта функция принимает тикер в различных форматах (с разделителями, постфиксом SWAP,
100
+ в верхнем или нижнем регистре) и приводит его к стандартному виду — только базовый актив.
101
+
102
+ Примеры:
103
+ ```python
104
+ normalize_ticker("BTC-USDT") # "BTC"
105
+ normalize_ticker("BTC-USDT-SWAP") # "BTC"
106
+ normalize_ticker("btc_usdt") # "BTC"
107
+ normalize_ticker("BTCUSDT") # "BTC"
108
+ normalize_ticker("BTC") # "BTC"
109
+ ```
110
+
111
+ Параметры:
112
+ raw_ticker (`str`): Исходный тикер в любом из распространённых форматов.
113
+
114
+ Возвращает:
115
+ `str`: Базовый актив в верхнем регистре (например, `"BTC"`).
116
+ """
117
+ ticker = raw_ticker.upper()
118
+
119
+ # Удаляем постфиксы SWAP
120
+ if ticker.endswith(("SWAP", "-SWAP", "_SWAP", ".SWAP")):
121
+ ticker = (
122
+ ticker.removesuffix("-SWAP")
123
+ .removesuffix("_SWAP")
124
+ .removesuffix(".SWAP")
125
+ .removesuffix("SWAP")
126
+ )
127
+
128
+ # Удаляем разделители
129
+ ticker = ticker.translate(str.maketrans("", "", "-_."))
130
+
131
+ # Убираем суффикс валюты котировки
132
+ for quote in ("USDT", "USDC"):
133
+ if ticker.endswith(quote):
134
+ ticker = ticker.removesuffix(quote)
135
+ break
136
+
137
+ return ticker
138
+
139
+
140
+ def normalize_symbol(raw_ticker: str, quote: Literal["USDT", "USDC"] = "USDT") -> str:
141
+ """Нормализует тикер до унифицированного символа (например, `BTCUSDT`).
142
+
143
+ Функция принимает тикер в любом из популярных форматов и возвращает полный символ,
144
+ состоящий из базовой валюты и указанной валюты котировки (`USDT` или `USDC`).
145
+
146
+ Примеры:
147
+ ```python
148
+ normalize_symbol("BTC-USDT") # "BTCUSDT"
149
+ normalize_symbol("BTC") # "BTCUSDT"
150
+ normalize_symbol("btc_usdt_swap") # "BTCUSDT"
151
+ normalize_symbol("ETH", "USDC") # "ETHUSDC"
152
+ ```
153
+
154
+ Параметры:
155
+ raw_ticker (`str`): Исходный тикер в любом из распространённых форматов.
156
+ quote (`Literal["USDT", "USDC"]`, optional): Валюта котировки.
157
+ По умолчанию `"USDT"`.
158
+
159
+ Возвращает:
160
+ `str`: Символ в унифицированном формате, например `"BTCUSDT"`.
161
+ """
162
+ base = normalize_ticker(raw_ticker)
163
+ return f"{base}{quote}"
164
+
165
+
94
166
  def generate_ex_link(exchange: Exchange, market_type: MarketType, symbol: str):
95
167
  """Генерирует ссылку на биржу.
96
168
 
@@ -102,7 +174,8 @@ def generate_ex_link(exchange: Exchange, market_type: MarketType, symbol: str):
102
174
  Возвращает:
103
175
  `str`: Ссылка на биржу.
104
176
  """
105
- ticker = symbol.removesuffix("USDT").removesuffix("_USDT").removesuffix("-USDT")
177
+ symbol = normalize_symbol(symbol)
178
+ ticker = normalize_ticker(symbol)
106
179
  if exchange == Exchange.BINANCE:
107
180
  if market_type == MarketType.FUTURES:
108
181
  return f"https://www.binance.com/en/futures/{symbol}"
@@ -152,7 +225,7 @@ def generate_ex_link(exchange: Exchange, market_type: MarketType, symbol: str):
152
225
  if market_type == MarketType.FUTURES:
153
226
  return f"https://app.hyperliquid.xyz/trade/{ticker}"
154
227
  else:
155
- return f"https://www.kcex.com/ru-RU/exchange/{ticker}/USDC"
228
+ return f"https://app.hyperliquid.xyz/trade/{ticker}/USDC"
156
229
  else:
157
230
  raise NotSupported(f"Exchange {exchange} is not supported")
158
231
 
@@ -168,6 +241,7 @@ def generate_tv_link(exchange: Exchange, market_type: MarketType, symbol: str) -
168
241
  Возвращает:
169
242
  `str`: Ссылка для TradingView.
170
243
  """
244
+ symbol = normalize_symbol(symbol)
171
245
  if market_type == MarketType.FUTURES:
172
246
  return f"https://www.tradingview.com/chart/?symbol={exchange}:{symbol}.P"
173
247
  else:
@@ -187,6 +261,8 @@ def generate_cg_link(exchange: Exchange, market_type: MarketType, symbol: str) -
187
261
  """
188
262
  base_url = "https://www.coinglass.com/tv/ru"
189
263
 
264
+ symbol = normalize_symbol(symbol)
265
+
190
266
  if market_type == MarketType.FUTURES:
191
267
  match exchange:
192
268
  case Exchange.OKX:
@@ -196,9 +272,11 @@ def generate_cg_link(exchange: Exchange, market_type: MarketType, symbol: str) -
196
272
  case Exchange.BITGET:
197
273
  return f"{base_url}/{exchange.capitalize()}_{symbol}_UMCBL"
198
274
  case Exchange.GATEIO:
199
- return f"{base_url}/{exchange.capitalize()}_{symbol.replace('USDT', '_USDT')}"
275
+ return f"{base_url}/Gate_{symbol.replace('USDT', '_USDT')}"
200
276
  case Exchange.BITUNIX:
201
277
  return f"{base_url}/{exchange.capitalize()}_{symbol}"
278
+ case Exchange.HYPERLIQUID:
279
+ return f"{base_url}/{exchange.capitalize()}_{symbol.replace('USDT', '-USD')}"
202
280
  case _:
203
281
  return f"{base_url}/{exchange.capitalize()}_{symbol}"
204
282
  else:
@@ -206,7 +284,7 @@ def generate_cg_link(exchange: Exchange, market_type: MarketType, symbol: str) -
206
284
  if exchange == Exchange.OKX:
207
285
  return f"{base_url}/SPOT_{exchange.upper()}_{symbol.replace('USDT', '-USDT')}"
208
286
  # Для остальных бирж ссылки нет → возвращаем заглушку
209
- return base_url
287
+ return generate_cg_link(exchange, MarketType.FUTURES, symbol)
210
288
 
211
289
 
212
290
  def make_humanreadable(value: float, locale: Literal["ru", "en"] = "ru") -> str:
unicex/gateio/adapter.py CHANGED
@@ -19,7 +19,7 @@ class Adapter:
19
19
  """Адаптер для унификации данных с Gateio API."""
20
20
 
21
21
  @staticmethod
22
- def tickers(raw_data: list[dict], only_usdt: bool = True) -> list[str]:
22
+ def tickers(raw_data: list[dict], only_usdt: bool) -> list[str]:
23
23
  """Преобразует сырой ответ о тикерах в список символов.
24
24
 
25
25
  Параметры:
@@ -36,7 +36,7 @@ class Adapter:
36
36
  ]
37
37
 
38
38
  @staticmethod
39
- def futures_tickers(raw_data: list[dict], only_usdt: bool = True) -> list[str]:
39
+ def futures_tickers(raw_data: list[dict], only_usdt: bool) -> list[str]:
40
40
  """Преобразует сырой ответ о фьючерсных тикерах в список символов.
41
41
 
42
42
  Параметры:
@@ -110,7 +110,18 @@ class Adapter:
110
110
  v = (day_ntl_vlm / mid_px) if mid_px else 0.0
111
111
  q = day_ntl_vlm
112
112
 
113
- result[coin] = TickerDailyItem(p=p, v=v, q=q)
113
+ if coin in result:
114
+ # В случае с конфликтом оставляем ту монету, в которой больше дневного объема, т.к
115
+ # для 6 монет (на 07.10.2025) встречаются пары к USDH, которые повторяют идентификатор.
116
+ # Проблема не критичная - т.к. флаг resolve_symbols по идее использовать должен редко.
117
+ prev_ticker_daily = result[coin]
118
+ curr_ticker_daily = TickerDailyItem(p=p, v=v, q=q)
119
+ if prev_ticker_daily["q"] > curr_ticker_daily["q"]:
120
+ result[coin] = prev_ticker_daily
121
+ else:
122
+ result[coin] = curr_ticker_daily
123
+ else:
124
+ result[coin] = TickerDailyItem(p=p, v=v, q=q)
114
125
 
115
126
  except (KeyError, TypeError, ValueError):
116
127
  continue
unicex/mexc/adapter.py CHANGED
@@ -17,7 +17,7 @@ class Adapter:
17
17
  """Адаптер для унификации данных с Mexc API."""
18
18
 
19
19
  @staticmethod
20
- def tickers(raw_data: list[dict], only_usdt: bool = True) -> list[str]:
20
+ def tickers(raw_data: list[dict], only_usdt: bool) -> list[str]:
21
21
  """Преобразует сырой ответ, в котором содержатся данные о тикерах, в список тикеров.
22
22
 
23
23
  Параметры:
@@ -32,7 +32,7 @@ class Adapter:
32
32
  ]
33
33
 
34
34
  @staticmethod
35
- def futures_tickers(raw_data: dict, only_usdt: bool = True) -> list[str]:
35
+ def futures_tickers(raw_data: dict, only_usdt: bool) -> list[str]:
36
36
  """Преобразует сырой ответ, в котором содержатся данные о фьючерсных тикерах, в список тикеров.
37
37
 
38
38
  Параметры:
unicex/okx/adapter.py CHANGED
@@ -18,7 +18,7 @@ class Adapter:
18
18
  """Адаптер для унификации данных с Okx API."""
19
19
 
20
20
  @staticmethod
21
- def tickers(raw_data: dict, only_usdt: bool = True) -> list[str]:
21
+ def tickers(raw_data: dict, only_usdt: bool) -> list[str]:
22
22
  """Преобразует сырые данные о тикерах в список унифицированных символов.
23
23
 
24
24
  Параметры:
@@ -35,7 +35,7 @@ class Adapter:
35
35
  ]
36
36
 
37
37
  @staticmethod
38
- def futures_tickers(raw_data: dict, only_usdt: bool = True) -> list[str]:
38
+ def futures_tickers(raw_data: dict, only_usdt: bool) -> list[str]:
39
39
  """Преобразует сырые данные о тикерах в список унифицированных символов.
40
40
 
41
41
  Параметры: