unicex 0.16.6__py3-none-any.whl → 0.17.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 +5 -4
- unicex/aster/__init__.py +27 -0
- unicex/aster/adapter.py +173 -0
- unicex/aster/client.py +794 -0
- unicex/aster/exchange_info.py +44 -0
- unicex/aster/uni_client.py +184 -0
- unicex/aster/uni_websocket_manager.py +286 -0
- unicex/aster/user_websocket.py +191 -0
- unicex/aster/websocket_manager.py +403 -0
- unicex/binance/uni_client.py +2 -2
- unicex/enums.py +18 -0
- unicex/mapper.py +8 -0
- unicex/mexc/uni_websocket_manager.py +7 -0
- unicex/okx/uni_client.py +1 -1
- {unicex-0.16.6.dist-info → unicex-0.17.1.dist-info}/METADATA +2 -1
- {unicex-0.16.6.dist-info → unicex-0.17.1.dist-info}/RECORD +19 -11
- {unicex-0.16.6.dist-info → unicex-0.17.1.dist-info}/WHEEL +1 -1
- {unicex-0.16.6.dist-info → unicex-0.17.1.dist-info}/licenses/LICENSE +0 -0
- {unicex-0.16.6.dist-info → unicex-0.17.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
__all__ = ["UserWebsocket"]
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from collections.abc import Awaitable, Callable
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from loguru import logger as _logger
|
|
8
|
+
|
|
9
|
+
from unicex._base import Websocket
|
|
10
|
+
from unicex.types import LoggerLike
|
|
11
|
+
|
|
12
|
+
from .client import Client
|
|
13
|
+
|
|
14
|
+
type CallbackType = Callable[[Any], Awaitable[None]]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class UserWebsocket:
|
|
18
|
+
"""Пользовательский вебсокет Aster с авто-продлением listenKey.
|
|
19
|
+
|
|
20
|
+
Работает только с фьючерсным пользовательским стримом.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
_BASE_FUTURES_URL: str = "wss://fstream.asterdex.com"
|
|
24
|
+
"""Базовый URL для пользовательского вебсокета Aster."""
|
|
25
|
+
|
|
26
|
+
_RENEW_INTERVAL: int = 30 * 60
|
|
27
|
+
"""Интервал продления listenKey (сек.)."""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
callback: CallbackType,
|
|
32
|
+
client: Client,
|
|
33
|
+
logger: LoggerLike | None = None,
|
|
34
|
+
**kwargs: Any,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Инициализирует пользовательский вебсокет Aster.
|
|
37
|
+
|
|
38
|
+
- Параметры:
|
|
39
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
40
|
+
client (`Client`): Авторизованный клиент Aster.
|
|
41
|
+
logger (`LoggerLike | None`): Логгер для записи логов.
|
|
42
|
+
kwargs (`dict[str, Any]`): Дополнительные параметры, которые будут переданы в `Websocket`.
|
|
43
|
+
|
|
44
|
+
Возвращает:
|
|
45
|
+
`None`: Ничего не возвращает.
|
|
46
|
+
"""
|
|
47
|
+
self._callback = callback
|
|
48
|
+
self._client = client
|
|
49
|
+
self._logger = logger or _logger
|
|
50
|
+
self._ws_kwargs = kwargs
|
|
51
|
+
|
|
52
|
+
self._listen_key: str | None = None
|
|
53
|
+
self._ws: Websocket | None = None
|
|
54
|
+
self._keepalive_task: asyncio.Task | None = None
|
|
55
|
+
|
|
56
|
+
self._running = False
|
|
57
|
+
|
|
58
|
+
async def start(self) -> None:
|
|
59
|
+
"""Запускает пользовательский стрим с автопродлением listenKey."""
|
|
60
|
+
if self._running:
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
self._running = True
|
|
64
|
+
|
|
65
|
+
# Создаем listenKey, запускаем keepalive и вебсокет.
|
|
66
|
+
try:
|
|
67
|
+
self._listen_key = await self._create_listen_key()
|
|
68
|
+
|
|
69
|
+
# Запускаем фоновое продление ключа до подключения.
|
|
70
|
+
self._keepalive_task = asyncio.create_task(self._keepalive_loop())
|
|
71
|
+
|
|
72
|
+
await self._start_ws(self._listen_key)
|
|
73
|
+
except Exception:
|
|
74
|
+
# Если старт не удался - сбрасываем состояние и чистим ресурсы.
|
|
75
|
+
self._running = False
|
|
76
|
+
await self._stop_keepalive_task()
|
|
77
|
+
if self._listen_key:
|
|
78
|
+
try:
|
|
79
|
+
await self._close_listen_key()
|
|
80
|
+
except Exception as exc:
|
|
81
|
+
self._logger.error(f"Error closing listenKey: {exc}")
|
|
82
|
+
finally:
|
|
83
|
+
self._listen_key = None
|
|
84
|
+
raise
|
|
85
|
+
|
|
86
|
+
async def stop(self) -> None:
|
|
87
|
+
"""Останавливает пользовательский стрим и закрывает listenKey."""
|
|
88
|
+
self._running = False
|
|
89
|
+
|
|
90
|
+
# Останавливаем вебсокет.
|
|
91
|
+
try:
|
|
92
|
+
if isinstance(self._ws, Websocket):
|
|
93
|
+
await self._ws.stop()
|
|
94
|
+
except Exception as exc:
|
|
95
|
+
self._logger.error(f"Error stopping websocket: {exc}")
|
|
96
|
+
finally:
|
|
97
|
+
self._ws = None
|
|
98
|
+
|
|
99
|
+
# Останавливаем фоновое продление ключа.
|
|
100
|
+
await self._stop_keepalive_task()
|
|
101
|
+
|
|
102
|
+
# Закрываем listenKey.
|
|
103
|
+
try:
|
|
104
|
+
if self._listen_key:
|
|
105
|
+
await self._close_listen_key()
|
|
106
|
+
except Exception as exc:
|
|
107
|
+
self._logger.error(f"Error closing listenKey: {exc}")
|
|
108
|
+
finally:
|
|
109
|
+
self._listen_key = None
|
|
110
|
+
|
|
111
|
+
self._logger.info("User websocket stopped")
|
|
112
|
+
|
|
113
|
+
async def restart(self) -> None:
|
|
114
|
+
"""Перезапускает пользовательский вебсокет."""
|
|
115
|
+
await self.stop()
|
|
116
|
+
await self.start()
|
|
117
|
+
|
|
118
|
+
async def _start_ws(self, listen_key: str) -> None:
|
|
119
|
+
"""Запускает WebSocket соединение."""
|
|
120
|
+
ws_url = f"{self._BASE_FUTURES_URL}/ws/{listen_key}"
|
|
121
|
+
self._ws_kwargs["no_message_reconnect_timeout"] = None
|
|
122
|
+
self._ws = Websocket(
|
|
123
|
+
callback=self._callback,
|
|
124
|
+
url=ws_url,
|
|
125
|
+
**self._ws_kwargs,
|
|
126
|
+
)
|
|
127
|
+
await self._ws.start()
|
|
128
|
+
self._logger.info(f"User websocket started: ...{ws_url[-5:]}")
|
|
129
|
+
|
|
130
|
+
async def _keepalive_loop(self) -> None:
|
|
131
|
+
"""Фоновый цикл продления listenKey."""
|
|
132
|
+
while self._running:
|
|
133
|
+
try:
|
|
134
|
+
response = await self._renew_listen_key()
|
|
135
|
+
listen_key = response.get("listenKey") if isinstance(response, dict) else None
|
|
136
|
+
|
|
137
|
+
# Если сервер вернул новый listenKey - перезапускаем соединение.
|
|
138
|
+
if listen_key and listen_key != self._listen_key:
|
|
139
|
+
self._logger.info(
|
|
140
|
+
f"Listen key changed: {self._listen_key} -> {listen_key}. Restarting websocket"
|
|
141
|
+
)
|
|
142
|
+
asyncio.create_task(self.restart())
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
except Exception as exc:
|
|
146
|
+
self._logger.error(f"Error while keeping alive: {exc}")
|
|
147
|
+
asyncio.create_task(self.restart())
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
# Ждем до следующего продления с возможностью быстрого выхода.
|
|
151
|
+
for _ in range(self._RENEW_INTERVAL):
|
|
152
|
+
if not self._running:
|
|
153
|
+
return
|
|
154
|
+
await asyncio.sleep(1)
|
|
155
|
+
|
|
156
|
+
async def _stop_keepalive_task(self) -> None:
|
|
157
|
+
"""Останавливает фоновую задачу продления listenKey."""
|
|
158
|
+
if not isinstance(self._keepalive_task, asyncio.Task):
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
current_task = asyncio.current_task()
|
|
162
|
+
keepalive_task = self._keepalive_task
|
|
163
|
+
self._keepalive_task = None
|
|
164
|
+
keepalive_task.cancel()
|
|
165
|
+
|
|
166
|
+
if keepalive_task is current_task:
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
await keepalive_task
|
|
171
|
+
except asyncio.CancelledError:
|
|
172
|
+
pass
|
|
173
|
+
except Exception as exc:
|
|
174
|
+
self._logger.error(f"Error stopping keepalive task: {exc}")
|
|
175
|
+
|
|
176
|
+
async def _create_listen_key(self) -> str:
|
|
177
|
+
"""Создает новый listenKey."""
|
|
178
|
+
response = await self._client.futures_listen_key()
|
|
179
|
+
key = response.get("listenKey") if isinstance(response, dict) else None
|
|
180
|
+
if not key:
|
|
181
|
+
raise RuntimeError(f"Can not create listenKey: {response}")
|
|
182
|
+
return key
|
|
183
|
+
|
|
184
|
+
async def _renew_listen_key(self) -> dict:
|
|
185
|
+
"""Продлевает listenKey."""
|
|
186
|
+
response = await self._client.futures_renew_listen_key()
|
|
187
|
+
return response if isinstance(response, dict) else {}
|
|
188
|
+
|
|
189
|
+
async def _close_listen_key(self) -> None:
|
|
190
|
+
"""Закрывает listenKey."""
|
|
191
|
+
await self._client.futures_close_listen_key()
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
__all__ = ["WebsocketManager"]
|
|
2
|
+
|
|
3
|
+
from collections.abc import Awaitable, Callable, Sequence
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from unicex._base import Websocket
|
|
7
|
+
|
|
8
|
+
from .client import Client
|
|
9
|
+
|
|
10
|
+
type CallbackType = Callable[[Any], Awaitable[None]]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class WebsocketManager:
|
|
14
|
+
"""Менеджер асинхронных вебсокетов для Aster."""
|
|
15
|
+
|
|
16
|
+
_BASE_URL: str = "wss://fstream.asterdex.com"
|
|
17
|
+
"""Базовый URL для вебсокетов Aster Futures."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, client: Client | None = None, **ws_kwargs: Any) -> None:
|
|
20
|
+
"""Инициализирует менеджер вебсокетов для Aster.
|
|
21
|
+
|
|
22
|
+
Параметры:
|
|
23
|
+
client (`Client | None`): Клиент для выполнения запросов. Нужен, чтобы открыть приватные вебсокеты.
|
|
24
|
+
ws_kwargs (`dict[str, Any]`): Дополнительные аргументы, которые прокидываются в `Websocket`.
|
|
25
|
+
"""
|
|
26
|
+
self.client = client
|
|
27
|
+
self._ws_kwargs = ws_kwargs
|
|
28
|
+
|
|
29
|
+
def _generate_stream_url(
|
|
30
|
+
self,
|
|
31
|
+
type: str,
|
|
32
|
+
symbol: str | None = None,
|
|
33
|
+
symbols: Sequence[str] | None = None,
|
|
34
|
+
require_symbol: bool = False,
|
|
35
|
+
) -> str:
|
|
36
|
+
"""Генерирует URL для вебсокета Aster. Параметры symbol и symbols не могут быть использованы вместе.
|
|
37
|
+
|
|
38
|
+
Параметры:
|
|
39
|
+
type (`str`): Тип стрима.
|
|
40
|
+
symbol (`str | None`): Один символ для подписки.
|
|
41
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
42
|
+
require_symbol (`bool`): Требуется ли символ для подписки.
|
|
43
|
+
|
|
44
|
+
Возвращает:
|
|
45
|
+
`str`: URL для вебсокета.
|
|
46
|
+
"""
|
|
47
|
+
if symbol and symbols:
|
|
48
|
+
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
49
|
+
if require_symbol and not (symbol or symbols):
|
|
50
|
+
raise ValueError("Either symbol or symbols must be provided")
|
|
51
|
+
if symbol:
|
|
52
|
+
return f"{self._BASE_URL}/ws/{symbol.lower()}@{type}"
|
|
53
|
+
if symbols:
|
|
54
|
+
streams = "/".join(f"{s.lower()}@{type}" for s in symbols)
|
|
55
|
+
return f"{self._BASE_URL}/stream?streams={streams}"
|
|
56
|
+
return f"{self._BASE_URL}/ws/{type}"
|
|
57
|
+
|
|
58
|
+
def futures_agg_trade(
|
|
59
|
+
self,
|
|
60
|
+
callback: CallbackType,
|
|
61
|
+
symbol: str | None = None,
|
|
62
|
+
symbols: Sequence[str] | None = None,
|
|
63
|
+
) -> Websocket:
|
|
64
|
+
"""Создает вебсокет для получения агрегированных сделок на фьючерсах.
|
|
65
|
+
|
|
66
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#aggregate-trade-streams
|
|
67
|
+
|
|
68
|
+
Параметры:
|
|
69
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
70
|
+
symbol (`str | None`): Один символ для подписки.
|
|
71
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
72
|
+
|
|
73
|
+
Возвращает:
|
|
74
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
75
|
+
"""
|
|
76
|
+
url = self._generate_stream_url(
|
|
77
|
+
type="aggTrade",
|
|
78
|
+
symbol=symbol,
|
|
79
|
+
symbols=symbols,
|
|
80
|
+
require_symbol=True,
|
|
81
|
+
)
|
|
82
|
+
return Websocket(callback=callback, url=url, **self._ws_kwargs)
|
|
83
|
+
|
|
84
|
+
def futures_symbol_mark_price(
|
|
85
|
+
self,
|
|
86
|
+
callback: CallbackType,
|
|
87
|
+
update_speed: str | None = None,
|
|
88
|
+
symbol: str | None = None,
|
|
89
|
+
symbols: Sequence[str] | None = None,
|
|
90
|
+
) -> Websocket:
|
|
91
|
+
"""Создает вебсокет для получения mark price и funding rate по символам на фьючерсах.
|
|
92
|
+
|
|
93
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#mark-price-stream
|
|
94
|
+
|
|
95
|
+
Параметры:
|
|
96
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
97
|
+
update_speed (`str | None`): Частота обновления ("1s" или пусто). По умолчанию "3s".
|
|
98
|
+
symbol (`str | None`): Один символ для подписки.
|
|
99
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
100
|
+
|
|
101
|
+
Возвращает:
|
|
102
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
103
|
+
"""
|
|
104
|
+
stream_type = "markPrice" + (f"@{update_speed}" if update_speed else "")
|
|
105
|
+
url = self._generate_stream_url(
|
|
106
|
+
type=stream_type,
|
|
107
|
+
symbol=symbol,
|
|
108
|
+
symbols=symbols,
|
|
109
|
+
require_symbol=True,
|
|
110
|
+
)
|
|
111
|
+
return Websocket(callback=callback, url=url, **self._ws_kwargs)
|
|
112
|
+
|
|
113
|
+
def futures_mark_price(
|
|
114
|
+
self, callback: CallbackType, update_speed: str | None = None
|
|
115
|
+
) -> Websocket:
|
|
116
|
+
"""Создает вебсокет для получения mark price и funding rate для всех символов на фьючерсах.
|
|
117
|
+
|
|
118
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#mark-price-stream-for-all-markets
|
|
119
|
+
|
|
120
|
+
Параметры:
|
|
121
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
122
|
+
update_speed (`str | None`): Частота обновления ("1s" или пусто). По умолчанию "3s".
|
|
123
|
+
|
|
124
|
+
Возвращает:
|
|
125
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
126
|
+
"""
|
|
127
|
+
stream_type = "!markPrice@arr" + (f"@{update_speed}" if update_speed else "")
|
|
128
|
+
url = self._generate_stream_url(type=stream_type)
|
|
129
|
+
return Websocket(callback=callback, url=url, **self._ws_kwargs)
|
|
130
|
+
|
|
131
|
+
def futures_klines(
|
|
132
|
+
self,
|
|
133
|
+
callback: CallbackType,
|
|
134
|
+
interval: str,
|
|
135
|
+
symbol: str | None = None,
|
|
136
|
+
symbols: Sequence[str] | None = None,
|
|
137
|
+
) -> Websocket:
|
|
138
|
+
"""Создает вебсокет для получения свечей на фьючерсах.
|
|
139
|
+
|
|
140
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#kline-candlestick-streams
|
|
141
|
+
|
|
142
|
+
Параметры:
|
|
143
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
144
|
+
interval (`str`): Временной интервал свечей.
|
|
145
|
+
symbol (`str | None`): Один символ для подписки.
|
|
146
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
147
|
+
|
|
148
|
+
Возвращает:
|
|
149
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
150
|
+
"""
|
|
151
|
+
url = self._generate_stream_url(
|
|
152
|
+
type=f"kline_{interval}",
|
|
153
|
+
symbol=symbol,
|
|
154
|
+
symbols=symbols,
|
|
155
|
+
require_symbol=True,
|
|
156
|
+
)
|
|
157
|
+
return Websocket(callback=callback, url=url, **self._ws_kwargs)
|
|
158
|
+
|
|
159
|
+
def futures_symbol_mini_ticker(
|
|
160
|
+
self,
|
|
161
|
+
callback: CallbackType,
|
|
162
|
+
symbol: str | None = None,
|
|
163
|
+
symbols: Sequence[str] | None = None,
|
|
164
|
+
) -> Websocket:
|
|
165
|
+
"""Создает вебсокет для мини‑статистики тикера за последние 24 часа на фьючерсах.
|
|
166
|
+
|
|
167
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#individual-symbol-mini-ticker-stream
|
|
168
|
+
|
|
169
|
+
Параметры:
|
|
170
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
171
|
+
symbol (`str | None`): Один символ для подписки.
|
|
172
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
173
|
+
|
|
174
|
+
Возвращает:
|
|
175
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
176
|
+
"""
|
|
177
|
+
url = self._generate_stream_url(
|
|
178
|
+
type="miniTicker",
|
|
179
|
+
symbol=symbol,
|
|
180
|
+
symbols=symbols,
|
|
181
|
+
require_symbol=True,
|
|
182
|
+
)
|
|
183
|
+
return Websocket(callback=callback, url=url, **self._ws_kwargs)
|
|
184
|
+
|
|
185
|
+
def futures_mini_ticker(self, callback: CallbackType) -> Websocket:
|
|
186
|
+
"""Создает вебсокет для мини‑статистики всех тикеров за последние 24 часа на фьючерсах.
|
|
187
|
+
|
|
188
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#all-market-mini-tickers-stream
|
|
189
|
+
|
|
190
|
+
Параметры:
|
|
191
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
192
|
+
|
|
193
|
+
Возвращает:
|
|
194
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
195
|
+
"""
|
|
196
|
+
url = self._generate_stream_url(type="!miniTicker@arr")
|
|
197
|
+
return Websocket(callback=callback, url=url, **self._ws_kwargs)
|
|
198
|
+
|
|
199
|
+
def futures_symbol_ticker(
|
|
200
|
+
self,
|
|
201
|
+
callback: CallbackType,
|
|
202
|
+
symbol: str | None = None,
|
|
203
|
+
symbols: Sequence[str] | None = None,
|
|
204
|
+
) -> Websocket:
|
|
205
|
+
"""Создает вебсокет для расширенной статистики тикера за последние 24 часа на фьючерсах.
|
|
206
|
+
|
|
207
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#individual-symbol-ticker-streams
|
|
208
|
+
|
|
209
|
+
Параметры:
|
|
210
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
211
|
+
symbol (`str | None`): Один символ для подписки.
|
|
212
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
213
|
+
|
|
214
|
+
Возвращает:
|
|
215
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
216
|
+
"""
|
|
217
|
+
url = self._generate_stream_url(
|
|
218
|
+
type="ticker",
|
|
219
|
+
symbol=symbol,
|
|
220
|
+
symbols=symbols,
|
|
221
|
+
require_symbol=True,
|
|
222
|
+
)
|
|
223
|
+
return Websocket(callback=callback, url=url, **self._ws_kwargs)
|
|
224
|
+
|
|
225
|
+
def futures_ticker(self, callback: CallbackType) -> Websocket:
|
|
226
|
+
"""Создает вебсокет для расширенной статистики всех тикеров за последние 24 часа на фьючерсах.
|
|
227
|
+
|
|
228
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#all-market-tickers-streams
|
|
229
|
+
|
|
230
|
+
Параметры:
|
|
231
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
232
|
+
|
|
233
|
+
Возвращает:
|
|
234
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
235
|
+
"""
|
|
236
|
+
url = self._generate_stream_url(type="!ticker@arr")
|
|
237
|
+
return Websocket(callback=callback, url=url, **self._ws_kwargs)
|
|
238
|
+
|
|
239
|
+
def futures_symbol_book_ticker(
|
|
240
|
+
self,
|
|
241
|
+
callback: CallbackType,
|
|
242
|
+
symbol: str | None = None,
|
|
243
|
+
symbols: Sequence[str] | None = None,
|
|
244
|
+
) -> Websocket:
|
|
245
|
+
"""Создает вебсокет для получения лучших бид/аск по символам на фьючерсах.
|
|
246
|
+
|
|
247
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#individual-symbol-book-ticker-streams
|
|
248
|
+
|
|
249
|
+
Параметры:
|
|
250
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
251
|
+
symbol (`str | None`): Один символ для подписки.
|
|
252
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
253
|
+
|
|
254
|
+
Возвращает:
|
|
255
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
256
|
+
"""
|
|
257
|
+
url = self._generate_stream_url(
|
|
258
|
+
type="bookTicker",
|
|
259
|
+
symbol=symbol,
|
|
260
|
+
symbols=symbols,
|
|
261
|
+
require_symbol=True,
|
|
262
|
+
)
|
|
263
|
+
return Websocket(callback=callback, url=url, **self._ws_kwargs)
|
|
264
|
+
|
|
265
|
+
def futures_book_ticker(self, callback: CallbackType) -> Websocket:
|
|
266
|
+
"""Создает вебсокет для получения лучших бид/аск по всем символам на фьючерсах.
|
|
267
|
+
|
|
268
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#all-book-tickers-stream
|
|
269
|
+
|
|
270
|
+
Параметры:
|
|
271
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
272
|
+
|
|
273
|
+
Возвращает:
|
|
274
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
275
|
+
"""
|
|
276
|
+
url = self._generate_stream_url(type="!bookTicker")
|
|
277
|
+
return Websocket(callback=callback, url=url, **self._ws_kwargs)
|
|
278
|
+
|
|
279
|
+
def futures_liquidation_order(
|
|
280
|
+
self,
|
|
281
|
+
callback: CallbackType,
|
|
282
|
+
symbol: str | None = None,
|
|
283
|
+
symbols: Sequence[str] | None = None,
|
|
284
|
+
) -> Websocket:
|
|
285
|
+
"""Создает вебсокет для получения ликвидационных ордеров по символам на фьючерсах.
|
|
286
|
+
|
|
287
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#liquidation-order-streams
|
|
288
|
+
|
|
289
|
+
Параметры:
|
|
290
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
291
|
+
symbol (`str | None`): Один символ для подписки.
|
|
292
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
293
|
+
|
|
294
|
+
Возвращает:
|
|
295
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
296
|
+
"""
|
|
297
|
+
url = self._generate_stream_url(
|
|
298
|
+
type="forceOrder",
|
|
299
|
+
symbol=symbol,
|
|
300
|
+
symbols=symbols,
|
|
301
|
+
require_symbol=True,
|
|
302
|
+
)
|
|
303
|
+
return Websocket(
|
|
304
|
+
callback=callback,
|
|
305
|
+
url=url,
|
|
306
|
+
no_message_reconnect_timeout=60 * 15,
|
|
307
|
+
**self._ws_kwargs,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
def futures_all_liquidation_orders(self, callback: CallbackType) -> Websocket:
|
|
311
|
+
"""Создает вебсокет для получения всех ликвидационных ордеров по рынку на фьючерсах.
|
|
312
|
+
|
|
313
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#all-market-liquidation-order-streams
|
|
314
|
+
|
|
315
|
+
Параметры:
|
|
316
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
317
|
+
|
|
318
|
+
Возвращает:
|
|
319
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
320
|
+
"""
|
|
321
|
+
url = self._generate_stream_url(type="!forceOrder@arr")
|
|
322
|
+
return Websocket(
|
|
323
|
+
callback=callback,
|
|
324
|
+
url=url,
|
|
325
|
+
no_message_reconnect_timeout=60 * 15,
|
|
326
|
+
**self._ws_kwargs,
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
def futures_partial_book_depth(
|
|
330
|
+
self,
|
|
331
|
+
callback: CallbackType,
|
|
332
|
+
levels: str,
|
|
333
|
+
update_speed: str | None = None,
|
|
334
|
+
symbol: str | None = None,
|
|
335
|
+
symbols: Sequence[str] | None = None,
|
|
336
|
+
) -> Websocket:
|
|
337
|
+
"""Создает вебсокет для получения стакана глубиной N уровней на фьючерсах.
|
|
338
|
+
|
|
339
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#partial-book-depth-streams
|
|
340
|
+
|
|
341
|
+
Параметры:
|
|
342
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
343
|
+
levels (`str`): Глубина стакана (уровни).
|
|
344
|
+
update_speed (`str | None`): Скорость обновления стакана ("100ms" | "500ms"). По умолчанию: "250ms".
|
|
345
|
+
symbol (`str | None`): Один символ для подписки.
|
|
346
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
347
|
+
|
|
348
|
+
Возвращает:
|
|
349
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
350
|
+
"""
|
|
351
|
+
stream_type = f"depth{levels}" + (f"@{update_speed}" if update_speed else "")
|
|
352
|
+
url = self._generate_stream_url(
|
|
353
|
+
type=stream_type,
|
|
354
|
+
symbol=symbol,
|
|
355
|
+
symbols=symbols,
|
|
356
|
+
require_symbol=True,
|
|
357
|
+
)
|
|
358
|
+
return Websocket(callback=callback, url=url, **self._ws_kwargs)
|
|
359
|
+
|
|
360
|
+
def futures_diff_depth(
|
|
361
|
+
self,
|
|
362
|
+
callback: CallbackType,
|
|
363
|
+
update_speed: str | None = None,
|
|
364
|
+
symbol: str | None = None,
|
|
365
|
+
symbols: Sequence[str] | None = None,
|
|
366
|
+
) -> Websocket:
|
|
367
|
+
"""Создает вебсокет для получения событий изменения стакана (без лимита глубины) на фьючерсах.
|
|
368
|
+
|
|
369
|
+
https://docs.asterdex.com/product/aster-perpetuals/api/api-documentation#diff-book-depth-streams
|
|
370
|
+
|
|
371
|
+
Параметры:
|
|
372
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
373
|
+
update_speed (`str | None`): Скорость обновления стакана ("100ms" | "500ms"). По умолчанию: "250ms".
|
|
374
|
+
symbol (`str | None`): Один символ для подписки.
|
|
375
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
376
|
+
|
|
377
|
+
Возвращает:
|
|
378
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
379
|
+
"""
|
|
380
|
+
stream_type = "depth" + (f"@{update_speed}" if update_speed else "")
|
|
381
|
+
url = self._generate_stream_url(
|
|
382
|
+
type=stream_type,
|
|
383
|
+
symbol=symbol,
|
|
384
|
+
symbols=symbols,
|
|
385
|
+
require_symbol=True,
|
|
386
|
+
)
|
|
387
|
+
return Websocket(callback=callback, url=url, **self._ws_kwargs)
|
|
388
|
+
|
|
389
|
+
def futures_multiplex_socket(self, callback: CallbackType, streams: str) -> Websocket:
|
|
390
|
+
"""Создает вебсокет для мультиплексирования нескольких стримов в один на фьючерсах.
|
|
391
|
+
|
|
392
|
+
Параметры:
|
|
393
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
394
|
+
streams (`str`): Строка с перечислением стримов.
|
|
395
|
+
|
|
396
|
+
Возвращает:
|
|
397
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
398
|
+
"""
|
|
399
|
+
if streams.startswith("streams="):
|
|
400
|
+
url = f"{self._BASE_URL}/stream?{streams}"
|
|
401
|
+
else:
|
|
402
|
+
url = f"{self._BASE_URL}/stream?streams={streams}"
|
|
403
|
+
return Websocket(callback=callback, url=url, **self._ws_kwargs)
|
unicex/binance/uni_client.py
CHANGED
|
@@ -163,8 +163,8 @@ class UniClient(IUniClient[Client]):
|
|
|
163
163
|
async def funding_rate(self, symbol: str | None = None) -> dict[str, float] | float:
|
|
164
164
|
"""Возвращает ставку финансирования для тикера или всех тикеров, если тикер не указан.
|
|
165
165
|
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
Параметры:
|
|
167
|
+
symbol (`str | None`): Название тикера (Опционально).
|
|
168
168
|
|
|
169
169
|
Возвращает:
|
|
170
170
|
`dict[str, float] | float`: Ставка финансирования для тикера или словарь со ставками для всех тикеров.
|
unicex/enums.py
CHANGED
|
@@ -24,6 +24,7 @@ class MarketType(StrEnum):
|
|
|
24
24
|
class Exchange(StrEnum):
|
|
25
25
|
"""Перечисление бирж."""
|
|
26
26
|
|
|
27
|
+
ASTER = "ASTER"
|
|
27
28
|
BINANCE = "BINANCE"
|
|
28
29
|
BITGET = "BITGET"
|
|
29
30
|
BYBIT = "BYBIT"
|
|
@@ -75,6 +76,23 @@ class Timeframe(StrEnum):
|
|
|
75
76
|
def mapping(self) -> dict[Exchange | tuple[Exchange, MarketType], dict["Timeframe", str]]:
|
|
76
77
|
"""Возвращает словарь с маппингом таймфреймов для каждой биржи."""
|
|
77
78
|
return {
|
|
79
|
+
Exchange.ASTER: {
|
|
80
|
+
Timeframe.MIN_1: "1m",
|
|
81
|
+
Timeframe.MIN_3: "3m",
|
|
82
|
+
Timeframe.MIN_5: "5m",
|
|
83
|
+
Timeframe.MIN_15: "15m",
|
|
84
|
+
Timeframe.MIN_30: "30m",
|
|
85
|
+
Timeframe.HOUR_1: "1h",
|
|
86
|
+
Timeframe.HOUR_2: "2h",
|
|
87
|
+
Timeframe.HOUR_4: "4h",
|
|
88
|
+
Timeframe.HOUR_6: "6h",
|
|
89
|
+
Timeframe.HOUR_8: "8h",
|
|
90
|
+
Timeframe.HOUR_12: "12h",
|
|
91
|
+
Timeframe.DAY_1: "1d",
|
|
92
|
+
Timeframe.DAY_3: "3d",
|
|
93
|
+
Timeframe.WEEK_1: "1w",
|
|
94
|
+
Timeframe.MONTH_1: "1M",
|
|
95
|
+
},
|
|
78
96
|
(Exchange.BINANCE, MarketType.SPOT): {
|
|
79
97
|
Timeframe.SECOND_1: "1s",
|
|
80
98
|
Timeframe.MIN_1: "1m",
|
unicex/mapper.py
CHANGED
|
@@ -12,6 +12,11 @@ from ._abc import IExchangeInfo, IUniClient, IUniWebsocketManager
|
|
|
12
12
|
from .enums import Exchange
|
|
13
13
|
from .exceptions import NotSupported
|
|
14
14
|
|
|
15
|
+
from .aster import (
|
|
16
|
+
UniClient as AsterUniClient,
|
|
17
|
+
UniWebsocketManager as AsterUniWebsocketManager,
|
|
18
|
+
ExchangeInfo as AsterExchangeInfo,
|
|
19
|
+
)
|
|
15
20
|
from .binance import (
|
|
16
21
|
UniClient as BinanceUniClient,
|
|
17
22
|
UniWebsocketManager as BinanceUniWebsocketManager,
|
|
@@ -68,6 +73,7 @@ from .bingx import (
|
|
|
68
73
|
|
|
69
74
|
|
|
70
75
|
_UNI_CLIENT_MAPPER: dict[Exchange, type[IUniClient]] = {
|
|
76
|
+
Exchange.ASTER: AsterUniClient,
|
|
71
77
|
Exchange.BINANCE: BinanceUniClient,
|
|
72
78
|
Exchange.BITGET: BitgetUniClient,
|
|
73
79
|
Exchange.BYBIT: BybitUniClient,
|
|
@@ -81,6 +87,7 @@ _UNI_CLIENT_MAPPER: dict[Exchange, type[IUniClient]] = {
|
|
|
81
87
|
"""Маппер, который связывает биржу и реализацию унифицированного клиента."""
|
|
82
88
|
|
|
83
89
|
_UNI_WS_MANAGER_MAPPER: dict[Exchange, type[IUniWebsocketManager]] = {
|
|
90
|
+
Exchange.ASTER: AsterUniWebsocketManager,
|
|
84
91
|
Exchange.BINANCE: BinanceUniWebsocketManager,
|
|
85
92
|
Exchange.BITGET: BitgetUniWebsocketManager,
|
|
86
93
|
Exchange.BYBIT: BybitUniWebsocketManager,
|
|
@@ -94,6 +101,7 @@ _UNI_WS_MANAGER_MAPPER: dict[Exchange, type[IUniWebsocketManager]] = {
|
|
|
94
101
|
"""Маппер, который связывает биржу и реализацию унифицированного вебсокет-менеджера."""
|
|
95
102
|
|
|
96
103
|
_EXCHANGE_INFO_MAPPER: dict[Exchange, type[IExchangeInfo]] = {
|
|
104
|
+
Exchange.ASTER: AsterExchangeInfo,
|
|
97
105
|
Exchange.BINANCE: BinanceExchangeInfo,
|
|
98
106
|
Exchange.BITGET: BitgetExchangeInfo,
|
|
99
107
|
Exchange.BYBIT: BybitExchangeInfo,
|