Files
dzentra_bot/docs/migrations/build_060_11.md

32 KiB
Raw Permalink Blame History

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.