unicex 0.7.0__py3-none-any.whl → 0.8.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/_abc/__init__.py +4 -0
- unicex/_abc/decoder.py +18 -0
- unicex/_abc/uni_client.py +1 -1
- unicex/_base/__init__.py +2 -0
- unicex/_base/websocket.py +16 -4
- unicex/binance/adapter.py +1 -1
- unicex/binance/client.py +2 -2
- unicex/bitget/client.py +25 -10
- unicex/bybit/adapter.py +1 -1
- unicex/bybit/client.py +4 -4
- unicex/bybit/uni_client.py +2 -2
- unicex/bybit/websocket_manager.py +330 -2
- unicex/extra.py +84 -6
- unicex/gateio/adapter.py +2 -2
- unicex/gateio/client.py +1 -1
- unicex/hyperliquid/adapter.py +12 -1
- unicex/mexc/_spot_ws_proto/PrivateAccountV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PrivateDealsV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PrivateOrdersV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicAggreBookTickerV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicAggreDealsV3Api_pb2.py +42 -0
- unicex/mexc/_spot_ws_proto/PublicAggreDepthsV3Api_pb2.py +42 -0
- unicex/mexc/_spot_ws_proto/PublicBookTickerBatchV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicBookTickerV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicDealsV3Api_pb2.py +42 -0
- unicex/mexc/_spot_ws_proto/PublicFuture_pb2.py +105 -0
- unicex/mexc/_spot_ws_proto/PublicIncreaseDepthsBatchV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicIncreaseDepthsV3Api_pb2.py +42 -0
- unicex/mexc/_spot_ws_proto/PublicLimitDepthsV3Api_pb2.py +42 -0
- unicex/mexc/_spot_ws_proto/PublicMiniTickerV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicMiniTickersV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PublicSpotKlineV3Api_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/PushDataV3ApiWrapper_pb2.py +40 -0
- unicex/mexc/_spot_ws_proto/__init__.py +332 -0
- unicex/mexc/adapter.py +2 -2
- unicex/mexc/client.py +2 -2
- unicex/okx/adapter.py +2 -2
- unicex/okx/client.py +2627 -43
- unicex/okx/uni_client.py +8 -8
- {unicex-0.7.0.dist-info → unicex-0.8.1.dist-info}/METADATA +4 -3
- {unicex-0.7.0.dist-info → unicex-0.8.1.dist-info}/RECORD +44 -25
- {unicex-0.7.0.dist-info → unicex-0.8.1.dist-info}/WHEEL +0 -0
- {unicex-0.7.0.dist-info → unicex-0.8.1.dist-info}/licenses/LICENSE +0 -0
- {unicex-0.7.0.dist-info → unicex-0.8.1.dist-info}/top_level.txt +0 -0
unicex/_abc/__init__.py
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
"""Пакет с абстракциями и интерфейсами."""
|
|
2
|
+
|
|
1
3
|
__all__ = [
|
|
2
4
|
"IUniClient",
|
|
3
5
|
"IUniWebsocketManager",
|
|
4
6
|
"IExchangeInfo",
|
|
7
|
+
"IDecoder",
|
|
5
8
|
]
|
|
6
9
|
|
|
10
|
+
from .decoder import IDecoder
|
|
7
11
|
from .exchange_info import IExchangeInfo
|
|
8
12
|
from .uni_client import IUniClient
|
|
9
13
|
from .uni_websocket_manager import IUniWebsocketManager
|
unicex/_abc/decoder.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
__all__ = ["IDecoder"]
|
|
2
|
+
|
|
3
|
+
from typing import Protocol
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class IDecoder(Protocol):
|
|
7
|
+
"""Протокол для декодеров сообщений c вебсокета."""
|
|
8
|
+
|
|
9
|
+
def decode(self, message: bytes | str) -> dict:
|
|
10
|
+
"""Декодирует сообщение.
|
|
11
|
+
|
|
12
|
+
Параметры:
|
|
13
|
+
message (`Any`): Сообщение для декодирования.
|
|
14
|
+
|
|
15
|
+
Возвращает:
|
|
16
|
+
`Any`: Декодированное сообщение.
|
|
17
|
+
"""
|
|
18
|
+
...
|
unicex/_abc/uni_client.py
CHANGED
|
@@ -115,7 +115,7 @@ class IUniClient(ABC, Generic[TClient]):
|
|
|
115
115
|
Возвращает:
|
|
116
116
|
`bool`: True, если апи ключи присутствуют, иначе False.
|
|
117
117
|
"""
|
|
118
|
-
return self._client.
|
|
118
|
+
return self._client.is_authorized()
|
|
119
119
|
|
|
120
120
|
async def close_connection(self) -> None:
|
|
121
121
|
"""Закрывает сессию клиента."""
|
unicex/_base/__init__.py
CHANGED
unicex/_base/websocket.py
CHANGED
|
@@ -3,7 +3,7 @@ __all__ = ["Websocket"]
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import time
|
|
5
5
|
from collections.abc import Awaitable, Callable
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
7
|
|
|
8
8
|
import orjson
|
|
9
9
|
import websockets
|
|
@@ -13,6 +13,9 @@ from websockets.asyncio.client import ClientConnection
|
|
|
13
13
|
from unicex.exceptions import QueueOverflowError
|
|
14
14
|
from unicex.types import LoggerLike
|
|
15
15
|
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from unicex._abc import IDecoder
|
|
18
|
+
|
|
16
19
|
|
|
17
20
|
class Websocket:
|
|
18
21
|
"""Базовый класс асинхронного вебсокета."""
|
|
@@ -20,6 +23,12 @@ class Websocket:
|
|
|
20
23
|
MAX_QUEUE_SIZE: int = 100
|
|
21
24
|
"""Максимальная длина очереди."""
|
|
22
25
|
|
|
26
|
+
class _JsonDecoder:
|
|
27
|
+
"""Базовый JSON декодер для WebSocket сообщений."""
|
|
28
|
+
|
|
29
|
+
def decode(self, message: bytes | str) -> dict:
|
|
30
|
+
return orjson.loads(message)
|
|
31
|
+
|
|
23
32
|
def __init__(
|
|
24
33
|
self,
|
|
25
34
|
callback: Callable[[Any], Awaitable[None]],
|
|
@@ -32,6 +41,7 @@ class Websocket:
|
|
|
32
41
|
reconnect_timeout: int | float | None = 5,
|
|
33
42
|
worker_count: int = 2,
|
|
34
43
|
logger: LoggerLike | None = None,
|
|
44
|
+
decoder: type["IDecoder"] = _JsonDecoder,
|
|
35
45
|
**kwargs: Any, # Не дадим сломаться, если юзер передал ненужные аргументы
|
|
36
46
|
) -> None:
|
|
37
47
|
"""Инициализация вебсокета.
|
|
@@ -47,6 +57,7 @@ class Websocket:
|
|
|
47
57
|
reconnect_timeout (`int | float | None`): Пауза перед переподключением, сек.
|
|
48
58
|
worker_count (`int`): Количество рабочих задач для обработки сообщений.
|
|
49
59
|
logger (`LoggerLike | None`): Логгер для записи логов.
|
|
60
|
+
decoder (`IDecoder | None`): Декодер для обработки входящих сообщений.
|
|
50
61
|
"""
|
|
51
62
|
self._callback = callback
|
|
52
63
|
self._url = url
|
|
@@ -59,6 +70,7 @@ class Websocket:
|
|
|
59
70
|
self._last_message_time = time.monotonic()
|
|
60
71
|
self._worker_count = worker_count
|
|
61
72
|
self._logger = logger or _logger
|
|
73
|
+
self._decoder = decoder()
|
|
62
74
|
self._tasks: list[asyncio.Task] = []
|
|
63
75
|
self._queue = asyncio.Queue()
|
|
64
76
|
self._running = False
|
|
@@ -114,14 +126,14 @@ class Websocket:
|
|
|
114
126
|
await asyncio.sleep(self._reconnect_timeout)
|
|
115
127
|
await self._after_disconnect()
|
|
116
128
|
|
|
117
|
-
async def _handle_message(self, message: str) -> None:
|
|
129
|
+
async def _handle_message(self, message: str | bytes) -> None:
|
|
118
130
|
"""Обрабатывает входящее сообщение вебсокета."""
|
|
119
131
|
try:
|
|
120
132
|
# Обновленяем время последнего сообщения
|
|
121
133
|
self._last_message_time = time.monotonic()
|
|
122
134
|
|
|
123
135
|
# Ложим сообщение в очередь, предварительно его сериализуя
|
|
124
|
-
await self._queue.put(
|
|
136
|
+
await self._queue.put(self._decoder.decode(message))
|
|
125
137
|
|
|
126
138
|
# Проверяем размер очереди сообщений и выбрасываем ошибку, если он превышает максимальный размер
|
|
127
139
|
self._check_queue_size()
|
|
@@ -139,7 +151,7 @@ class Websocket:
|
|
|
139
151
|
"""Проверяет размер очереди и выбрасывает ошибку при переполнении."""
|
|
140
152
|
qsize = self._queue.qsize()
|
|
141
153
|
if qsize >= self.MAX_QUEUE_SIZE:
|
|
142
|
-
raise QueueOverflowError("Message queue is overflow")
|
|
154
|
+
raise QueueOverflowError(f"Message queue is overflow: {qsize}")
|
|
143
155
|
|
|
144
156
|
async def _after_connect(self, conn: ClientConnection) -> None:
|
|
145
157
|
"""Вызывается после установки соединения."""
|
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
|
|
20
|
+
def tickers(raw_data: list[dict], only_usdt: bool) -> list[str]:
|
|
21
21
|
"""Преобразует сырой ответ, в котором содержатся данные о тикерах в список тикеров.
|
|
22
22
|
|
|
23
23
|
Параметры:
|
unicex/binance/client.py
CHANGED
|
@@ -62,8 +62,8 @@ class Client(BaseClient):
|
|
|
62
62
|
if not signed:
|
|
63
63
|
return {"params": params, "data": data}, None
|
|
64
64
|
|
|
65
|
-
if not self.
|
|
66
|
-
raise NotAuthorized("Api key is required to private endpoints")
|
|
65
|
+
if not self.is_authorized():
|
|
66
|
+
raise NotAuthorized("Api key and api secret is required to private endpoints")
|
|
67
67
|
|
|
68
68
|
# Объединяем все параметры в payload
|
|
69
69
|
payload = {**params, **data}
|
unicex/bitget/client.py
CHANGED
|
@@ -5,6 +5,7 @@ import time
|
|
|
5
5
|
from typing import Any, Literal
|
|
6
6
|
|
|
7
7
|
from unicex._base import BaseClient
|
|
8
|
+
from unicex.exceptions import NotAuthorized
|
|
8
9
|
from unicex.types import RequestMethod
|
|
9
10
|
from unicex.utils import (
|
|
10
11
|
dict_to_query_string,
|
|
@@ -20,6 +21,18 @@ class Client(BaseClient):
|
|
|
20
21
|
_BASE_URL: str = "https://api.bitget.com"
|
|
21
22
|
"""Базовый URL для REST API Bitget."""
|
|
22
23
|
|
|
24
|
+
def is_authorized(self) -> bool:
|
|
25
|
+
"""Проверяет наличие API‑ключей у клиента.
|
|
26
|
+
|
|
27
|
+
Возвращает:
|
|
28
|
+
`bool`: Признак наличия ключей.
|
|
29
|
+
"""
|
|
30
|
+
return (
|
|
31
|
+
self._api_key is not None
|
|
32
|
+
and self._api_secret is not None
|
|
33
|
+
and self._api_passphrase is not None
|
|
34
|
+
)
|
|
35
|
+
|
|
23
36
|
def _sign_message(
|
|
24
37
|
self,
|
|
25
38
|
method: RequestMethod,
|
|
@@ -45,6 +58,9 @@ class Client(BaseClient):
|
|
|
45
58
|
- `timestamp (str)`: Временная метка в миллисекундах.
|
|
46
59
|
- `signature (str)`: Подпись в формате base64.
|
|
47
60
|
"""
|
|
61
|
+
if not self.is_authorized():
|
|
62
|
+
raise NotAuthorized("Api key and api secret is required to private endpoints")
|
|
63
|
+
|
|
48
64
|
timestamp = str(int(time.time() * 1000))
|
|
49
65
|
|
|
50
66
|
path = f"{endpoint}?{dict_to_query_string(params)}" if params else endpoint
|
|
@@ -68,16 +84,15 @@ class Client(BaseClient):
|
|
|
68
84
|
`dict[str, str]`: Словарь заголовков запроса.
|
|
69
85
|
"""
|
|
70
86
|
headers = {"Content-Type": "application/json", "Accept": "application/json"}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
)
|
|
87
|
+
headers.update(
|
|
88
|
+
{
|
|
89
|
+
"ACCESS-KEY": self._api_key, # type: ignore[attr-defined]
|
|
90
|
+
"ACCESS-PASSPHRASE": self._api_passphrase, # type: ignore[attr-defined]
|
|
91
|
+
"ACCESS-TIMESTAMP": timestamp,
|
|
92
|
+
"ACCESS-SIGN": signature,
|
|
93
|
+
"locale": "en-US",
|
|
94
|
+
}
|
|
95
|
+
)
|
|
81
96
|
return headers
|
|
82
97
|
|
|
83
98
|
def _prepare_request_params(
|
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
|
|
19
|
+
def tickers(raw_data: dict, only_usdt: bool) -> list[str]:
|
|
20
20
|
"""Преобразует сырой ответ, в котором содержатся данные о тикерах в список тикеров.
|
|
21
21
|
|
|
22
22
|
Параметры:
|
unicex/bybit/client.py
CHANGED
|
@@ -41,12 +41,12 @@ class Client(BaseClient):
|
|
|
41
41
|
Источник: https://github.com/bybit-exchange/api-usage-examples/blob/master/V5_demo/api_demo/Encryption_HMAC.py
|
|
42
42
|
"""
|
|
43
43
|
# Проверяем наличие апи ключей для подписи запроса
|
|
44
|
-
if not self.
|
|
45
|
-
raise NotAuthorized("
|
|
44
|
+
if not self.is_authorized():
|
|
45
|
+
raise NotAuthorized("Api key and api secret is required to private endpoints")
|
|
46
46
|
|
|
47
47
|
dumped_payload = json.dumps(payload)
|
|
48
|
-
prepared_query_string = timestamp + self._api_key + self._RECV_WINDOW + dumped_payload
|
|
49
|
-
return generate_hmac_sha256_signature(self._api_secret, prepared_query_string)
|
|
48
|
+
prepared_query_string = timestamp + self._api_key + self._RECV_WINDOW + dumped_payload # type: ignore[attrDefined]
|
|
49
|
+
return generate_hmac_sha256_signature(self._api_secret, prepared_query_string) # type: ignore[attrDefined]
|
|
50
50
|
|
|
51
51
|
async def _make_request(
|
|
52
52
|
self,
|
unicex/bybit/uni_client.py
CHANGED
|
@@ -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
|
"""Возвращает последнюю цену для каждого тикера.
|
|
@@ -1,11 +1,339 @@
|
|
|
1
1
|
__all__ = ["WebsocketManager"]
|
|
2
2
|
|
|
3
|
+
import json
|
|
4
|
+
import warnings
|
|
5
|
+
from collections.abc import Awaitable, Callable, Sequence
|
|
6
|
+
from typing import Any, Literal
|
|
3
7
|
|
|
4
|
-
from
|
|
5
|
-
|
|
8
|
+
from unicex._base import Websocket
|
|
9
|
+
|
|
10
|
+
from .client import Client
|
|
6
11
|
|
|
7
12
|
type CallbackType = Callable[[Any], Awaitable[None]]
|
|
8
13
|
|
|
9
14
|
|
|
10
15
|
class WebsocketManager:
|
|
11
16
|
"""Менеджер асинхронных вебсокетов для Bybit."""
|
|
17
|
+
|
|
18
|
+
_BASE_SPOT_URL: str = "wss://stream.bybit.com/v5/public/spot"
|
|
19
|
+
"""Базовый URL для вебсокета на спот."""
|
|
20
|
+
|
|
21
|
+
_BASE_LINEAR_URL: str = "wss://stream.bybit.com/v5/public/linear"
|
|
22
|
+
"""Базовый URL для вебсокета на USDT/USDC перпетуалы и фьючерсы."""
|
|
23
|
+
|
|
24
|
+
_BASE_INVERSE_URL: str = "wss://stream.bybit.com/v5/public/inverse"
|
|
25
|
+
"""Базовый URL для вебсокета на инверсные контракты."""
|
|
26
|
+
|
|
27
|
+
_BASE_OPTION_URL: str = "wss://stream.bybit.com/v5/public/option"
|
|
28
|
+
"""Базовый URL для вебсокета на опционы."""
|
|
29
|
+
|
|
30
|
+
_BASE_PRIVATE_URL: str = "wss://stream.bybit.com/v5/private"
|
|
31
|
+
"""Базовый URL для приватных вебсокетов."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, client: Client | None = None, **ws_kwargs: Any) -> None:
|
|
34
|
+
"""Инициализирует менеджер вебсокетов для Bybit.
|
|
35
|
+
|
|
36
|
+
Параметры:
|
|
37
|
+
client (`Client | None`): Клиент для выполнения запросов. Нужен, чтобы открыть приватные вебсокеты.
|
|
38
|
+
ws_kwargs (`dict[str, Any]`): Дополнительные аргументы, которые прокидываются в `Websocket`.
|
|
39
|
+
"""
|
|
40
|
+
self.client = client
|
|
41
|
+
self._ws_kwargs = ws_kwargs
|
|
42
|
+
|
|
43
|
+
def _generate_subscription_message(
|
|
44
|
+
self,
|
|
45
|
+
topics: Sequence[str],
|
|
46
|
+
req_id: str | None = None,
|
|
47
|
+
) -> list[str]:
|
|
48
|
+
"""Сформировать сообщение для подписки на вебсокет.
|
|
49
|
+
|
|
50
|
+
Параметры:
|
|
51
|
+
topics (`Sequence[str]`): Список топиков для подписки.
|
|
52
|
+
req_id (`str | None`): Опциональный идентификатор запроса.
|
|
53
|
+
|
|
54
|
+
Возвращает:
|
|
55
|
+
`list[str]`: Список JSON строк для отправки.
|
|
56
|
+
"""
|
|
57
|
+
message = {"op": "subscribe", "args": list(topics)}
|
|
58
|
+
if req_id:
|
|
59
|
+
message["req_id"] = req_id
|
|
60
|
+
|
|
61
|
+
return [json.dumps(message)]
|
|
62
|
+
|
|
63
|
+
def _get_url_for_category(
|
|
64
|
+
self, category: Literal["spot", "linear", "inverse", "option", "private"]
|
|
65
|
+
) -> str:
|
|
66
|
+
"""Получить URL для категории.
|
|
67
|
+
|
|
68
|
+
Параметры:
|
|
69
|
+
category (`Literal["spot", "linear", "inverse", "option", "private"]`): Категория рынка.
|
|
70
|
+
|
|
71
|
+
Возвращает:
|
|
72
|
+
`str`: URL для вебсокета.
|
|
73
|
+
"""
|
|
74
|
+
if category == "spot":
|
|
75
|
+
return self._BASE_SPOT_URL
|
|
76
|
+
elif category == "linear":
|
|
77
|
+
return self._BASE_LINEAR_URL
|
|
78
|
+
elif category == "inverse":
|
|
79
|
+
return self._BASE_INVERSE_URL
|
|
80
|
+
elif category == "option":
|
|
81
|
+
return self._BASE_OPTION_URL
|
|
82
|
+
elif category == "private":
|
|
83
|
+
return self._BASE_PRIVATE_URL
|
|
84
|
+
else:
|
|
85
|
+
raise ValueError(f"Unsupported category: {category}")
|
|
86
|
+
|
|
87
|
+
def orderbook(
|
|
88
|
+
self,
|
|
89
|
+
callback: CallbackType,
|
|
90
|
+
category: Literal["spot", "linear", "inverse", "option"],
|
|
91
|
+
depth: Literal[1, 25, 50, 100, 200, 500, 1000] = 1,
|
|
92
|
+
symbol: str | None = None,
|
|
93
|
+
symbols: Sequence[str] | None = None,
|
|
94
|
+
req_id: str | None = None,
|
|
95
|
+
) -> Websocket:
|
|
96
|
+
"""Создает вебсокет для получения данных order book.
|
|
97
|
+
|
|
98
|
+
https://bybit-exchange.github.io/docs/v5/websocket/public/orderbook
|
|
99
|
+
|
|
100
|
+
Параметры:
|
|
101
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
102
|
+
category (`Literal["spot", "linear", "inverse", "option"]`): Категория рынка.
|
|
103
|
+
depth (`Literal[1, 25, 50, 100, 200, 500, 1000]`): Глубина order book.
|
|
104
|
+
symbol (`str | None`): Один символ для подписки.
|
|
105
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
106
|
+
req_id (`str | None`): Опциональный идентификатор запроса.
|
|
107
|
+
|
|
108
|
+
Возвращает:
|
|
109
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
110
|
+
"""
|
|
111
|
+
if symbol and symbols:
|
|
112
|
+
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
113
|
+
if not (symbol or symbols):
|
|
114
|
+
raise ValueError("Either symbol or symbols must be provided")
|
|
115
|
+
|
|
116
|
+
tickers = [symbol] if symbol else symbols
|
|
117
|
+
topics = [f"orderbook.{depth}.{ticker.upper()}" for ticker in tickers] # type: ignore
|
|
118
|
+
|
|
119
|
+
subscription_messages = self._generate_subscription_message(topics, req_id)
|
|
120
|
+
url = self._get_url_for_category(category)
|
|
121
|
+
|
|
122
|
+
return Websocket(
|
|
123
|
+
callback=callback,
|
|
124
|
+
url=url,
|
|
125
|
+
subscription_messages=subscription_messages,
|
|
126
|
+
**self._ws_kwargs,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def klines(
|
|
130
|
+
self,
|
|
131
|
+
callback: CallbackType,
|
|
132
|
+
category: Literal["spot", "linear", "inverse", "option"],
|
|
133
|
+
interval: Literal[
|
|
134
|
+
"1", "3", "5", "15", "30", "60", "120", "240", "360", "720", "D", "W", "M"
|
|
135
|
+
],
|
|
136
|
+
symbol: str | None = None,
|
|
137
|
+
symbols: Sequence[str] | None = None,
|
|
138
|
+
req_id: str | None = None,
|
|
139
|
+
) -> Websocket:
|
|
140
|
+
"""Создает вебсокет для получения данных klines (свечей).
|
|
141
|
+
|
|
142
|
+
https://bybit-exchange.github.io/docs/v5/websocket/public/kline
|
|
143
|
+
|
|
144
|
+
Параметры:
|
|
145
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
146
|
+
category (`Literal["spot", "linear", "inverse", "option"]`): Категория рынка.
|
|
147
|
+
interval (`Literal["1", "3", "5", "15", "30", "60", "120", "240", "360", "720", "D", "W", "M"]`): Интервал свечи.
|
|
148
|
+
symbol (`str | None`): Один символ для подписки.
|
|
149
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
150
|
+
req_id (`str | None`): Опциональный идентификатор запроса.
|
|
151
|
+
|
|
152
|
+
Возвращает:
|
|
153
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
154
|
+
"""
|
|
155
|
+
if symbol and symbols:
|
|
156
|
+
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
157
|
+
if not (symbol or symbols):
|
|
158
|
+
raise ValueError("Either symbol or symbols must be provided")
|
|
159
|
+
|
|
160
|
+
tickers = [symbol] if symbol else symbols
|
|
161
|
+
topics = [f"kline.{interval}.{ticker.upper()}" for ticker in tickers] # type: ignore
|
|
162
|
+
|
|
163
|
+
subscription_messages = self._generate_subscription_message(topics, req_id)
|
|
164
|
+
url = self._get_url_for_category(category)
|
|
165
|
+
|
|
166
|
+
return Websocket(
|
|
167
|
+
callback=callback,
|
|
168
|
+
url=url,
|
|
169
|
+
subscription_messages=subscription_messages,
|
|
170
|
+
**self._ws_kwargs,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
def public_trade(
|
|
174
|
+
self,
|
|
175
|
+
callback: CallbackType,
|
|
176
|
+
category: Literal["spot", "linear", "inverse", "option"],
|
|
177
|
+
symbol: str | None = None,
|
|
178
|
+
symbols: Sequence[str] | None = None,
|
|
179
|
+
req_id: str | None = None,
|
|
180
|
+
) -> Websocket:
|
|
181
|
+
"""Создает вебсокет для получения публичных сделок.
|
|
182
|
+
|
|
183
|
+
https://bybit-exchange.github.io/docs/v5/websocket/public/trade
|
|
184
|
+
|
|
185
|
+
Параметры:
|
|
186
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
187
|
+
category (`Literal["spot", "linear", "inverse", "option"]`): Категория рынка.
|
|
188
|
+
symbol (`str | None`): Один символ для подписки.
|
|
189
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
190
|
+
req_id (`str | None`): Опциональный идентификатор запроса.
|
|
191
|
+
|
|
192
|
+
Возвращает:
|
|
193
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
194
|
+
"""
|
|
195
|
+
if symbol and symbols:
|
|
196
|
+
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
197
|
+
if not (symbol or symbols):
|
|
198
|
+
raise ValueError("Either symbol or symbols must be provided")
|
|
199
|
+
|
|
200
|
+
tickers = [symbol] if symbol else symbols
|
|
201
|
+
topics = [f"publicTrade.{ticker.upper()}" for ticker in tickers] # type: ignore
|
|
202
|
+
|
|
203
|
+
subscription_messages = self._generate_subscription_message(topics, req_id)
|
|
204
|
+
url = self._get_url_for_category(category)
|
|
205
|
+
|
|
206
|
+
return Websocket(
|
|
207
|
+
callback=callback,
|
|
208
|
+
url=url,
|
|
209
|
+
subscription_messages=subscription_messages,
|
|
210
|
+
**self._ws_kwargs,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
def ticker(
|
|
214
|
+
self,
|
|
215
|
+
callback: CallbackType,
|
|
216
|
+
category: Literal["spot", "linear", "inverse", "option"],
|
|
217
|
+
symbol: str | None = None,
|
|
218
|
+
symbols: Sequence[str] | None = None,
|
|
219
|
+
req_id: str | None = None,
|
|
220
|
+
) -> Websocket:
|
|
221
|
+
"""Создает вебсокет для получения тикеров.
|
|
222
|
+
|
|
223
|
+
https://bybit-exchange.github.io/docs/v5/websocket/public/ticker
|
|
224
|
+
|
|
225
|
+
Параметры:
|
|
226
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
227
|
+
category (`Literal["spot", "linear", "inverse", "option"]`): Категория рынка.
|
|
228
|
+
symbol (`str | None`): Один символ для подписки.
|
|
229
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
230
|
+
req_id (`str | None`): Опциональный идентификатор запроса.
|
|
231
|
+
|
|
232
|
+
Возвращает:
|
|
233
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
234
|
+
"""
|
|
235
|
+
if symbol and symbols:
|
|
236
|
+
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
237
|
+
if not (symbol or symbols):
|
|
238
|
+
raise ValueError("Either symbol or symbols must be provided")
|
|
239
|
+
|
|
240
|
+
tickers = [symbol] if symbol else symbols
|
|
241
|
+
topics = [f"tickers.{ticker.upper()}" for ticker in tickers] # type: ignore
|
|
242
|
+
|
|
243
|
+
subscription_messages = self._generate_subscription_message(topics, req_id)
|
|
244
|
+
url = self._get_url_for_category(category)
|
|
245
|
+
|
|
246
|
+
return Websocket(
|
|
247
|
+
callback=callback,
|
|
248
|
+
url=url,
|
|
249
|
+
subscription_messages=subscription_messages,
|
|
250
|
+
**self._ws_kwargs,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
def liquidation(
|
|
254
|
+
self,
|
|
255
|
+
callback: CallbackType,
|
|
256
|
+
category: Literal["linear", "inverse"],
|
|
257
|
+
symbol: str | None = None,
|
|
258
|
+
symbols: Sequence[str] | None = None,
|
|
259
|
+
req_id: str | None = None,
|
|
260
|
+
) -> Websocket:
|
|
261
|
+
"""Создает вебсокет для получения данных ликвидаций.
|
|
262
|
+
|
|
263
|
+
https://bybit-exchange.github.io/docs/v5/websocket/public/liquidation
|
|
264
|
+
|
|
265
|
+
Параметры:
|
|
266
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
267
|
+
category (`Literal["linear", "inverse"]`): Категория рынка (только для деривативов).
|
|
268
|
+
symbol (`str | None`): Один символ для подписки.
|
|
269
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
270
|
+
req_id (`str | None`): Опциональный идентификатор запроса.
|
|
271
|
+
|
|
272
|
+
Возвращает:
|
|
273
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
274
|
+
"""
|
|
275
|
+
warnings.warn(
|
|
276
|
+
"TDepreicated liquidation stream, please move to All Liquidation Subscribe"
|
|
277
|
+
"to the liquidation stream. Pushes at most one order per second per symbol."
|
|
278
|
+
"As such, this feed does not push all liquidations that occur on Bybit.",
|
|
279
|
+
DeprecationWarning,
|
|
280
|
+
stacklevel=2,
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
if symbol and symbols:
|
|
284
|
+
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
285
|
+
if not (symbol or symbols):
|
|
286
|
+
raise ValueError("Either symbol or symbols must be provided")
|
|
287
|
+
|
|
288
|
+
tickers = [symbol] if symbol else symbols
|
|
289
|
+
topics = [f"liquidation.{ticker.upper()}" for ticker in tickers] # type: ignore
|
|
290
|
+
|
|
291
|
+
subscription_messages = self._generate_subscription_message(topics, req_id)
|
|
292
|
+
url = self._get_url_for_category(category)
|
|
293
|
+
|
|
294
|
+
return Websocket(
|
|
295
|
+
callback=callback,
|
|
296
|
+
url=url,
|
|
297
|
+
subscription_messages=subscription_messages,
|
|
298
|
+
**self._ws_kwargs,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
def all_liquidation(
|
|
302
|
+
self,
|
|
303
|
+
callback: CallbackType,
|
|
304
|
+
category: Literal["linear", "inverse"],
|
|
305
|
+
symbol: str | None = None,
|
|
306
|
+
symbols: Sequence[str] | None = None,
|
|
307
|
+
req_id: str | None = None,
|
|
308
|
+
) -> Websocket:
|
|
309
|
+
"""Создает вебсокет для получения данных ликвидаций.
|
|
310
|
+
|
|
311
|
+
https://bybit-exchange.github.io/docs/v5/websocket/public/all-liquidation
|
|
312
|
+
|
|
313
|
+
Параметры:
|
|
314
|
+
callback (`CallbackType`): Асинхронная функция обратного вызова для обработки сообщений.
|
|
315
|
+
category (`Literal["linear", "inverse"]`): Категория рынка (только для деривативов).
|
|
316
|
+
symbol (`str | None`): Один символ для подписки.
|
|
317
|
+
symbols (`Sequence[str] | None`): Список символов для мультиплекс‑подключения.
|
|
318
|
+
req_id (`str | None`): Опциональный идентификатор запроса.
|
|
319
|
+
|
|
320
|
+
Возвращает:
|
|
321
|
+
`Websocket`: Объект для управления вебсокет соединением.
|
|
322
|
+
"""
|
|
323
|
+
if symbol and symbols:
|
|
324
|
+
raise ValueError("Parameters symbol and symbols cannot be used together")
|
|
325
|
+
if not (symbol or symbols):
|
|
326
|
+
raise ValueError("Either symbol or symbols must be provided")
|
|
327
|
+
|
|
328
|
+
tickers = [symbol] if symbol else symbols
|
|
329
|
+
topics = [f"allLiquidation.{ticker.upper()}" for ticker in tickers] # type: ignore
|
|
330
|
+
|
|
331
|
+
subscription_messages = self._generate_subscription_message(topics, req_id)
|
|
332
|
+
url = self._get_url_for_category(category)
|
|
333
|
+
|
|
334
|
+
return Websocket(
|
|
335
|
+
callback=callback,
|
|
336
|
+
url=url,
|
|
337
|
+
subscription_messages=subscription_messages,
|
|
338
|
+
**self._ws_kwargs,
|
|
339
|
+
)
|