32 KiB
Build 060.11 — WebSocket Trade Parser
Engineering Migration Report
Контроль документа
| Свойство | Значение |
|---|---|
| Build | 060.11 |
| Название | WebSocket Trade Parser |
| Статус | Completed |
| Проект | Dzentra |
| Подсистема | Market Data Acquisition |
| Компонент | Trades Feed |
| Версия | 1.0 |
Цель Build
После завершения Build 060.10 система получила завершённый уровень Schema Validation для WebSocket-событий канала internal.trade.
Структурная корректность входящего сообщения теперь гарантируется объектом
ValidatedWebSocketTradeDocument
Следующим обязательным этапом развития WebSocket-конвейера является реализация слоя Parser, отвечающего за преобразование структурно корректного документа в транспортную модель адаптера Dzengi.
Build 060.11 вводит механизм WebSocket Trade Parser.
Основная задача Build — изолировать знания о транспортном формате WebSocket-сообщения внутри Parser и предоставить последующим уровням Pipeline готовую transport-модель.
Данный Build ограничивается исключительно транспортным преобразованием документа и не затрагивает:
- Schema Validation;
- Value Validation;
- Mapper;
- Adapter;
- Runtime;
- Routing;
- Trades Feed.
Предпосылки
К моменту начала Build архитектура Dzentra уже содержала завершённые Parser для остальных типов WebSocket-событий.
WebSocket Quote
Raw WebSocket Quote
│
▼
Schema Validation
│
▼
ValidatedWebSocketQuoteDocument
│
▼
Quote Parser
│
▼
DzengiWebSocketQuoteResponse
WebSocket OHLC
Raw WebSocket OHLC
│
▼
Schema Validation
│
▼
ValidatedWebSocketOhlcDocument
│
▼
OHLC Parser
│
▼
DzengiWebSocketOhlcEvent
После завершения Build 060.10 появился документ
ValidatedWebSocketTradeDocument
однако между ним и транспортной моделью
DzengiWebSocketTradeEvent
отсутствовал собственный слой Parsing.
В результате WebSocket-конвейер обработки сделок оставался незавершённым и отличался от уже реализованных конвейеров Quote и OHLC.
Архитектурное основание
В архитектуре Dzentra каждый уровень Pipeline имеет строго определённую область ответственности.
Parser располагается между Schema Validation и Value Validation и отвечает исключительно за транспортное преобразование документа.
На данном уровне выполняется:
- извлечение обязательных полей из
payload; - минимальная проверка типов, необходимая для построения транспортной модели;
- переименование транспортных полей;
- создание immutable transport object.
Parser принципиально не выполняет:
- проверку диапазонов значений;
- проверку корректности timestamp;
- проверку корректности цены;
- проверку корректности объёма;
- бизнес-валидацию;
- Mapping;
- создание канонической модели
Trade.
Такое разделение позволяет каждому уровню Pipeline выполнять одну строго определённую задачу и исключает смешивание ответственности между компонентами.
Результаты архитектурного аудита
Перед началом реализации был выполнен аудит существующей подсистемы Parsing.
Подтверждено наличие полностью реализованных компонентов:
parse_dzengi_websocket_quote();parse_dzengi_websocket_ohlc();DzengiWebSocketQuoteResponse;DzengiWebSocketOhlcEvent.
Также подтверждено существование транспортной модели:
DzengiWebSocketTradeEvent
и завершённого уровня Schema Validation:
ValidatedWebSocketTradeDocument
Одновременно подтверждено отсутствие собственного Parser для WebSocket Trade.
Таким образом Build 060.11 полностью соответствует утверждённой дорожной карте серии 060 и закрывает третий этап WebSocket-ветки обработки сделок.
Архитектурное решение
По итогам аудита было принято решение не проектировать отдельную архитектуру для Trade.
Вместо этого реализован третий экземпляр уже существующего архитектурного шаблона.
В систему добавлена функция
parse_dzengi_websocket_trade(...)
которая преобразует
ValidatedWebSocketTradeDocument
в
DzengiWebSocketTradeEvent
Архитектура всех WebSocket-конвейеров стала полностью симметричной.
Quote
ValidatedWebSocketQuoteDocument
│
▼
Quote Parser
│
▼
DzengiWebSocketQuoteResponse
OHLC
ValidatedWebSocketOhlcDocument
│
▼
OHLC Parser
│
▼
DzengiWebSocketOhlcEvent
Trade
ValidatedWebSocketTradeDocument
│
▼
Trade Parser
│
▼
DzengiWebSocketTradeEvent
Build 060.11 не изменяет существующее поведение Quote и OHLC, а лишь расширяет существующую архитектуру новым типом рыночных данных.
Реализованный Parser
В файл
src/market_data/acquisition/adapters/dzengi/parser.py
добавлена новая функция транспортного преобразования:
parse_dzengi_websocket_trade(...)
Parser получает результат успешной структурной проверки
ValidatedWebSocketTradeDocument
и создаёт транспортную модель
DzengiWebSocketTradeEvent
Parser является единственным компонентом системы, который знает транспортный формат WebSocket-сообщения Dzengi.
Все последующие уровни Pipeline работают исключительно с транспортной моделью и полностью изолированы от структуры исходного JSON-документа.
Почему используется отдельный Parser
DzengiWebSocketTradeEvent не создаётся непосредственно во время Schema Validation.
Подобное разделение соответствует архитектурному принципу Dzentra, согласно которому каждый уровень Pipeline отвечает только за одну задачу.
Schema Validation гарантирует корректность структуры документа.
Parser преобразует структуру документа в транспортную модель.
Value Validation анализирует корректность самих значений.
Mapper преобразует транспортную модель в каноническую модель предметной области.
Такое разделение ответственности уже используется для Quote и OHLC и полностью повторено для Trade.
Входной документ Parser
Parser принимает единственный аргумент
ValidatedWebSocketTradeDocument
Документ гарантирует:
- корректную структуру транспортной оболочки;
- наличие объекта
payload; - наличие обязательных транспортных полей;
- неизменяемость содержимого
payload.
Благодаря этому Parser не выполняет повторную структурную проверку сообщения и не анализирует наличие обязательных полей.
Эти гарантии уже обеспечены предыдущим уровнем Pipeline.
Формируемая транспортная модель
Результатом успешной работы Parser является объект
@dataclass(frozen=True, slots=True)
class DzengiWebSocketTradeEvent:
trade_id: int
price: DzengiRawNumeric
size: DzengiRawNumeric
timestamp: int
symbol: str
buyer: bool
order_id: str
Экземпляр представляет собой неизменяемую транспортную модель одного события биржи.
Объект полностью соответствует транспортному контракту WebSocket Trade и используется последующими этапами обработки без дополнительного обращения к исходному JSON-документу.
Выполняемое преобразование полей
Parser извлекает значения исключительно из объекта
payload
При создании транспортной модели выполняется переименование отдельных транспортных полей.
| Поле WebSocket | Поле модели |
|---|---|
id |
trade_id |
price |
price |
size |
size |
symbol |
symbol |
ts |
timestamp |
buyer |
buyer |
orderId |
order_id |
Все остальные значения сохраняются без изменения.
Подобное переименование позволяет транспортной модели использовать единый стиль именования, принятый во всём проекте Dzentra.
Сохранение исходных значений
Build 060.11 принципиально не преобразует содержимое транспортных полей.
В частности Parser:
- не преобразует цену в
Decimal; - не преобразует объём в
Decimal; - не преобразует timestamp в
datetime; - не изменяет строковое представление символа;
- не интерпретирует направление сделки.
Все значения сохраняются в том виде, в котором они были получены от биржи.
Это позволяет полностью отделить транспортный уровень от уровня предметной валидации.
Почему Parser не переносит транспортную оболочку
В ходе архитектурного аудита отдельно рассматривался вопрос о необходимости переноса полей транспортной оболочки
status
destination
correlationId
в объект
DzengiWebSocketTradeEvent
По итогам анализа принято решение отказаться от подобного переноса.
После успешного завершения Schema Validation транспортная оболочка полностью выполняет свою задачу и больше не участвует в обработке рыночного события.
Последующие уровни Pipeline работают исключительно с содержимым объекта payload.
Таким образом транспортная модель содержит только данные, непосредственно описывающие совершённую сделку.
Подобный подход уже используется в существующих Parser для Quote и OHLC и полностью сохраняет архитектурную симметрию подсистемы Market Data Acquisition.
Использование TradeParseError
Для всех ошибок, возникающих на этапе Parsing, используется существующее исключение
TradeParseError
Build не вводит новых типов исключений.
Это сохраняет единую иерархию обработки ошибок Trade и полностью соответствует архитектуре Dzentra.
Parser использует TradeParseError исключительно для ошибок транспортного преобразования и проверки типов, необходимых для построения транспортной модели.
Ошибки предметной корректности значений остаются областью ответственности Build 060.12.
Целевой конвейер обработки WebSocket Trade
После завершения Build 060.11 конвейер обработки принимает следующий вид.
Raw WebSocket Object
│
▼
WebSocket Trade Schema Validation
│
▼
ValidatedWebSocketTradeDocument
│
▼
WebSocket Trade Parser
│
▼
DzengiWebSocketTradeEvent
│
▼
Build 060.12 — WebSocket Trade Value Validation
│
▼
Validated Trade Transport Event
│
▼
Build 060.13 — WebSocket Trade Mapper
│
▼
Trade
Таким образом Build 060.11 завершает третий архитектурный уровень WebSocket-конвейера обработки сделок.
Соотношение с предыдущим Build
Build 060.10 и Build 060.11 реализуют два различных архитектурных уровня.
Build 060.10
вводит документ
ValidatedWebSocketTradeDocument
который гарантирует структурную корректность входящего WebSocket-сообщения.
Build 060.11
вводит Parser
parse_dzengi_websocket_trade(...)
который преобразует проверенный документ в транспортную модель
DzengiWebSocketTradeEvent
Таким образом последовательность обработки становится следующей.
Raw JSON
│
▼
ValidatedWebSocketTradeDocument
│
▼
DzengiWebSocketTradeEvent
│
▼
Trade
Каждый компонент относится к собственному архитектурному уровню и не дублирует ответственность другого.
Изменённые файлы
В рамках Build были изменены только два файла.
Parser
src/market_data/acquisition/adapters/dzengi/parser.py
Добавлены:
parse_dzengi_websocket_trade(...)
_websocket_trade_required_string(...)
_websocket_trade_required_int(...)
_websocket_trade_required_bool(...)
_websocket_trade_required_raw_numeric(...)
При этом существующие Parser для Quote и OHLC, импорты и поведение файла не изменялись.
Unit-тесты
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_trade_parser.py
Добавлен полный набор unit-тестов нового Parser.
Добавленные тесты
В рамках Build реализовано двадцать девять unit-тестов, полностью покрывающих функциональность нового Parser.
Проверка успешного Parsing
Тест
test_parse_websocket_trade_returns_transport_event
проверяет:
- успешное создание
DzengiWebSocketTradeEvent; - корректное заполнение всех полей;
- создание неизменяемой транспортной модели.
Проверка переименования полей
Тест
test_parse_websocket_trade_renames_transport_fields
подтверждает корректное преобразование:
id → trade_id;ts → timestamp;orderId → order_id.
Проверка сохранения raw-значений
Тест
test_parse_websocket_trade_preserves_raw_numeric_values
подтверждает, что Parser не изменяет:
- цену;
- объём.
Значения сохраняются в исходном виде без каких-либо преобразований.
Проверка сохранения buyer
Тест
test_parse_websocket_trade_preserves_buyer_flag
подтверждает корректную передачу направления сделки в транспортную модель.
Игнорирование транспортной оболочки
Тест
test_parse_websocket_trade_ignores_transport_envelope
подтверждает, что поля
status;destination;correlationId;
не входят в состав транспортной модели и не используются Parser после успешного прохождения Schema Validation.
Дополнительные поля payload
Тест
test_parse_websocket_trade_ignores_additional_payload_fields
подтверждает, что дополнительные поля WebSocket-сообщения не влияют на результат Parsing.
Parser использует только обязательные поля транспортного контракта.
Проверка обязательных типов
Реализована серия параметризованных тестов, проверяющих корректность типов каждого обязательного поля транспортной модели.
При несоответствии ожидаемому типу Parser генерирует
TradeParseError
с указанием пути к некорректному элементу.
Проверка отсутствующих значений
Реализована серия тестов, подтверждающих генерацию
TradeParseError
при невозможности построить транспортную модель из-за отсутствия требуемого значения.
Проверка отсутствия Value Validation
Отдельные тесты подтверждают архитектурный принцип Build.
Parser выполняет исключительно транспортное преобразование и не анализирует корректность самих значений.
Проверка диапазонов, семантики и бизнес-ограничений полностью переносится на следующий этап дорожной карты.
Результаты тестирования
Выполнен целевой запуск нового набора unit-тестов.
python -m pytest \
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_trade_parser.py \
-q
Результат:
29 passed in 0.03s
Все проверки новой функциональности успешно завершены.
Следует отметить, что в процессе разработки была обнаружена неточность в первоначальной версии unit-тестов.
При проверке сообщений исключений использовался параметр
pytest.raises(..., match=expected_path)
где в качестве шаблона передавался JSONPath, например
$.payload.id
Поскольку параметр match интерпретирует строку как регулярное выражение, символы $ и . требовали экранирования.
После замены
match=expected_path
на
match=re.escape(expected_path)
тесты стали корректно проверять текст сообщений исключений.
Данное изменение затронуло исключительно тестовый код и не потребовало каких-либо изменений реализации Parser.
Регрессионное тестирование
После завершения реализации выполнен полный запуск набора unit-тестов проекта.
python -m pytest -q
Результат:
1161 passed in 2.63s
Регрессий существующей функциональности не обнаружено.
Все ранее реализованные Build продолжают работать без изменений.
Проверка компиляции
Выполнена проверка компиляции изменённых файлов.
python -m compileall \
src/market_data/acquisition/adapters/dzengi/parser.py \
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_trade_parser.py
Компиляция завершилась успешно.
Синтаксические ошибки отсутствуют.
Проверка Git diff
Выполнена финальная проверка изменений.
git diff --check
Ошибок не обнаружено.
Это подтверждает отсутствие:
- trailing whitespace;
- конфликтов окончания строк;
- ошибок форматирования diff.
Scope Build 060.11
В рамках данного Build реализовано только
WebSocket Trade Parser
Build не включает:
- WebSocket Trade Schema Validation;
- WebSocket Trade Value Validation;
- WebSocket Trade Mapper;
- WebSocket Trade Adapter;
- Runtime Integration;
- Unified Routing;
- Trades Feed.
Это полностью соответствует принципу атомарной реализации Build.
Архитектурный результат
После завершения Build система содержит завершённый уровень Parser для всех поддерживаемых WebSocket-событий.
Quote
ValidatedWebSocketQuoteDocument
│
▼
Quote Parser
│
▼
DzengiWebSocketQuoteResponse
OHLC
ValidatedWebSocketOhlcDocument
│
▼
OHLC Parser
│
▼
DzengiWebSocketOhlcEvent
Trade
ValidatedWebSocketTradeDocument
│
▼
Trade Parser
│
▼
DzengiWebSocketTradeEvent
Архитектура Parsing стала полностью симметричной.
Состояние WebSocket Trade Pipeline
После завершения Build 060.11 конвейер имеет следующий вид.
Raw WebSocket Trade Document
│
▼
ValidatedWebSocketTradeDocument
│
▼
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 | ✔ Completed |
| WebSocket Trade Parser | 060.11 | ✔ Completed |
| 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.
Локальность изменений
Изменены только:
- Parser;
- unit-тесты нового Parser.
Повторное использование архитектуры
Новая реализация полностью повторяет существующий шаблон Quote и OHLC.
Новая архитектура не проектировалась.
Разделение ответственности
Parser отвечает исключительно за транспортное преобразование документа.
Schema Validation, Value Validation, Mapper и Runtime остаются полностью независимыми уровнями Pipeline.
Отсутствие бизнес-логики
Build не выполняет:
- Value Validation;
- преобразование числовых значений;
- проверку диапазонов;
- Mapping;
- Runtime Integration.
Это полностью соответствует архитектуре Dzentra.
Обратная совместимость
Существующая обработка Quote, OHLC и REST Trade не изменилась.
Новая функциональность добавлена изолированно и не влияет на ранее реализованные Build.
Критерии завершения Build
Build 060.11 считается завершённым, поскольку выполнены все поставленные задачи.
- ✔ реализована функция
parse_dzengi_websocket_trade(); - ✔ реализовано преобразование
ValidatedWebSocketTradeDocumentвDzengiWebSocketTradeEvent; - ✔ реализовано переименование транспортных полей:
id → trade_id;ts → timestamp;orderId → order_id;
- ✔ реализована минимальная проверка типов, необходимая для построения транспортной модели;
- ✔ используются существующие исключения
TradeParseError; - ✔ транспортная модель остаётся неизменяемой (
frozen=True); - ✔ реализовано двадцать девять unit-тестов;
- ✔ все целевые тесты успешно проходят;
- ✔ полное регрессионное тестирование успешно завершено;
- ✔ компиляция выполнена без ошибок;
- ✔
git diff --checkне выявил замечаний; - ✔ изменения не выходят за пределы согласованного scope.
Следующий этап
Следующим этапом дорожной карты является
Build 060.12 — WebSocket Trade Value Validation
Цель Build:
- проверка корректности значений транспортной модели;
- проверка цены сделки;
- проверка объёма сделки;
- проверка временной метки;
- проверка символа;
- проверка идентификатора сделки;
- проверка идентификатора ордера;
- создание валидированной транспортной модели.
После завершения Build 060.12 конвейер примет следующий вид.
Raw WebSocket Object
│
▼
Schema Validation
│
▼
ValidatedWebSocketTradeDocument
│
▼
WebSocket Trade Parser
│
▼
DzengiWebSocketTradeEvent
│
▼
WebSocket Trade Value Validation
│
▼
ValidatedTradeTransportEvent
Build 060.12 по-прежнему не будет выполнять:
- Mapping в каноническую модель
Trade; - Runtime Integration;
- Routing;
- обработку Trade Feed.
Все перечисленные задачи будут реализованы на последующих этапах дорожной карты серии 060.
Итог
Build 060.11 завершил формирование уровня Parser для WebSocket Trade и сделал архитектуру транспортного преобразования всех поддерживаемых WebSocket-событий Dzentra полностью симметричной.
Новая реализация основана на существующем шаблоне Quote и OHLC, использует единый подход к транспортному преобразованию сообщений, повторно применяет существующую иерархию исключений и не изменяет ранее реализованное поведение системы.
Parser изолирует знания о транспортном формате WebSocket-сообщений Dzengi, выполняет минимально необходимую проверку типов для построения транспортной модели и передаёт дальнейшую обработку следующему архитектурному уровню — Value Validation.
Build ограничен согласованным scope, успешно прошёл целевое и полное регрессионное тестирование и создаёт необходимый фундамент для следующего этапа — Build 060.12 — WebSocket Trade Value Validation.