Files
dzentra_bot/docs/migrations/build_060_9.md

29 KiB
Raw Blame History

Build 060.9 — WebSocket Trade Transport Model

Engineering Migration Report


Контроль документа

Свойство Значение
Build 060.9
Название WebSocket Trade Transport Model
Статус Completed
Проект Dzentra
Подсистема Market Data Acquisition
Компонент Trades Feed
Версия 1.0

Цель Build

После завершения Build 060.8 система получила источник получения сырых REST-документов Trade (DzengiTradesDocumentSource).

Следующим этапом развития является построение полного конвейера обработки сделок, поступающих по WebSocket.

Build 060.9 открывает вторую ветку реализации Trades Feed и вводит транспортную модель, описывающую одно WebSocket-событие биржи.

Данный Build ограничивается исключительно транспортным уровнем (Transport Layer) и не затрагивает Parser, Schema Validation, Value Validation, Mapper, Adapter, Routing и Runtime.


Предпосылки

К моменту начала Build архитектура Dzentra уже содержала транспортные модели для остальных типов рыночных данных.

REST Trades

REST Document
        │
        ▼
DzengiRestAggTrade
        │
        ▼
Trade

WebSocket Quote

WebSocket Quote Document
        │
        ▼
DzengiWebSocketQuoteResponse
        │
        ▼
Quote

WebSocket OHLC

WebSocket OHLC Document
        │
        ▼
DzengiWebSocketOhlcEvent
        │
        ▼
Candle

Для WebSocket Trade аналогичная транспортная модель отсутствовала.

В результате архитектура обработки сделок оставалась неполной и асимметричной относительно остальных типов рыночных данных.


Архитектурное основание

Build 060.9 не проектирует транспортный контракт самостоятельно.

В качестве первичного источника использованы результаты инженерного исследования, выполненного в рамках Build 057.

Во время исследования были изучены:

  • REST aggTrades;
  • WebSocket trades.subscribe;
  • реальные Production-сообщения;
  • соответствие REST и WebSocket контрактов.

Исследование подтвердило фактический формат события:

{
    "status": "OK",
    "destination": "internal.trade",
    "payload": {
        "buyer": false,
        "id": 2134846831,
        "orderId": "00a02503-0079-54c4-0000-000081e62b58",
        "price": 64555.55,
        "size": 0.002,
        "symbol": "BTC/USD_LEVERAGE",
        "ts": 1784218012030
    }
}

Именно этот контракт принят за основу реализации Build 060.9.


Результаты архитектурного аудита

Перед началом реализации был выполнен аудит существующего адаптера Dzengi.

Подтверждено наличие транспортных моделей:

  • DzengiRestAggTrade;
  • DzengiWebSocketQuoteResponse;
  • DzengiWebSocketOhlcEvent.

Также подтверждено существование полной иерархии ошибок обработки Trade:

  • TradeTransportError;
  • TradeSchemaError;
  • TradeParseError;
  • TradeValueError;
  • TradeMappingError.

Одновременно подтверждено отсутствие следующих компонентов WebSocket Trade Pipeline:

  • Transport Model;
  • Schema Validation;
  • Parser;
  • Value Validation;
  • Mapper;
  • Adapter.

Таким образом Build 060.9 полностью соответствует утверждённой дорожной карте серии 060 и закрывает первый этап WebSocket-ветки обработки сделок.


Архитектурное решение

В транспортный слой адаптера введена новая модель

DzengiWebSocketTradeEvent

Она представляет собой типизированное описание одного события

destination = internal.trade

и относится исключительно к транспортному уровню адаптера.

Модель не является:

  • внутренней моделью Dzentra;
  • бизнес-сущностью;
  • канонической моделью Trade.

Её единственная ответственность — хранение уже разобранных транспортных данных WebSocket-сообщения.


Реализованная транспортная модель

В файл

src/market_data/acquisition/adapters/dzengi/models.py

добавлена новая транспортная модель:

@dataclass(frozen=True, slots=True)
class DzengiWebSocketTradeEvent:
    trade_id: int
    price: DzengiRawNumeric
    size: DzengiRawNumeric
    timestamp: int
    symbol: str
    buyer: bool
    order_id: str

Модель размещена рядом с существующими транспортными моделями адаптера.

Итоговая последовательность моделей выглядит следующим образом:

DzengiRestAggTrade
DzengiWebSocketTradeEvent
DzengiWebSocketOhlcEvent

Такое расположение сохраняет логическую группировку транспортных сущностей по типам рыночных данных.


Почему используется dataclass

DzengiWebSocketTradeEvent представляет собой неизменяемый контейнер транспортных данных.

Модель:

  • не содержит бизнес-логики;
  • не выполняет преобразование типов;
  • не выполняет структурную проверку;
  • не выполняет Value Validation;
  • не выполняет Mapping;
  • не взаимодействует с сетью;
  • не содержит изменяемого состояния.

Поэтому используется конструкция

@dataclass(frozen=True, slots=True)

что полностью соответствует архитектурному стилю остальных транспортных моделей проекта.


Назначение полей

В модель включены только поля, подтверждённые реальными Production-сообщениями.

Поле Назначение
trade_id идентификатор сделки
price цена сделки
size объём сделки
timestamp время исполнения
symbol торговый инструмент
buyer сторона инициатора
order_id идентификатор ордера

Каждое поле соответствует данным, полученным в ходе исследования Build 057.


Нормализация имён полей

Во входящем WebSocket JSON используются ключи

id
ts
orderId

Во внутренней транспортной модели используются нормализованные имена

trade_id
timestamp
order_id

Подобная нормализация уже используется в остальных транспортных моделях Dzentra и обеспечивает единый стиль внутренних контрактов.

Преобразование JSON-ключей выполняется Parser.

Транспортная модель не зависит от формата сериализации входящего сообщения.


Использование DzengiRawNumeric

Поля

price
size

имеют тип

DzengiRawNumeric

Такое решение уже применяется в REST Trade и других транспортных моделях адаптера.

Использование DzengiRawNumeric означает, что транспортная модель сохраняет числовые значения в исходном виде без выполнения каких-либо преобразований.

Ответственность за интерпретацию числовых значений относится к последующим этапам конвейера.

Это позволяет полностью разделить:

  • транспортное представление данных;
  • их синтаксическую корректность;
  • семантическую валидацию;
  • преобразование в каноническую модель.

Неизменяемость модели

Параметр

frozen=True

гарантирует, что после создания экземпляра его поля не могут быть изменены.

Это важно для транспортного слоя, поскольку модель представляет собой результат разбора одного входящего сообщения и после создания должна оставаться неизменной.

После формирования экземпляр проходит через последующие этапы конвейера:

Validated WebSocket Document
            │
            ▼
DzengiWebSocketTradeEvent
            │
            ▼
Value Validation
            │
            ▼
Mapper
            │
            ▼
Trade

Неизменяемость транспортной модели обеспечивает воспроизводимость обработки сообщения и исключает случайную модификацию данных на последующих этапах.


Использование slots

Параметр

slots=True

используется для:

  • фиксации структуры модели;
  • предотвращения динамического добавления атрибутов;
  • уменьшения накладных расходов на экземпляр;
  • сохранения единого архитектурного стиля транспортных моделей Dzentra.

Экземпляр DzengiWebSocketTradeEvent не содержит __dict__, что дополнительно подтверждает его роль как лёгкого транспортного контейнера.


Граница ответственности модели

DzengiWebSocketTradeEvent отвечает исключительно за хранение уже разобранных транспортных данных.

В обязанности модели не входит:

  • проверка структуры исходного JSON;
  • проверка значения status;
  • проверка destination;
  • проверка наличия обязательных полей;
  • проверка корректности цены;
  • проверка корректности объёма;
  • проверка корректности timestamp;
  • определение бизнес-семантики сделки;
  • преобразование в каноническую модель Trade.

Каждая из перечисленных задач относится к отдельному уровню архитектуры и будет реализована в соответствующих Build.


Целевой конвейер обработки WebSocket Trade

После завершения Builds 060.9060.14 полный конвейер обработки будет иметь следующий вид:

Raw WebSocket Object
        │
        ▼
WebSocket Trade Schema Validation
        │
        ▼
ValidatedWebSocketTradeDocument
        │
        ▼
WebSocket Trade Parser
        │
        ▼
DzengiWebSocketTradeEvent
        │
        ▼
WebSocket Trade Value Validation
        │
        ▼
WebSocket Trade Mapper
        │
        ▼
Trade

Build 060.9 реализует только один компонент этого конвейера:

DzengiWebSocketTradeEvent

Все остальные этапы будут реализованы последовательно в следующих Build.


Соотношение с канонической моделью Trade

В Build 060.1 была реализована каноническая модель

Trade

Она используется внутренними компонентами Dzentra и полностью независима от способа получения рыночных данных.

DzengiWebSocketTradeEvent не заменяет Trade.

Эти модели относятся к различным архитектурным уровням.

DzengiWebSocketTradeEvent

— транспортное представление WebSocket-события конкретной биржи.

Trade

— единая внутренняя модель сделки, используемая всеми компонентами Dzentra.

После реализации Mapper транспортная модель будет преобразовываться в каноническую.


Соотношение REST и WebSocket транспортных моделей

REST и WebSocket описывают одну и ту же биржевую сделку, но используют различные транспортные контракты.

REST:

{
    "a": 2134846831,
    "p": "64555.55",
    "q": "0.002",
    "T": 1784218012030,
    "m": true
}

WebSocket:

{
    "id": 2134846831,
    "price": 64555.55,
    "size": 0.002,
    "symbol": "BTC/USD_LEVERAGE",
    "ts": 1784218012030,
    "buyer": false,
    "orderId": "00a02503-0079-54c4-0000-000081e62b58"
}

Несмотря на различие транспортных контрактов, обе модели описывают одну и ту же биржевую сущность и далее преобразуются в единую каноническую модель Trade.


Сопоставление полей REST и WebSocket

Каноническая семантика REST WebSocket
идентификатор сделки a id
цена p price
объём q size
timestamp T ts
сторона инициатора m buyer
торговый инструмент отсутствует symbol
идентификатор ордера отсутствует orderId

Согласно результатам исследования Build 057 выполняется соответствие:

buyer == !m

Данная нормализация будет реализована WebSocket Trade Mapper в Build 060.13.


Обработка транспортной оболочки сообщения

Фактическое WebSocket-сообщение имеет следующую структуру:

{
    "status": "OK",
    "destination": "internal.trade",
    "payload": {
        "...": "..."
    }
}

DzengiWebSocketTradeEvent представляет исключительно содержимое объекта payload.

Поля

status
destination

не входят в транспортную модель, поскольку относятся к внешней оболочке WebSocket-документа.

Их обработка будет реализована в Build 060.10 — WebSocket Trade Schema Validation.


Решение по полю clientOrderId

Во время исследования было обнаружено, что Swagger содержит дополнительное поле

clientOrderId

Однако анализ реальных Production-сообщений подтвердил обязательное наличие только поля

orderId

Поэтому clientOrderId не включён в обязательный транспортный контракт Build 060.9.

Это исключает зависимость Parser от поля, которое отсутствует в фактических сообщениях биржи.

При реализации Schema Validation будут использоваться следующие принципы:

  • обязательными считаются только поля, подтверждённые Production;
  • наличие дополнительных полей не должно приводить к ошибке;
  • неподтверждённые поля не становятся обязательными автоматически.

Изменённые файлы

В рамках Build были изменены только два файла.

Транспортная модель

src/market_data/acquisition/adapters/dzengi/models.py

Добавлена новая модель

DzengiWebSocketTradeEvent

При этом существующие модели, импорты и поведение файла не изменялись.


Unit-тесты

tests/unit/market_data/acquisition/adapters/dzengi/test_models.py

Добавлены три новых unit-теста транспортной модели.


Добавленные тесты

Проверка полного транспортного контракта

Тест

test_websocket_trade_event_stores_complete_transport_contract

проверяет сохранение всех подтверждённых полей транспортной модели.


Проверка сохранения исходных числовых значений

Тест

test_websocket_trade_event_preserves_raw_numeric_values

подтверждает, что модель сохраняет цену и объём без преобразования типов.


Проверка неизменяемости и slots

Тест

test_websocket_trade_event_is_immutable_and_uses_slots

подтверждает:

  • отсутствие __dict__;
  • невозможность изменения экземпляра после создания.

Результаты тестирования

Выполнен целевой запуск unit-тестов транспортных моделей:

python -m pytest \
  tests/unit/market_data/acquisition/adapters/dzengi/test_models.py \
  -q

Результат выполнения:

12 passed in 0.03s

До реализации Build 060.9 файл содержал девять тестов.

После добавления транспортной модели количество тестов увеличилось:

9 → 12

что соответствует трём новым проверкам.


Регрессионное тестирование

После завершения реализации был выполнен полный запуск тестов адаптера Dzengi.

python -m pytest \
  tests/unit/market_data/acquisition/adapters/dzengi \
  -q

Результат:

246 passed in 0.11s

До реализации Build 060.9 набор содержал:

243 passed

После добавления новой транспортной модели:

246 passed

Регрессий не обнаружено.


Проверка компиляции

Выполнена проверка компиляции изменённых файлов:

python -m compileall \
  src/market_data/acquisition/adapters/dzengi/models.py \
  tests/unit/market_data/acquisition/adapters/dzengi/test_models.py

Компиляция завершилась успешно.

Синтаксические ошибки отсутствуют.


Проверка Git diff

Финальный анализ изменений подтвердил, что Build затронул только согласованный scope.

Добавлены:

  • одна транспортная модель;
  • три unit-теста.

Не изменялись:

src/market_data/acquisition/adapters/dzengi/parser.py
src/market_data/acquisition/adapters/dzengi/mapper.py
src/market_data/acquisition/adapters/dzengi/websocket.py
src/market_data/acquisition/validation/schema.py
src/market_data/acquisition/validation/values.py
src/market_data/acquisition/exceptions.py
src/market_data/acquisition/runtime/
src/market_data/acquisition/feeds/
src/market_data/acquisition/handlers/

Таким образом Build полностью соответствует принципу локальности изменений.


Scope Build 060.9

В рамках данного Build реализовано только:

WebSocket Trade Transport Model

В Build не входят:

  • WebSocket Trade Schema Validation;
  • WebSocket Trade Parser;
  • WebSocket Trade Value Validation;
  • WebSocket Trade Mapper;
  • WebSocket Trade Adapter;
  • Unified WebSocket Routing;
  • Trade Subscription Layer;
  • Trades Feed Core;
  • Runtime Integration.

Такое разделение обеспечивает атомарность миграции и позволяет независимо проверять каждый архитектурный уровень.


Архитектурный результат

После завершения Build система содержит транспортные представления обеих форм получения сделок.

REST:

REST Trade Document
        │
        ▼
DzengiRestAggTrade
        │
        ▼
Trade

WebSocket:

WebSocket Trade Document
        │
        ▼
DzengiWebSocketTradeEvent
        │
        ▼
Trade

На текущем этапе WebSocket-ветка содержит только транспортную модель.

Остальные уровни конвейера будут добавлены последовательно в следующих Build.


Состояние WebSocket Trade Pipeline

После завершения Build 060.9 архитектура конвейера выглядит следующим образом:

Raw WebSocket Trade Document
        │
        ▼
Schema Validation
        │
        ▼
Parser
        │
        ▼
DzengiWebSocketTradeEvent
        │
        ▼
Value Validation
        │
        ▼
Mapper
        │
        ▼
Trade

Статус реализации компонентов:

Компонент Build Статус
Canonical Trade Model 060.1 ✔ Completed
WebSocket Trade Transport Model 060.9 ✔ Completed
WebSocket Trade Schema Validation 060.10 Pending
WebSocket Trade Parser 060.11 Pending
WebSocket Trade Value Validation 060.12 Pending
WebSocket Trade Mapper 060.13 Pending
WebSocket Trade Adapter 060.14 Pending
Unified WebSocket Routing 060.15 Pending

Соблюдение архитектурных принципов

В рамках реализации Build сохранены все архитектурные инварианты проекта Dzentra.

Локальность изменений

Изменены только транспортная модель и её unit-тесты.


Отсутствие преждевременной интеграции

Новая модель не подключена к Adapter, Runtime и Trades Feed до появления всех промежуточных этапов обработки.


Разделение транспортной и канонической моделей

DzengiWebSocketTradeEvent используется только внутри транспортного слоя адаптера.

Внутренние компоненты системы продолжают работать исключительно с канонической моделью Trade.


Отсутствие бизнес-логики

Транспортная модель не выполняет:

  • Parsing;
  • Schema Validation;
  • Value Validation;
  • Mapping;
  • Routing;
  • Subscription;
  • Network I/O.

Она представляет собой исключительно неизменяемый контейнер транспортных данных.


Неизменность существующего поведения

Рабочая логика обработки Quote, OHLC и REST Trade не изменялась.

Build является полностью обратимо-совместимым с существующей архитектурой.


Критерии завершения Build

Build 060.9 считается завершённым, поскольку выполнены все поставленные задачи.

  • ✔ исследован Production-контракт WebSocket Trade;
  • ✔ использованы результаты Build 057;
  • ✔ реализована транспортная модель DzengiWebSocketTradeEvent;
  • ✔ модель содержит все подтверждённые поля события;
  • ✔ цена и объём сохраняются как DzengiRawNumeric;
  • ✔ модель является неизменяемой;
  • ✔ используется slots;
  • ✔ добавлены три unit-теста;
  • ✔ все целевые тесты успешно проходят;
  • ✔ регрессионное тестирование успешно завершено;
  • ✔ компиляция выполнена без ошибок;
  • ✔ изменения не выходят за пределы согласованного scope.

Следующий этап

Следующим этапом дорожной карты является

Build 060.10 — WebSocket Trade Schema Validation

Цель Build:

  • реализовать структурную проверку WebSocket Trade-документа;
  • проверить транспортную оболочку сообщения;
  • проверить status;
  • проверить destination = internal.trade;
  • проверить наличие объекта payload;
  • проверить наличие обязательных ключей транспортного события;
  • сформировать ValidatedWebSocketTradeDocument;
  • использовать TradeSchemaError для ошибок структуры.

После завершения Build 060.10 конвейер примет следующий вид:

Raw WebSocket Object
        │
        ▼
validate_dzengi_websocket_trade_schema(...)
        │
        ▼
ValidatedWebSocketTradeDocument
        │
        ▼
Build 060.11 — WebSocket Trade Parser

Build 060.10 по-прежнему не будет выполнять:

  • преобразование значений;
  • семантическую валидацию;
  • Mapping в Trade;
  • WebSocket Routing;
  • Runtime Integration.

Итог

Build 060.9 завершил формирование транспортного уровня обработки WebSocket Trade, добавив отсутствующую транспортную модель DzengiWebSocketTradeEvent.

Новая модель представляет собой типизированное описание события

destination = internal.trade

и основана на фактическом Production-контракте, подтверждённом в ходе исследования Build 057.

Реализация полностью соответствует архитектурным принципам Dzentra:

  • транспортный слой отделён от канонической модели;
  • Build ограничен согласованным scope;
  • существующее поведение системы не изменено;
  • создан фундамент для последующей реализации WebSocket Trade Schema Validation, Parser, Mapper и полного конвейера Trades Feed.

На этом Build 060.9 считается полностью завершённым.