29 KiB
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.9–060.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 считается полностью завершённым.