Files
dzentra_bot/docs/migrations/build_060_9.md

957 lines
29 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```text
REST Document
DzengiRestAggTrade
Trade
```
## WebSocket Quote
```text
WebSocket Quote Document
DzengiWebSocketQuoteResponse
Quote
```
## WebSocket OHLC
```text
WebSocket OHLC Document
DzengiWebSocketOhlcEvent
Candle
```
Для WebSocket Trade аналогичная транспортная модель отсутствовала.
В результате архитектура обработки сделок оставалась неполной и асимметричной относительно остальных типов рыночных данных.
---
# Архитектурное основание
Build 060.9 не проектирует транспортный контракт самостоятельно.
В качестве первичного источника использованы результаты инженерного исследования, выполненного в рамках Build 057.
Во время исследования были изучены:
- REST `aggTrades`;
- WebSocket `trades.subscribe`;
- реальные Production-сообщения;
- соответствие REST и WebSocket контрактов.
Исследование подтвердило фактический формат события:
```json
{
"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-ветки обработки сделок.
---
# Архитектурное решение
В транспортный слой адаптера введена новая модель
```text
DzengiWebSocketTradeEvent
```
Она представляет собой типизированное описание одного события
```text
destination = internal.trade
```
и относится исключительно к транспортному уровню адаптера.
Модель не является:
- внутренней моделью Dzentra;
- бизнес-сущностью;
- канонической моделью `Trade`.
Её единственная ответственность — хранение уже разобранных транспортных данных WebSocket-сообщения.
---
# Реализованная транспортная модель
В файл
```text
src/market_data/acquisition/adapters/dzengi/models.py
```
добавлена новая транспортная модель:
```python
@dataclass(frozen=True, slots=True)
class DzengiWebSocketTradeEvent:
trade_id: int
price: DzengiRawNumeric
size: DzengiRawNumeric
timestamp: int
symbol: str
buyer: bool
order_id: str
```
Модель размещена рядом с существующими транспортными моделями адаптера.
Итоговая последовательность моделей выглядит следующим образом:
```text
DzengiRestAggTrade
DzengiWebSocketTradeEvent
DzengiWebSocketOhlcEvent
```
Такое расположение сохраняет логическую группировку транспортных сущностей по типам рыночных данных.
---
# Почему используется dataclass
`DzengiWebSocketTradeEvent` представляет собой неизменяемый контейнер транспортных данных.
Модель:
- не содержит бизнес-логики;
- не выполняет преобразование типов;
- не выполняет структурную проверку;
- не выполняет Value Validation;
- не выполняет Mapping;
- не взаимодействует с сетью;
- не содержит изменяемого состояния.
Поэтому используется конструкция
```python
@dataclass(frozen=True, slots=True)
```
что полностью соответствует архитектурному стилю остальных транспортных моделей проекта.
---
# Назначение полей
В модель включены только поля, подтверждённые реальными Production-сообщениями.
| Поле | Назначение |
|------|------------|
| `trade_id` | идентификатор сделки |
| `price` | цена сделки |
| `size` | объём сделки |
| `timestamp` | время исполнения |
| `symbol` | торговый инструмент |
| `buyer` | сторона инициатора |
| `order_id` | идентификатор ордера |
Каждое поле соответствует данным, полученным в ходе исследования Build 057.
---
# Нормализация имён полей
Во входящем WebSocket JSON используются ключи
```text
id
ts
orderId
```
Во внутренней транспортной модели используются нормализованные имена
```text
trade_id
timestamp
order_id
```
Подобная нормализация уже используется в остальных транспортных моделях Dzentra и обеспечивает единый стиль внутренних контрактов.
Преобразование JSON-ключей выполняется Parser.
Транспортная модель не зависит от формата сериализации входящего сообщения.
---
# Использование DzengiRawNumeric
Поля
```python
price
size
```
имеют тип
```python
DzengiRawNumeric
```
Такое решение уже применяется в REST Trade и других транспортных моделях адаптера.
Использование `DzengiRawNumeric` означает, что транспортная модель сохраняет числовые значения в исходном виде без выполнения каких-либо преобразований.
Ответственность за интерпретацию числовых значений относится к последующим этапам конвейера.
Это позволяет полностью разделить:
- транспортное представление данных;
- их синтаксическую корректность;
- семантическую валидацию;
- преобразование в каноническую модель.
# Неизменяемость модели
Параметр
```python
frozen=True
```
гарантирует, что после создания экземпляра его поля не могут быть изменены.
Это важно для транспортного слоя, поскольку модель представляет собой результат разбора одного входящего сообщения и после создания должна оставаться неизменной.
После формирования экземпляр проходит через последующие этапы конвейера:
```text
Validated WebSocket Document
DzengiWebSocketTradeEvent
Value Validation
Mapper
Trade
```
Неизменяемость транспортной модели обеспечивает воспроизводимость обработки сообщения и исключает случайную модификацию данных на последующих этапах.
---
# Использование slots
Параметр
```python
slots=True
```
используется для:
- фиксации структуры модели;
- предотвращения динамического добавления атрибутов;
- уменьшения накладных расходов на экземпляр;
- сохранения единого архитектурного стиля транспортных моделей Dzentra.
Экземпляр `DzengiWebSocketTradeEvent` не содержит `__dict__`, что дополнительно подтверждает его роль как лёгкого транспортного контейнера.
---
# Граница ответственности модели
`DzengiWebSocketTradeEvent` отвечает исключительно за хранение уже разобранных транспортных данных.
В обязанности модели **не входит**:
- проверка структуры исходного JSON;
- проверка значения `status`;
- проверка `destination`;
- проверка наличия обязательных полей;
- проверка корректности цены;
- проверка корректности объёма;
- проверка корректности timestamp;
- определение бизнес-семантики сделки;
- преобразование в каноническую модель `Trade`.
Каждая из перечисленных задач относится к отдельному уровню архитектуры и будет реализована в соответствующих Build.
---
# Целевой конвейер обработки WebSocket Trade
После завершения Builds 060.9060.14 полный конвейер обработки будет иметь следующий вид:
```text
Raw WebSocket Object
WebSocket Trade Schema Validation
ValidatedWebSocketTradeDocument
WebSocket Trade Parser
DzengiWebSocketTradeEvent
WebSocket Trade Value Validation
WebSocket Trade Mapper
Trade
```
Build 060.9 реализует только один компонент этого конвейера:
```text
DzengiWebSocketTradeEvent
```
Все остальные этапы будут реализованы последовательно в следующих Build.
---
# Соотношение с канонической моделью Trade
В Build 060.1 была реализована каноническая модель
```text
Trade
```
Она используется внутренними компонентами Dzentra и полностью независима от способа получения рыночных данных.
`DzengiWebSocketTradeEvent` не заменяет `Trade`.
Эти модели относятся к различным архитектурным уровням.
```text
DzengiWebSocketTradeEvent
```
— транспортное представление WebSocket-события конкретной биржи.
```text
Trade
```
— единая внутренняя модель сделки, используемая всеми компонентами Dzentra.
После реализации Mapper транспортная модель будет преобразовываться в каноническую.
---
# Соотношение REST и WebSocket транспортных моделей
REST и WebSocket описывают одну и ту же биржевую сделку, но используют различные транспортные контракты.
REST:
```json
{
"a": 2134846831,
"p": "64555.55",
"q": "0.002",
"T": 1784218012030,
"m": true
}
```
WebSocket:
```json
{
"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 выполняется соответствие:
```text
buyer == !m
```
Данная нормализация будет реализована WebSocket Trade Mapper в Build 060.13.
---
# Обработка транспортной оболочки сообщения
Фактическое WebSocket-сообщение имеет следующую структуру:
```json
{
"status": "OK",
"destination": "internal.trade",
"payload": {
"...": "..."
}
}
```
`DzengiWebSocketTradeEvent` представляет исключительно содержимое объекта `payload`.
Поля
```text
status
destination
```
не входят в транспортную модель, поскольку относятся к внешней оболочке WebSocket-документа.
Их обработка будет реализована в Build 060.10 — WebSocket Trade Schema Validation.
---
# Решение по полю clientOrderId
Во время исследования было обнаружено, что Swagger содержит дополнительное поле
```text
clientOrderId
```
Однако анализ реальных Production-сообщений подтвердил обязательное наличие только поля
```text
orderId
```
Поэтому `clientOrderId` не включён в обязательный транспортный контракт Build 060.9.
Это исключает зависимость Parser от поля, которое отсутствует в фактических сообщениях биржи.
При реализации Schema Validation будут использоваться следующие принципы:
- обязательными считаются только поля, подтверждённые Production;
- наличие дополнительных полей не должно приводить к ошибке;
- неподтверждённые поля не становятся обязательными автоматически.
---
# Изменённые файлы
В рамках Build были изменены только два файла.
## Транспортная модель
```text
src/market_data/acquisition/adapters/dzengi/models.py
```
Добавлена новая модель
```text
DzengiWebSocketTradeEvent
```
При этом существующие модели, импорты и поведение файла не изменялись.
---
## Unit-тесты
```text
tests/unit/market_data/acquisition/adapters/dzengi/test_models.py
```
Добавлены три новых unit-теста транспортной модели.
---
# Добавленные тесты
## Проверка полного транспортного контракта
Тест
```text
test_websocket_trade_event_stores_complete_transport_contract
```
проверяет сохранение всех подтверждённых полей транспортной модели.
---
## Проверка сохранения исходных числовых значений
Тест
```text
test_websocket_trade_event_preserves_raw_numeric_values
```
подтверждает, что модель сохраняет цену и объём без преобразования типов.
---
## Проверка неизменяемости и slots
Тест
```text
test_websocket_trade_event_is_immutable_and_uses_slots
```
подтверждает:
- отсутствие `__dict__`;
- невозможность изменения экземпляра после создания.
# Результаты тестирования
Выполнен целевой запуск unit-тестов транспортных моделей:
```bash
python -m pytest \
tests/unit/market_data/acquisition/adapters/dzengi/test_models.py \
-q
```
Результат выполнения:
```text
12 passed in 0.03s
```
До реализации Build 060.9 файл содержал девять тестов.
После добавления транспортной модели количество тестов увеличилось:
```text
9 → 12
```
что соответствует трём новым проверкам.
---
# Регрессионное тестирование
После завершения реализации был выполнен полный запуск тестов адаптера Dzengi.
```bash
python -m pytest \
tests/unit/market_data/acquisition/adapters/dzengi \
-q
```
Результат:
```text
246 passed in 0.11s
```
До реализации Build 060.9 набор содержал:
```text
243 passed
```
После добавления новой транспортной модели:
```text
246 passed
```
Регрессий не обнаружено.
---
# Проверка компиляции
Выполнена проверка компиляции изменённых файлов:
```bash
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-теста.
Не изменялись:
```text
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 реализовано только:
```text
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:
```text
REST Trade Document
DzengiRestAggTrade
Trade
```
WebSocket:
```text
WebSocket Trade Document
DzengiWebSocketTradeEvent
Trade
```
На текущем этапе WebSocket-ветка содержит только транспортную модель.
Остальные уровни конвейера будут добавлены последовательно в следующих Build.
---
# Состояние WebSocket Trade Pipeline
После завершения Build 060.9 архитектура конвейера выглядит следующим образом:
```text
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.
---
# Следующий этап
Следующим этапом дорожной карты является
```text
Build 060.10 — WebSocket Trade Schema Validation
```
Цель Build:
- реализовать структурную проверку WebSocket Trade-документа;
- проверить транспортную оболочку сообщения;
- проверить `status`;
- проверить `destination = internal.trade`;
- проверить наличие объекта `payload`;
- проверить наличие обязательных ключей транспортного события;
- сформировать `ValidatedWebSocketTradeDocument`;
- использовать `TradeSchemaError` для ошибок структуры.
После завершения Build 060.10 конвейер примет следующий вид:
```text
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`.
Новая модель представляет собой типизированное описание события
```text
destination = internal.trade
```
и основана на фактическом Production-контракте, подтверждённом в ходе исследования Build 057.
Реализация полностью соответствует архитектурным принципам Dzentra:
- транспортный слой отделён от канонической модели;
- Build ограничен согласованным scope;
- существующее поведение системы не изменено;
- создан фундамент для последующей реализации WebSocket Trade Schema Validation, Parser, Mapper и полного конвейера Trades Feed.
На этом Build 060.9 считается полностью завершённым.