unicex 0.14.2__py3-none-any.whl → 0.14.7__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.
@@ -20,16 +20,20 @@ class UniWebsocketManager(IUniWebsocketManager):
20
20
  """Реализация менеджера асинхронных унифицированных вебсокетов."""
21
21
 
22
22
  def __init__(
23
- self, client: Client | UniClient | None = None, logger: LoggerLike | None = None
23
+ self,
24
+ client: Client | UniClient | None = None,
25
+ logger: LoggerLike | None = None,
26
+ **ws_kwargs: Any,
24
27
  ) -> None:
25
28
  """Инициализирует унифицированный менеджер вебсокетов.
26
29
 
27
30
  Параметры:
28
31
  client (`Client | UniClient | None`): Клиент Bybit или унифицированный клиент. Нужен для подключения к приватным топикам.
29
32
  logger (`LoggerLike | None`): Логгер для записи логов.
33
+ ws_kwargs (`dict[str, Any]`): Дополнительные параметры инициализации, которые будут переданы WebsocketManager/Websocket.
30
34
  """
31
35
  super().__init__(client=client, logger=logger)
32
- self._websocket_manager = WebsocketManager(self._client) # type: ignore
36
+ self._websocket_manager = WebsocketManager(self._client, **ws_kwargs) # type: ignore
33
37
  self._adapter = Adapter()
34
38
 
35
39
  @overload
@@ -207,7 +211,7 @@ class UniWebsocketManager(IUniWebsocketManager):
207
211
  Возвращает:
208
212
  `Websocket`: Экземпляр вебсокета.
209
213
  """
210
- raise NotImplementedError()
214
+ return self.trades(callback=callback, symbol=symbol, symbols=symbols) # type: ignore
211
215
 
212
216
  @overload
213
217
  def futures_trades(
@@ -288,4 +292,4 @@ class UniWebsocketManager(IUniWebsocketManager):
288
292
  Возвращает:
289
293
  `Websocket`: Экземпляр вебсокета.
290
294
  """
291
- raise NotImplementedError()
295
+ return self.futures_trades(callback=callback, symbol=symbol, symbols=symbols) # type: ignore
unicex/enums.py CHANGED
@@ -176,6 +176,23 @@ class Timeframe(StrEnum):
176
176
  Timeframe.WEEK_1: "Week1",
177
177
  Timeframe.MONTH_1: "Month1",
178
178
  },
179
+ Exchange.KUCOIN: {
180
+ Timeframe.MIN_1: "1min",
181
+ Timeframe.MIN_3: "3min",
182
+ Timeframe.MIN_5: "5min",
183
+ Timeframe.MIN_15: "15min",
184
+ Timeframe.MIN_30: "30min",
185
+ Timeframe.HOUR_1: "1hour",
186
+ Timeframe.HOUR_2: "2hour",
187
+ Timeframe.HOUR_4: "4hour",
188
+ Timeframe.HOUR_6: "6hour",
189
+ Timeframe.HOUR_8: "8hour",
190
+ Timeframe.HOUR_12: "12hour",
191
+ Timeframe.DAY_1: "1day",
192
+ Timeframe.DAY_3: "3day",
193
+ Timeframe.WEEK_1: "1week",
194
+ Timeframe.MONTH_1: "1month",
195
+ },
179
196
  Exchange.OKX: {
180
197
  Timeframe.MIN_1: "1m",
181
198
  Timeframe.MIN_3: "3m",
unicex/gate/client.py CHANGED
@@ -8,7 +8,7 @@ from typing import Any, Literal
8
8
 
9
9
  from unicex._base import BaseClient
10
10
  from unicex.exceptions import NotAuthorized
11
- from unicex.types import RequestMethod
11
+ from unicex.types import NumberLike, RequestMethod
12
12
  from unicex.utils import dict_to_query_string, filter_params
13
13
 
14
14
 
@@ -328,8 +328,8 @@ class Client(BaseClient):
328
328
  async def cross_liquidate_orders(
329
329
  self,
330
330
  currency_pair: str,
331
- amount: str,
332
- price: str,
331
+ amount: NumberLike,
332
+ price: NumberLike,
333
333
  text: str | None = None,
334
334
  action_mode: str | None = None,
335
335
  ) -> dict:
@@ -353,11 +353,11 @@ class Client(BaseClient):
353
353
  self,
354
354
  currency_pair: str,
355
355
  side: str,
356
- amount: str,
356
+ amount: NumberLike,
357
357
  text: str | None = None,
358
358
  type: str | None = None,
359
359
  account: str | None = None,
360
- price: str | None = None,
360
+ price: NumberLike | None = None,
361
361
  time_in_force: str | None = None,
362
362
  iceberg: str | None = None,
363
363
  auto_borrow: bool | None = None,
@@ -479,8 +479,8 @@ class Client(BaseClient):
479
479
  order_id: str,
480
480
  currency_pair: str | None = None,
481
481
  account: str | None = None,
482
- amount: str | None = None,
483
- price: str | None = None,
482
+ amount: NumberLike | None = None,
483
+ price: NumberLike | None = None,
484
484
  amend_text: str | None = None,
485
485
  action_mode: str | None = None,
486
486
  ) -> dict:
@@ -1344,8 +1344,8 @@ class Client(BaseClient):
1344
1344
  self,
1345
1345
  settle: str,
1346
1346
  order_id: str,
1347
- size: int | None = None,
1348
- price: str | None = None,
1347
+ size: NumberLike | None = None,
1348
+ price: NumberLike | None = None,
1349
1349
  amend_text: str | None = None,
1350
1350
  text: str | None = None,
1351
1351
  ) -> dict:
unicex/gate/uni_client.py CHANGED
@@ -173,8 +173,7 @@ class UniClient(IUniClient[Client]):
173
173
  raw_data = await self._client.futures_tickers(settle="usdt", contract=symbol)
174
174
  items = raw_data if isinstance(raw_data, list) else [raw_data]
175
175
  adapted_data = Adapter.funding_rate(raw_data=items) # type: ignore[reportArgumentType]
176
- if symbol:
177
- return adapted_data[symbol]
176
+ return adapted_data[symbol] if symbol else adapted_data
178
177
  return adapted_data
179
178
 
180
179
  @overload
@@ -20,16 +20,20 @@ class UniWebsocketManager(IUniWebsocketManager):
20
20
  """Реализация менеджера асинхронных унифицированных вебсокетов."""
21
21
 
22
22
  def __init__(
23
- self, client: Client | UniClient | None = None, logger: LoggerLike | None = None
23
+ self,
24
+ client: Client | UniClient | None = None,
25
+ logger: LoggerLike | None = None,
26
+ **ws_kwargs: Any,
24
27
  ) -> None:
25
28
  """Инициализирует унифицированный менеджер вебсокетов.
26
29
 
27
30
  Параметры:
28
31
  client (`Client | UniClient | None`): Клиент Gateio или унифицированный клиент. Нужен для подключения к приватным топикам.
29
32
  logger (`LoggerLike | None`): Логгер для записи логов.
33
+ ws_kwargs (`dict[str, Any]`): Дополнительные параметры инициализации, которые будут переданы WebsocketManager/Websocket.
30
34
  """
31
35
  super().__init__(client=client, logger=logger)
32
- self._websocket_manager = WebsocketManager(self._client) # type: ignore
36
+ self._websocket_manager = WebsocketManager(self._client, **ws_kwargs) # type: ignore
33
37
  self._adapter = Adapter()
34
38
 
35
39
  @overload
@@ -13,7 +13,7 @@ from eth_utils.crypto import keccak
13
13
 
14
14
  from unicex._base import BaseClient
15
15
  from unicex.exceptions import NotAuthorized
16
- from unicex.types import LoggerLike
16
+ from unicex.types import LoggerLike, NumberLike
17
17
  from unicex.utils import filter_params
18
18
 
19
19
  # Authentication
@@ -1184,11 +1184,11 @@ class Client(BaseClient):
1184
1184
  self,
1185
1185
  asset: int,
1186
1186
  is_buy: bool,
1187
- size: str,
1187
+ size: NumberLike,
1188
1188
  reduce_only: bool,
1189
1189
  order_type: Literal["limit", "trigger"],
1190
1190
  order_body: dict,
1191
- price: str | None = None,
1191
+ price: NumberLike | None = None,
1192
1192
  client_order_id: str | None = None,
1193
1193
  grouping: Literal["na", "normalTpsl", "positionTpsl"] = "na",
1194
1194
  builder_address: str | None = None,
@@ -1478,8 +1478,8 @@ class Client(BaseClient):
1478
1478
  order_id: int | str,
1479
1479
  asset: int,
1480
1480
  is_buy: bool,
1481
- price: str | float,
1482
- size: str | float,
1481
+ price: NumberLike,
1482
+ size: NumberLike,
1483
1483
  reduce_only: bool,
1484
1484
  order_type: Literal["limit", "trigger"],
1485
1485
  order_body: dict[str, Any],
@@ -1668,7 +1668,7 @@ class Client(BaseClient):
1668
1668
  hyperliquid_chain: Literal["Mainnet", "Testnet"],
1669
1669
  signature_chain_id: str,
1670
1670
  destination: str,
1671
- amount: str,
1671
+ amount: NumberLike,
1672
1672
  time_ms: int,
1673
1673
  nonce: int | None = None,
1674
1674
  ) -> dict:
@@ -1711,7 +1711,7 @@ class Client(BaseClient):
1711
1711
  signature_chain_id: str,
1712
1712
  destination: str,
1713
1713
  token: str,
1714
- amount: str,
1714
+ amount: NumberLike,
1715
1715
  time_ms: int,
1716
1716
  nonce: int | None = None,
1717
1717
  ) -> dict:
@@ -1753,7 +1753,7 @@ class Client(BaseClient):
1753
1753
  self,
1754
1754
  hyperliquid_chain: Literal["Mainnet", "Testnet"],
1755
1755
  signature_chain_id: str,
1756
- amount: str,
1756
+ amount: NumberLike,
1757
1757
  time_ms: int,
1758
1758
  destination: str,
1759
1759
  nonce: int | None = None,
@@ -1795,7 +1795,7 @@ class Client(BaseClient):
1795
1795
  self,
1796
1796
  hyperliquid_chain: Literal["Mainnet", "Testnet"],
1797
1797
  signature_chain_id: str,
1798
- amount: str,
1798
+ amount: NumberLike,
1799
1799
  to_perp: bool,
1800
1800
  subaccount: str | None = None,
1801
1801
  ) -> dict:
@@ -1845,7 +1845,7 @@ class Client(BaseClient):
1845
1845
  source_dex: str,
1846
1846
  destination_dex: str,
1847
1847
  token: str,
1848
- amount: str,
1848
+ amount: NumberLike,
1849
1849
  from_subaccount: str,
1850
1850
  nonce_value: int,
1851
1851
  nonce: int | None = None,
@@ -2015,7 +2015,7 @@ class Client(BaseClient):
2015
2015
  self,
2016
2016
  vault_address: str,
2017
2017
  is_deposit: bool,
2018
- usd: int,
2018
+ usd: NumberLike,
2019
2019
  nonce: int | None = None,
2020
2020
  expires_after: int | None = None,
2021
2021
  signing_vault_address: str | None = None,
@@ -2147,7 +2147,7 @@ class Client(BaseClient):
2147
2147
  self,
2148
2148
  asset: int,
2149
2149
  is_buy: bool,
2150
- size: str | float,
2150
+ size: NumberLike,
2151
2151
  reduce_only: bool,
2152
2152
  minutes: int,
2153
2153
  randomize: bool,
@@ -1,6 +1,5 @@
1
1
  __all__ = ["UniClient"]
2
2
 
3
- import time
4
3
  from typing import Self, overload
5
4
 
6
5
  import aiohttp
@@ -217,15 +216,14 @@ class UniClient(IUniClient[Client]):
217
216
  list[KlineDict]: Список свечей для тикера.
218
217
  """
219
218
  if not limit and not all([start_time, end_time]):
220
- raise ValueError("limit and (start_time and end_time) must be provided")
219
+ raise ValueError("limit or (start_time and end_time) must be provided")
221
220
 
222
221
  if limit: # Перезаписываем start_time и end_time если указан limit, т.к. по умолчанию HyperLiquid не принимают этот параметр
223
222
  if not isinstance(interval, Timeframe):
224
223
  raise ValueError("interval must be a Timeframe if limit param provided")
225
- end_time = int(time.time() * 1000)
226
- start_time = end_time - (limit * interval.to_seconds * 1000) # type: ignore[reportOptionalOperand]
224
+ start_time, end_time = self.limit_to_start_and_end_time(interval, limit)
227
225
  interval = (
228
- interval.to_exchange_format(Exchange.HYPERLIQUID, MarketType.SPOT)
226
+ interval.to_exchange_format(Exchange.HYPERLIQUID)
229
227
  if isinstance(interval, Timeframe)
230
228
  else interval
231
229
  )
@@ -263,8 +261,7 @@ class UniClient(IUniClient[Client]):
263
261
  if limit: # Перезаписываем start_time и end_time если указан limit, т.к. по умолчанию HyperLiquid не принимают этот параметр
264
262
  if not isinstance(interval, Timeframe):
265
263
  raise ValueError("interval must be a Timeframe if limit param provided")
266
- end_time = int(time.time() * 1000)
267
- start_time = end_time - (limit * interval.to_seconds * 1000) # type: ignore[reportOptionalOperand]
264
+ start_time, end_time = self.limit_to_start_and_end_time(interval, limit)
268
265
  interval = (
269
266
  interval.to_exchange_format(Exchange.HYPERLIQUID, MarketType.FUTURES)
270
267
  if isinstance(interval, Timeframe)
@@ -20,16 +20,20 @@ class UniWebsocketManager(IUniWebsocketManager):
20
20
  """Реализация менеджера асинхронных унифицированных вебсокетов."""
21
21
 
22
22
  def __init__(
23
- self, client: Client | UniClient | None = None, logger: LoggerLike | None = None
23
+ self,
24
+ client: Client | UniClient | None = None,
25
+ logger: LoggerLike | None = None,
26
+ **ws_kwargs: Any,
24
27
  ) -> None:
25
28
  """Инициализирует унифицированный менеджер вебсокетов.
26
29
 
27
30
  Параметры:
28
31
  client (`Client | UniClient | None`): Клиент Hyperliquid или унифицированный клиент. Нужен для подключения к приватным топикам.
29
32
  logger (`LoggerLike | None`): Логгер для записи логов.
33
+ ws_kwargs (`dict[str, Any]`): Дополнительные параметры инициализации, которые будут переданы WebsocketManager/Websocket.
30
34
  """
31
35
  super().__init__(client=client, logger=logger)
32
- self._websocket_manager = WebsocketManager(self._client) # type: ignore
36
+ self._websocket_manager = WebsocketManager(self._client, **ws_kwargs) # type: ignore
33
37
  self._adapter = Adapter()
34
38
 
35
39
  @overload
unicex/kucoin/adapter.py CHANGED
@@ -2,7 +2,13 @@ __all__ = ["Adapter"]
2
2
 
3
3
  from typing import Any
4
4
 
5
- from unicex.types import OpenInterestDict, OpenInterestItem, TickerDailyDict, TickerDailyItem
5
+ from unicex.types import (
6
+ KlineDict,
7
+ OpenInterestDict,
8
+ OpenInterestItem,
9
+ TickerDailyDict,
10
+ TickerDailyItem,
11
+ )
6
12
  from unicex.utils import catch_adapter_errors, decorate_all_methods
7
13
 
8
14
  from .exchange_info import ExchangeInfo
@@ -16,6 +22,23 @@ class Adapter:
16
22
  def tickers(raw_data: dict, only_usdt: bool) -> list[str]:
17
23
  """Преобразует сырой ответ, в котором содержатся данные о тикерах в список тикеров.
18
24
 
25
+ Параметры:
26
+ raw_data (dict): Сырой ответ с биржи.
27
+ only_usdt (bool): Флаг, указывающий, нужно ли включать только тикеры в паре к USDT.
28
+
29
+ Возвращает:
30
+ list[str]: Список тикеров.
31
+ """
32
+ return [
33
+ item["symbol"]
34
+ for item in raw_data["data"]["list"]
35
+ if item["symbol"].endswith("USDT") or not only_usdt
36
+ ]
37
+
38
+ @staticmethod
39
+ def futures_tickers(raw_data: dict, only_usdt: bool) -> list[str]:
40
+ """Преобразует сырой ответ, в котором содержатся данные о тикерах в список тикеров.
41
+
19
42
  Параметры:
20
43
  raw_data (dict): Сырой ответ с биржи.
21
44
  only_usdt (bool): Флаг, указывающий, нужно ли включать только тикеры в паре к USDT.
@@ -39,18 +62,39 @@ class Adapter:
39
62
  Возвращает:
40
63
  TickerDailyDict: Словарь, где ключ - тикер, а значение - статистика за последние 24 часа.
41
64
  """
42
- return {
43
- item["symbol"]: TickerDailyItem(
44
- p=(
45
- round((float(item["lastPrice"]) / float(item["open"]) - 1) * 100, 2)
46
- if float(item["open"]) != 0
47
- else 0.0
48
- ),
49
- v=float(item["baseVolume"]),
50
- q=float(item["quoteVolume"]),
65
+
66
+ def safe_float(value: object, default: float = 0.0) -> float:
67
+ try:
68
+ if value is None:
69
+ return default
70
+ return float(value) # type: ignore
71
+ except (TypeError, ValueError):
72
+ return default
73
+
74
+ result: dict[str, TickerDailyItem] = {}
75
+
76
+ for item in raw_data["data"]["list"]:
77
+ symbol = item.get("symbol")
78
+ if not symbol:
79
+ continue
80
+
81
+ last_price = safe_float(item.get("lastPrice"))
82
+ open_price = safe_float(item.get("open"))
83
+ base_volume = safe_float(item.get("baseVolume"))
84
+ quote_volume = safe_float(item.get("quoteVolume"))
85
+
86
+ if open_price > 0:
87
+ p = round((last_price / open_price - 1) * 100, 2)
88
+ else:
89
+ p = 0.0
90
+
91
+ result[symbol] = TickerDailyItem(
92
+ p=p,
93
+ v=base_volume,
94
+ q=quote_volume,
51
95
  )
52
- for item in raw_data["data"]["list"]
53
- }
96
+
97
+ return result
54
98
 
55
99
  @staticmethod
56
100
  def last_price(raw_data: dict) -> dict[str, float]:
@@ -62,7 +106,11 @@ class Adapter:
62
106
  Возвращает:
63
107
  dict[str, float]: Словарь, где ключ - тикер, а значение - последняя цена.
64
108
  """
65
- return {item["symbol"]: float(item["lastPrice"]) for item in raw_data["data"]["list"]}
109
+ return {
110
+ item["symbol"]: float(item["lastPrice"])
111
+ for item in raw_data["data"]["list"]
112
+ if item["lastPrice"]
113
+ }
66
114
 
67
115
  @staticmethod
68
116
  def open_interest(raw_data: dict[str, Any]) -> OpenInterestDict:
@@ -82,6 +130,24 @@ class Adapter:
82
130
  for item in raw_data["data"]
83
131
  }
84
132
 
133
+ @staticmethod
134
+ def funding_rate(raw_data: dict) -> dict[str, float]:
135
+ """Преобразует историю ставок финансирования в унифицированный формат.
136
+
137
+ Параметры:
138
+ raw_data (dict): Сырой ответ с биржи.
139
+
140
+ Возвращает:
141
+ dict[str, float]: Словарь, где ключ - тикер, а значение - актуальная ставка финансирования.
142
+ """
143
+ symbol = raw_data["data"]["symbol"]
144
+ history = raw_data["data"]["list"]
145
+ if not history:
146
+ return {}
147
+
148
+ last_point = max(history, key=lambda item: int(item["ts"]))
149
+ return {symbol: float(last_point["fundingRate"]) * 100}
150
+
85
151
  @staticmethod
86
152
  def _get_contract_size(symbol: str) -> float:
87
153
  """Возвращает размер контракта для указанного символа тикера."""
@@ -89,3 +155,32 @@ class Adapter:
89
155
  return ExchangeInfo.get_futures_ticker_info(symbol)["contract_size"] or 1
90
156
  except: # noqa
91
157
  return 1
158
+
159
+ @staticmethod
160
+ def klines(raw_data: dict, symbol: str) -> list[KlineDict]:
161
+ """Преобразует данные о свечах в унифицированный формат.
162
+
163
+ Параметры:
164
+ raw_data (dict): Сырой ответ с биржи.
165
+ symbol (str): Символ тикера.
166
+
167
+ Возвращает:
168
+ list[KlineDict]: Список свечей.
169
+ """
170
+ klines: list[KlineDict] = []
171
+ for item in sorted(raw_data["data"]["list"], key=lambda x: int(float(x[0]))):
172
+ klines.append( # noqa: PERF401
173
+ KlineDict(
174
+ s=symbol,
175
+ t=item[0],
176
+ o=float(item[1]),
177
+ h=float(item[3]),
178
+ l=float(item[4]),
179
+ c=float(item[2]),
180
+ v=float(item[5]),
181
+ q=float(item[6]),
182
+ T=None,
183
+ x=None,
184
+ )
185
+ )
186
+ return klines
unicex/kucoin/client.py CHANGED
@@ -4,6 +4,8 @@ __all__ = ["Client"]
4
4
  from typing import Any, Literal
5
5
 
6
6
  from unicex._base import BaseClient
7
+ from unicex.types import RequestMethod
8
+ from unicex.utils import filter_params
7
9
 
8
10
 
9
11
  class Client(BaseClient):
@@ -12,6 +14,36 @@ class Client(BaseClient):
12
14
  _BASE_URL: str = "https://api.kucoin.com"
13
15
  """Базовый URL для запросов."""
14
16
 
17
+ async def _make_request(
18
+ self,
19
+ method: RequestMethod,
20
+ endpoint: str,
21
+ *,
22
+ params: dict[str, Any] | None = None,
23
+ ) -> dict[str, Any]:
24
+ """Выполняет HTTP-запрос к эндпоинтам Kucoin API.
25
+
26
+ Параметры:
27
+ method (str): HTTP метод запроса ("GET", "POST", "DELETE" и т.д.).
28
+ endpoint (str): URL эндпоинта Kucoin API.
29
+ params (dict | None): Параметры запроса.
30
+
31
+ Возвращает:
32
+ dict: Ответ в формате JSON.
33
+ """
34
+ # Составляем URL для запроса
35
+ url = self._BASE_URL + endpoint
36
+
37
+ # Фильтруем параметры от None значений
38
+ params = filter_params(params) if params else {}
39
+
40
+ # Выполняем запрос
41
+ return await super()._make_request(
42
+ method=method,
43
+ url=url,
44
+ params=params,
45
+ )
46
+
15
47
  async def symbol(
16
48
  self,
17
49
  trade_type: Literal["SPOT", "FUTURES", "ISOLATED", "CROSS"],
@@ -21,12 +53,9 @@ class Client(BaseClient):
21
53
 
22
54
  https://www.kucoin.com/docs-new/rest/ua/get-symbol
23
55
  """
24
- url = self._BASE_URL + "/api/ua/v1/market/instrument"
25
- params = {"tradeType": trade_type}
26
- if symbol:
27
- params["symbol"] = symbol
56
+ params = {"tradeType": trade_type, "symbol": symbol}
28
57
 
29
- return await self._make_request("GET", url, params=params)
58
+ return await self._make_request("GET", "/api/ua/v1/market/instrument", params=params)
30
59
 
31
60
  async def ticker(
32
61
  self,
@@ -37,18 +66,57 @@ class Client(BaseClient):
37
66
 
38
67
  https://www.kucoin.com/docs-new/rest/ua/get-ticker
39
68
  """
40
- url = self._BASE_URL + "/api/ua/v1/market/ticker"
41
- params = {"tradeType": trade_type}
42
- if symbol:
43
- params["symbol"] = symbol
69
+ params = {"tradeType": trade_type, "symbol": symbol}
44
70
 
45
- return await self._make_request("GET", url, params=params)
71
+ return await self._make_request("GET", "/api/ua/v1/market/ticker", params=params)
46
72
 
47
73
  async def open_interest(self) -> dict[str, Any]:
48
74
  """Получение открытого интереса.
49
75
 
50
76
  https://www.kucoin.com/docs-new/3476287e0
51
77
  """
52
- url = self._BASE_URL + "/api/ua/v1/market/open-interest"
78
+ return await self._make_request("GET", "/api/ua/v1/market/open-interest")
79
+
80
+ async def kline(
81
+ self,
82
+ trade_type: Literal["SPOT", "FUTURES"],
83
+ symbol: str,
84
+ interval: str,
85
+ start_at: int | None = None,
86
+ end_at: int | None = None,
87
+ ) -> dict[str, Any]:
88
+ """Получение списка свечей.
89
+
90
+ https://www.kucoin.com/docs-new/rest/ua/get-klines
91
+ """
92
+ params = {
93
+ "tradeType": trade_type,
94
+ "symbol": symbol,
95
+ "interval": interval,
96
+ "startAt": start_at,
97
+ "endAt": end_at,
98
+ }
99
+
100
+ return await self._make_request("GET", "/api/ua/v1/market/kline", params=params)
101
+
102
+ async def funding_rate_history(
103
+ self,
104
+ symbol: str,
105
+ start_at: int,
106
+ end_at: int,
107
+ ) -> dict[str, Any]:
108
+ """Получение истории ставок финансирования.
109
+
110
+ https://www.kucoin.com/docs-new/rest/ua/get-history-funding-rate
111
+ """
112
+ params = {
113
+ "symbol": symbol,
114
+ "startAt": start_at,
115
+ "endAt": end_at,
116
+ }
53
117
 
54
- return await self._make_request("GET", url)
118
+ return await self._make_request(
119
+ "GET",
120
+ "/api/ua/v1/market/funding-rate-history",
121
+ params=params,
122
+ )
@@ -22,9 +22,9 @@ class ExchangeInfo(IExchangeInfo):
22
22
  for el in exchange_info["data"]["list"]:
23
23
  tickers_info[el["symbol"]] = TickerInfoItem(
24
24
  tick_precision=None,
25
- tick_step=None,
25
+ tick_step=float(el["tickSize"]),
26
26
  size_precision=None,
27
- size_step=None,
27
+ size_step=float(el["baseOrderStep"]),
28
28
  contract_size=1,
29
29
  )
30
30
 
@@ -36,12 +36,15 @@ class ExchangeInfo(IExchangeInfo):
36
36
  tickers_info = {}
37
37
  exchange_info = await Client(session).symbol("FUTURES")
38
38
  for el in exchange_info["data"]["list"]:
39
- tickers_info[el["symbol"]] = TickerInfoItem(
40
- tick_precision=None,
41
- tick_step=None,
42
- size_precision=None,
43
- size_step=None,
44
- contract_size=float(el["unitSize"]),
45
- )
39
+ try:
40
+ tickers_info[el["symbol"]] = TickerInfoItem(
41
+ tick_precision=None,
42
+ tick_step=float(el["tickSize"]),
43
+ size_precision=None,
44
+ size_step=float(el["lotSize"]),
45
+ contract_size=float(el["unitSize"]),
46
+ )
47
+ except Exception as e:
48
+ cls._logger.error(f"Error loading ticker info for {el}: {e}")
46
49
 
47
50
  cls._futures_tickers_info = tickers_info