Files
dzentra_bot/docs/migrations/build_058.md

21 KiB
Raw Permalink Blame History

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.