Stage 07.3.5 — WebSocket Probe + REST Fallback
This commit is contained in:
319
docs/stages/stage-07_3_5-websocket-probe-and-rest-fallback.md
Normal file
319
docs/stages/stage-07_3_5-websocket-probe-and-rest-fallback.md
Normal 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 уже стабильны.
|
||||
Reference in New Issue
Block a user