21 KiB
Build 058 — Runtime исследование Dzengi WebSocket OHLC Market Data
Контроль документа
| Свойство | Значение |
|---|---|
| Build | 058 |
| Название | Runtime исследование Dzengi WebSocket OHLC Market Data |
| Статус | Completed |
| Проект | Dzentra |
| Подсистема | Market Data Acquisition |
| Источник | Dzengi Exchange |
| Дата | 2026-07-16 |
Цель Build
Провести полное runtime-исследование контракта WebSocket
OHLCMarketData.subscribe
и определить возможность его использования в качестве канонического источника Candles Feed.
Необходимо было подтвердить:
- соответствие Swagger реальному API;
- формат подписки;
- формат ACK;
- формат событий;
- поддерживаемые параметры;
- поддержку нескольких символов;
- поддержку нескольких интервалов;
- наличие или отсутствие Volume;
- соответствие REST API;
- возможность использования в Build 059.
Исходные материалы
Были исследованы:
REST
GET /api/v1/klines
WebSocket
OHLCMarketData.subscribe
Swagger WebSocket
wss://.../connect
Также были выполнены реальные подключения к:
demo-api-adapter.dzengi.com
и
api-adapter.dzengi.com
Вывод по Demo
Во время исследования обнаружено:
Demo WebSocket корректно принимает подписку:
OHLCMarketData.subscribe
но поток данных практически отсутствует.
Поэтому дальнейшее исследование проводилось исключительно на Production API.
Production WebSocket
Используемый URL
wss://api-adapter.dzengi.com/connect
Подписка выполняется сообщением
{
"correlationId": "<uuid>",
"destination": "OHLCMarketData.subscribe",
"payload": {
"intervals": [
"1m"
],
"symbols": [
"BTC/USD_LEVERAGE"
],
"type": "classic"
}
}
После отправки сервер возвращает ACK.
ACK
Фактический ответ сервера
{
"correlationId": "...",
"destination": "OHLCMarketData.subscribe",
"payload": {
"subscriptions": {
"BTC/USD_LEVERAGE:1m": "PROCESSED"
}
},
"status": "OK"
}
Подтверждено:
- destination совпадает со Swagger;
- status = OK;
- подписки перечислены внутри payload.subscriptions;
- ключ имеет вид
symbol:interval
например
BTC/USD_LEVERAGE:1m
Поддержка нескольких интервалов
Проверена подписка
{
"payload": {
"symbols":[
"BTC/USD_LEVERAGE"
],
"intervals":[
"1m",
"5m"
],
"type":"classic"
}
}
Полученный ACK
{
"subscriptions": {
"BTC/USD_LEVERAGE:1m":"PROCESSED",
"BTC/USD_LEVERAGE:5m":"PROCESSED"
}
}
Таким образом подтверждено, что одна подписка может одновременно обслуживать несколько таймфреймов.
Каждая комбинация
symbol + interval
имеет собственный статус.
Формат события
После закрытия свечи сервер публикует
{
"destination":"ohlc.event",
"payload":{
"symbol":"BTC/USD_LEVERAGE",
"interval":"1m",
"type":"classic",
"t":1784224740000,
"o":63992.0,
"h":64032.55,
"l":63984.0,
"c":64032.55
},
"status":"OK"
}
Полученные поля
| Поле | Значение |
|---|---|
| symbol | инструмент |
| interval | таймфрейм |
| type | тип свечи |
| t | open time |
| o | Open |
| h | High |
| l | Low |
| c | Close |
Volume отсутствует.
Это подтверждено всеми полученными событиями.
Исследование типа свечей
Swagger указывает, что поле
type
используется для выбора разновидности свечей.
Во время runtime-исследования были проверены все возможные значения.
Попытка использовать bid
Подписка
{
"payload":{
"symbols":[
"BTC/USD_LEVERAGE"
],
"intervals":[
"1m"
],
"type":"bid"
}
}
Ответ сервера
{
"subscriptions":{
"bid":"ERROR: Invalid type. Valid: classic, heikin-ashi"
}
}
Таким образом подтверждено, что WebSocket не использует параметр
priceType
аналогичный REST.
Поддерживаемые значения type
Во время исследования подтверждены два допустимых значения
classic
и
heikin-ashi
Других поддерживаемых вариантов обнаружено не было.
Исследование classic
Подписка
{
"payload":{
"symbols":[
"BTC/USD_LEVERAGE"
],
"intervals":[
"1m"
],
"type":"classic"
}
}
События поступают регулярно после закрытия каждой минутной свечи.
Пример
{
"destination":"ohlc.event",
"payload":{
"symbol":"BTC/USD_LEVERAGE",
"interval":"1m",
"type":"classic",
"t":1784224860000,
"o":64047.45,
"h":64099.10,
"l":64042.25,
"c":64098.00
}
}
Сравнение с REST
Для той же свечи был выполнен запрос
GET /api/v1/klines
priceType=bid
Получен ответ
[
[
1784224860000,
"64047.45",
"64099.10",
"64042.25",
"64098.00",
165
]
]
Полное сравнение
| Поле | REST | WebSocket |
|---|---|---|
| Open Time | ✔ | ✔ |
| Open | ✔ | ✔ |
| High | ✔ | ✔ |
| Low | ✔ | ✔ |
| Close | ✔ | ✔ |
| Volume | ✔ | ✖ |
OHLC совпадают полностью.
Вывод
Runtime подтвердил, что
classic
представляет собой классические рыночные свечи, идентичные
REST /klines
priceType=bid
за исключением отсутствующего объёма.
Исследование Heikin-Ashi
Подписка
{
"payload":{
"symbols":[
"BTC/USD_LEVERAGE"
],
"intervals":[
"1m"
],
"type":"heikin-ashi"
}
}
Получен ACK
{
"subscriptions":{
"BTC/USD_LEVERAGE:1m":"PROCESSED"
}
}
После этого сервер начал публиковать события
{
"destination":"ohlc.event",
"payload":{
"symbol":"BTC/USD_LEVERAGE",
"interval":"1m",
"type":"heikin-ashi",
"t":1784225280000,
"o":64068.18,
"h":64128.80,
"l":64068.18,
"c":64100.85
}
}
Получены несколько последовательных свечей.
Вывод по Heikin-Ashi
Контракт полностью совпадает с форматом
classic
Изменяется только способ расчёта OHLC.
Следовательно
classic
и
heikin-ashi
представляют две различные серии свечей, имеющие одинаковый транспортный контракт.
Исследование времени публикации
Во время наблюдения было подтверждено, что событие публикуется только после завершения интервала.
Например
t = 1784224860000
соответствует времени открытия свечи.
Само событие было получено примерно через одну минуту, после её закрытия.
Таким образом
t
является временем открытия свечи, а не временем публикации события.
Исследование нескольких интервалов
Одновременно были подписаны
1m
и
5m
После закрытия пятиминутного интервала сервер практически одновременно прислал
1m
и
5m
события.
Полученное пятиминутное событие
{
"payload":{
"interval":"5m",
"type":"classic",
"t":1784225400000,
"o":64130.80,
"h":64184.70,
"l":64118.20,
"c":64151.00
}
}
Тем самым подтверждено, что сервер самостоятельно агрегирует свечи для всех запрошенных интервалов.
Клиенту не требуется самостоятельно строить 5m, 15m или другие свечи из минутных данных.
Сравнение REST и WebSocket
Во время исследования одновременно выполнялись:
- периодические запросы REST;
- постоянная подписка WebSocket.
Это позволило определить различия поведения.
Поведение REST
Запрос
GET /api/v1/klines
возвращает последние свечи.
Последняя свеча является текущей, поэтому её значения изменяются по мере поступления новых сделок.
Например
21:01:41
Open 64047.45
High 64074.90
Low 64042.25
Close 64074.90
Volume 111
Через некоторое время
21:01:56
High 64095.45
Close 64095.45
Volume 153
Ещё позже
21:02:11
High 64099.10
Close 64098.00
Volume 165
Таким образом REST показывает формирующуюся свечу.
Поведение WebSocket
Для этой же свечи был получен
{
"t":1784224860000,
"o":64047.45,
"h":64099.10,
"l":64042.25,
"c":64098.00
}
WebSocket опубликовал событие только после окончания интервала.
Таким образом опубликована уже окончательная свеча.
Главное различие
REST
может вернуть
ещё незавершённую свечу
WebSocket
публикует только
закрытую свечу
Именно это делает WebSocket очень полезным источником событий.
Исследование Volume
Во всех событиях WebSocket отсутствует
volume
Например
{
"payload":{
"o":64096.20,
"h":64110.60,
"l":64045.45,
"c":64052.50
}
}
Поле
volume
не передаётся.
Однако REST возвращает
389
для этой же свечи.
Следовательно
WebSocket публикует
OHLC
но не
OHLCV
Ограничения использования
Текущая каноническая модель проекта
Candle
содержит
volume: Decimal
Следовательно WebSocket-событие не может напрямую быть преобразовано в канонический Candle.
Заполнение
volume = 0
является архитектурно неверным, поскольку означает "объём равен нулю", хотя на самом деле объём неизвестен.
Поэтому WebSocket не должен непосредственно создавать канонический Candle.
Архитектурное решение
На основании проведённого исследования для Build 059 принято решение использовать гибридную схему.
OHLCMarketData.subscribe
│
▼
получено событие закрытия свечи
│
▼
REST GET /klines
(symbol + interval + open_time)
│
▼
получена окончательная OHLCV свеча
│
▼
Validation
│
▼
Parser
│
▼
Mapper
│
▼
Canonical Candle
Таким образом
WebSocket становится
источником события
о закрытии свечи
а REST
остаётся
авторитетным источником
полной OHLCV модели.
Новая транспортная модель
Для Build 059 рекомендуется создать отдельную transport-модель
DzengiWebSocketOhlcEvent
например
@dataclass(frozen=True, slots=True)
class DzengiWebSocketOhlcEvent:
symbol: str
interval: str
candle_type: str
open_time: int
open_price: DzengiRawNumeric
high_price: DzengiRawNumeric
low_price: DzengiRawNumeric
close_price: DzengiRawNumeric
Она не должна заменять каноническую модель Candle.
Её назначение —
представление транспортного WebSocket-события.
Итоги исследования
Подтверждено
✓ WebSocket полностью работоспособен.
✓ Поддерживает несколько инструментов.
✓ Поддерживает несколько интервалов.
✓ Поддерживает
classic
и
heikin-ashi
✓ Публикует только завершённые свечи.
✓ Формат classic совпадает с REST bid OHLC.
✓ Передаёт только OHLC.
✓ Не содержит Volume.
✓ Может использоваться как триггер появления новой закрытой свечи.
Вывод Build 058
Исследование показало, что
OHLCMarketData.subscribe
не является заменой REST Candles Feed.
Однако данный поток является идеальным механизмом для своевременного обнаружения момента закрытия новой свечи.
Именно такая схема будет реализована в следующем этапе проекта.
Следующий Build
Build 059
Интеграция WebSocket OHLC
в Candles Feed
Основные задачи Build 059
-
создать транспортную модель DzengiWebSocketOhlcEvent;
-
реализовать schema validation;
-
реализовать parser;
-
реализовать value validation;
-
реализовать mapper;
-
добавить WebSocket Adapter;
-
интегрировать новый источник в Candles Feed;
-
использовать WebSocket как источник события закрытия свечи;
-
использовать REST для получения окончательной OHLCV свечи;
-
сохранить существующую каноническую модель Candle без изменений.
Работа с диагностическим скриптом
В рамках Build 058 был разработан отдельный диагностический инструмент
scripts/check_ohlc_websocket.py
Назначение скрипта:
- проверка доступности WebSocket;
- проверка подписки OHLCMarketData.subscribe;
- получение ACK;
- получение реальных OHLC-событий;
- проверка нескольких символов;
- проверка нескольких интервалов;
- проверка типа свечей;
- исследование поведения API после изменений на стороне биржи.
Скрипт не является частью Production-кода и предназначен исключительно для инженерной диагностики.
Проверка синтаксиса
python -m compileall \
scripts/check_ohlc_websocket.py
Проверка оформления
git diff --check
Запуск
EXCHANGE_BASE_URL=https://api-adapter.dzengi.com \
EXCHANGE_WS_URL=wss://api-adapter.dzengi.com \
EXCHANGE_API_KEY="" \
PYTHONPATH=. \
python scripts/check_ohlc_websocket.py
Настраиваемые параметры
В начале файла можно изменять
SYMBOLS = (
"BTC/USD_LEVERAGE",
)
для исследования нескольких инструментов.
Например
SYMBOLS = (
"BTC/USD_LEVERAGE",
"ETH/USD_LEVERAGE",
)
Можно изменять интервалы
INTERVALS = (
"1m",
)
например
INTERVALS = (
"1m",
"5m",
)
Можно выбирать тип свечей
CANDLE_TYPE = "classic"
или
CANDLE_TYPE = "heikin-ashi"
Также можно изменять
MAX_MESSAGES
и
TOTAL_TIMEOUT_SECONDS
для длительного наблюдения.
Что выводит скрипт
После запуска отображаются
- URL подключения;
- список инструментов;
- интервалы;
- тип свечей;
- параметры исследования.
Затем выводится отправляемый JSON подписки.
После подключения отображается ACK.
Например
{
"subscriptions": {
"BTC/USD_LEVERAGE:1m": "PROCESSED"
}
}
После этого выводятся все события
ohlc.event
с указанием
- номера сообщения;
- времени от начала подключения;
- полного JSON.
Проверка нескольких интервалов
При использовании
INTERVALS = (
"1m",
"5m",
)
ожидается ACK
{
"subscriptions": {
"BTC/USD_LEVERAGE:1m": "PROCESSED",
"BTC/USD_LEVERAGE:5m": "PROCESSED"
}
}
и последующее получение событий
interval = 1m
и
interval = 5m
в одном WebSocket-соединении.
Проверка Heikin-Ashi
Для исследования синтетических свечей достаточно изменить
CANDLE_TYPE = "heikin-ashi"
После этого ожидаются события
{
"type":"heikin-ashi"
}
Контракт полностью совпадает с классическими свечами, однако значения OHLC рассчитываются по алгоритму Heikin-Ashi.
Использование в дальнейшем
Скрипт рекомендуется сохранять в репозитории проекта.
Он может использоваться
- после изменений API Dzengi;
- после обновления WebSocket;
- при регрессионном тестировании;
- при диагностике Production;
- при разработке новых адаптеров;
- при интеграции новых таймфреймов.
Наличие отдельного диагностического инструмента значительно упрощает проверку реального контракта биржи без запуска всего приложения Dzentra.
Итог Build 058
Build 058 полностью подтвердил контракт WebSocket OHLCMarketData.subscribe на реальном Production API Dzengi.
Полученные результаты являются основанием для перехода к следующему этапу проекта —
Build 059
Интеграция WebSocket OHLC
в канонический Candles Feed.