Stage 07.3.5 — WebSocket Probe + REST Fallback

This commit is contained in:
2026-04-29 21:40:25 +03:00
parent 41c332d9cb
commit 7c8895c3a5
17 changed files with 934 additions and 18 deletions

View File

@@ -0,0 +1,319 @@
# Stage 07.3.5 — WebSocket Probe + REST Fallback
## Статус этапа
Этап не переводит рынок на WebSocket в production-режим.
В рамках этапа была выполнена проверка WebSocket API Dzengi и подготовлена архитектурная заготовка:
- WebSocket client;
- market cache;
- market stream task;
- fallback через REST.
Но реальный WebSocket endpoint не удалось подтвердить на runtime-уровне.
---
## Что проверялось
В Swagger есть группа `websocket-api`, где endpoints отображаются в формате:
```text
wss:/api/v2/depth
wss:/api/v2/account
wss:/api/v2/aggTrades
wss:/api/v2/exchangeInfo
...
```
Для рынка был выбран endpoint:
```text
wss:/api/v2/depth
```
Он описан как `orderBook`.
Параметры запроса:
```json
{
"limit": 0,
"symbol": "string"
}
```
Для нашего случая:
```json
{
"limit": 5,
"symbol": "BTC/USD_LEVERAGE"
}
```
---
## Что означают параметры
### symbol
Инструмент, по которому нужно получить данные стакана.
Пример:
```text
BTC/USD_LEVERAGE
```
### limit
Количество уровней стакана.
Например:
```json
"limit": 5
```
означает получить 5 лучших уровней ask и 5 лучших уровней bid.
---
## Проверенные варианты подключения
Были проверены варианты:
```text
/api/v2/depth
/api/v1/depth
/ws/api/v2/depth
/ws/api/v1/depth
/ws
/websocket
/stream
/api/v2/ws
/api/v1/ws
```
Также проверялись варианты:
```text
без query parameters
?symbol=BTC/USD_LEVERAGE&limit=5
?symbolName=BTC/USD_LEVERAGE&limit=5
```
И варианты headers:
```text
без headers
X-MBX-APIKEY
Origin
Content-Type: application/json
subprotocol: json
```
---
## Фактический результат
Ни один вариант не вернул:
```text
101 Switching Protocols
```
А именно `101 Switching Protocols` является признаком успешного WebSocket Upgrade.
Фактические ответы:
```text
HTTP 404
HTTP 400
HTTP 200
```
---
## Интерпретация ошибок
### HTTP 404
Endpoint не найден как WebSocket route.
### HTTP 400
Сервер получил WebSocket handshake, но отклонил запрос как некорректный.
### HTTP 200
Endpoint существует как обычный HTTP endpoint, но не выполняет WebSocket Upgrade.
Это значит, что сервер отвечает как REST API, а не как WebSocket.
---
## Вывод
На текущих URL и по текущей Swagger-документации WebSocket endpoint Dzengi не подтверждён.
Swagger показывает `wss:/api/v2/*`, но runtime-проверка не нашла endpoint, который реально открывает WebSocket-соединение.
Поэтому рынок временно остаётся на REST polling через существующий `LiveScreenRunner`.
---
## Что оставить в коде
Можно оставить заготовки:
```text
app/src/integrations/exchange/ws_client.py
app/src/integrations/exchange/market_cache.py
app/src/integrations/exchange/market_stream.py
app/tools/ws_probe.py
```
Они пригодятся, если Dzengi подтвердит настоящий WebSocket endpoint.
Также можно оставить зависимость:
```text
websockets==13.1
```
---
## Что отключить сейчас
Нужно отключить автозапуск WebSocket stream в `app/src/main.py`.
### Было
```python
import asyncio
from contextlib import suppress
from src.bootstrap.app_factory import create_app
from src.integrations.exchange.market_stream import start_market_stream
async def main() -> None:
bot, dispatcher = create_app()
market_stream_task = asyncio.create_task(start_market_stream())
try:
await dispatcher.start_polling(bot)
finally:
market_stream_task.cancel()
with suppress(asyncio.CancelledError):
await market_stream_task
if __name__ == "__main__":
asyncio.run(main())
```
### Должно стать временно
```python
import asyncio
from src.bootstrap.app_factory import create_app
async def main() -> None:
# создаём bot + dispatcher
bot, dispatcher = create_app()
# WebSocket stream временно отключён.
# Причина: Dzengi Swagger содержит wss:/api/v2/* endpoints,
# но runtime probe не нашёл endpoint с WebSocket Upgrade 101.
#
# Когда Dzengi подтвердит рабочий WS endpoint,
# можно будет вернуть запуск:
#
# from src.integrations.exchange.market_stream import start_market_stream
# market_stream_task = asyncio.create_task(start_market_stream())
# запускаем Telegram polling
await dispatcher.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
```
---
## Что НЕ нужно отключать
Не нужно откатывать:
- live-экран рынка;
- live-экран портфеля;
- `LiveScreenRunner`;
- REST polling;
- `ExchangeService.get_price()`;
- `ExchangeService.get_market_snapshot()`.
REST fallback должен остаться рабочим.
---
## Текущее поведение после отключения stream
После отключения WebSocket task:
- бот больше не спамит `market_ws_reconnect`;
- экран 📈 Рынок продолжает обновляться через REST polling;
- экран 💼 Портфель продолжает работать;
- архитектурная заготовка WebSocket остаётся в проекте.
---
## Что нужно запросить у Dzengi / брокера
Для продолжения WebSocket-интеграции нужен один из вариантов:
1. настоящий WebSocket base URL;
2. пример рабочего подключения;
3. required headers;
4. required subprotocol;
5. пример handshake;
6. пример Python/JavaScript клиента;
7. подтверждение, что `wss:/api/v2/depth` действительно поддерживает WebSocket Upgrade.
Ключевой вопрос:
```text
Какой полный URL должен вернуть 101 Switching Protocols для market depth stream?
```
---
## Commit
Рекомендуемый commit message:
```bash
git add .
git commit -m "Stage 07.3.5 - websocket probe with REST fallback"
git push
```
---
## Следующий этап
После фиксации 07.3.5 можно перейти к:
```text
Stage 07.4 — Strategy Plugins
```
Потому что UI, мониторинг и REST fallback уже стабильны.