Files
dzentra_bot/docs/migrations/build_060_13.md

1169 lines
40 KiB
Markdown
Raw 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.13 — WebSocket Trade Mapper
**Engineering Migration Report**
---
# Контроль документа
| Свойство | Значение |
|----------|----------|
| Build | 060.13 |
| Название | WebSocket Trade Mapper |
| Статус | Completed |
| Проект | Dzentra |
| Подсистема | Market Data Acquisition |
| Компонент | Trades Feed |
| Версия | 1.0 |
---
# Цель Build
После завершения Build 060.12 система получила полностью реализованный уровень **WebSocket Trade Value Validation**, подтверждающий корректность всех значений транспортной модели
```text
DzengiWebSocketTradeEvent
```
На этом этапе Pipeline гарантирует:
- корректную структуру транспортного документа;
- успешное построение transport model;
- корректность всех обязательных значений;
- отсутствие недопустимых числовых представлений;
- корректность строковых идентификаторов.
Однако даже после успешного прохождения всех перечисленных этапов транспортная модель ещё не может использоваться остальной частью системы.
Несмотря на корректность структуры и значений, объект
```text
DzengiWebSocketTradeEvent
```
по-прежнему остаётся транспортной моделью биржи Dzengi.
Он содержит особенности конкретного источника данных:
- транспортные числовые представления;
- транспортное поле `size`;
- транспортное поле `buyer`;
- транспортное поле `order_id`;
- транспортное представление времени в миллисекундах Unix Epoch.
Использование подобных моделей за пределами слоя адаптера противоречит базовым архитектурным принципам Dzentra.
Внутренние компоненты системы не должны зависеть от особенностей API конкретной биржи.
Для решения данной задачи архитектура Dzentra предусматривает следующий обязательный уровень Pipeline —
**Mapper**.
Именно Mapper выполняет преобразование транспортной модели источника данных в единую каноническую модель предметной области.
Build 060.13 реализует данный уровень для WebSocket Trade.
Основная задача Build — преобразовать проверенную транспортную модель
```text
DzengiWebSocketTradeEvent
```
в каноническую immutable-модель
```text
Trade
```
с выполнением всех необходимых преобразований типов данных.
При этом Build не затрагивает:
- Schema Validation;
- Parser;
- Value Validation;
- WebSocket Runtime;
- Routing;
- Trades Feed.
---
# Предпосылки
К началу Build архитектура Market Data Acquisition уже содержала полностью реализованные Mapper для остальных типов рыночных данных.
Аналогичный уровень преобразования уже существовал для:
- REST Quote;
- WebSocket Quote;
- WebSocket OHLC;
- REST Aggregate Trade.
Во всех случаях использовалась одинаковая архитектурная схема обработки.
```text
Transport Model
Mapper
Canonical Model
```
После завершения Build 060.12 аналогичный транспортный конвейер появился и для WebSocket Trade.
```text
ValidatedWebSocketTradeDocument
Trade Parser
DzengiWebSocketTradeEvent
Value Validation
DzengiWebSocketTradeEvent
```
Однако следующий обязательный уровень — преобразование транспортной модели в каноническую модель системы — ещё отсутствовал.
Таким образом WebSocket-конвейер обработки сделок оставался архитектурно незавершённым.
---
# Архитектурное основание
Одним из базовых принципов архитектуры Dzentra является полная изоляция внутренних компонентов системы от формата данных конкретной биржи.
Любые особенности транспортного протокола должны оставаться исключительно внутри слоя адаптера.
Общий конвейер обработки рыночных данных имеет следующий вид.
```text
Raw Source
Schema Validation
Transport Model
Value Validation
Mapper
Domain Model
```
Каждый уровень Pipeline отвечает только за одну категорию задач.
Для WebSocket Trade это разделение выглядит следующим образом.
```text
Schema Validation
```
гарантирует корректность структуры транспортного документа.
---
```text
Parser
```
создаёт транспортную модель
```text
DzengiWebSocketTradeEvent
```
без изменения типов данных.
---
```text
Value Validation
```
подтверждает корректность значений транспортной модели без выполнения каких-либо преобразований.
---
Следующий уровень —
```text
Mapper
```
выполняет преобразование транспортной модели в каноническую модель предметной области.
Именно Mapper отвечает за:
- преобразование транспортных числовых представлений в `Decimal`;
- преобразование транспортного timestamp в `datetime`;
- преобразование транспортных признаков сделки в канонические перечисления;
- переименование транспортных полей;
- создание immutable-модели `Trade`.
При этом Mapper принципиально **не выполняет**:
- повторную Validation;
- анализ структуры JSON;
- Runtime-логику;
- бизнес-логику;
- маршрутизацию событий.
Такое разделение ответственности позволяет каждому уровню Pipeline оставаться независимым, повторно используемым и легко тестируемым.
---
# Результаты архитектурного аудита
Перед реализацией Build был выполнен аудит существующего слоя Mapper.
В ходе анализа подтверждено наличие полностью сформированной архитектуры преобразования транспортных моделей.
Для различных типов рыночных данных уже реализованы функции:
```text
map_dzengi_quote_to_quote(...)
map_dzengi_websocket_quote_to_quote(...)
map_dzengi_websocket_ohlc_to_candle_close_event(...)
map_dzengi_rest_agg_trades_to_trades(...)
```
Все существующие Mapper используют одинаковые архитектурные принципы.
Преобразование транспортных типов выполняется исключительно внутри Mapper.
Для этого повторно используются существующие helper-функции проекта.
Одновременно аудит подтвердил наличие общего исключения
```text
TradeMappingError
```
используемого всеми существующими Mapper при невозможности построения канонической модели.
При этом отдельный Mapper для транспортной модели
```text
DzengiWebSocketTradeEvent
```
в системе отсутствовал.
Таким образом единственным отсутствующим элементом архитектурной цепочки являлся собственный уровень Mapping для WebSocket Trade.
Build 060.13 полностью закрывает данный пробел и завершает следующий обязательный уровень Pipeline серии Build 060.
---
# Архитектурное решение
По результатам проведённого аудита было принято решение полностью повторить архитектурный шаблон, уже используемый Mapper остальных типов рыночных данных.
В систему добавлена функция
```text
map_dzengi_websocket_trade_to_trade(...)
```
которая принимает транспортную модель
```text
DzengiWebSocketTradeEvent
```
и создаёт каноническую immutable-модель
```text
Trade
```
Во время преобразования выполняются все необходимые преобразования транспортных типов данных.
После успешного завершения Mapping транспортная модель больше не используется последующими уровнями системы.
Конвейер WebSocket Trade принимает следующий вид.
```text
Raw WebSocket Trade
WebSocket Trade Schema Validation
ValidatedWebSocketTradeDocument
WebSocket Trade Parser
DzengiWebSocketTradeEvent
WebSocket Trade Value Validation
DzengiWebSocketTradeEvent
WebSocket Trade Mapper
Trade
```
Build 060.13 не изменяет архитектуру ранее реализованных компонентов и завершает следующий обязательный уровень транспортного Pipeline.
# Реализованный уровень Mapper
В файл
```text
src/market_data/acquisition/adapters/dzengi/mapper.py
```
добавлена новая функция
```text
map_dzengi_websocket_trade_to_trade(...)
```
Функция получает транспортную модель
```text
DzengiWebSocketTradeEvent
```
и выполняет построение канонической модели
```text
Trade
```
Во время выполнения Mapping создаётся новый immutable-объект предметной области.
Транспортная модель при этом остаётся неизменной.
Таким образом следующий уровень Pipeline получает уже не транспортную модель биржи, а полностью независимую внутреннюю модель системы.
---
# Почему Mapper создаёт новую модель
Во время архитектурного проектирования отдельно рассматривался вопрос о возможности повторного использования объекта
```text
DzengiWebSocketTradeEvent
```
на последующих уровнях системы.
По результатам анализа было принято решение полностью отказаться от подобного подхода.
Основные причины:
- транспортная модель отражает структуру конкретной биржи;
- транспортная модель содержит поля, отсутствующие в предметной области;
- транспортная модель использует транспортные представления числовых данных;
- транспортная модель использует транспортные соглашения о наименовании полей;
- внутренние компоненты системы не должны зависеть от API биржи.
Поэтому Mapper всегда создаёт новый экземпляр
```text
Trade
```
который становится единственной моделью, используемой за пределами слоя адаптера.
Подобное решение полностью соответствует базовому архитектурному принципу Dzentra — полной изоляции внутренних компонентов от транспортных контрактов внешних источников данных.
---
# Преобразуемая транспортная модель
Преобразование выполняется над объектом
```python
@dataclass(frozen=True, slots=True)
class DzengiWebSocketTradeEvent:
trade_id: int
price: DzengiRawNumeric
size: DzengiRawNumeric
timestamp: int
symbol: str
buyer: bool
order_id: str
```
После выполнения Mapping создаётся объект
```python
@dataclass(frozen=True, slots=True)
class Trade:
symbol: str
trade_id: int
price: Decimal
quantity: Decimal
executed_at: datetime
aggressor_side: TradeAggressorSide
source: str
```
Таким образом Mapper полностью устраняет зависимость системы от транспортной модели биржи.
---
# Преобразование идентификатора сделки
Поле
```text
trade_id
```
имеет одинаковую семантику в транспортной и канонической модели.
Поэтому Mapper переносит его без изменения значения.
Дополнительных преобразований не выполняется.
---
# Преобразование цены сделки
Поле
```text
price
```
в транспортной модели может быть представлено в нескольких форматах.
Например:
```text
"63992.50"
63992
63992.50
```
Во время Mapping используется существующий helper проекта, преобразующий транспортное представление в
```text
Decimal
```
После завершения Mapping каноническая модель всегда содержит внутренний числовой тип системы независимо от исходного представления данных.
---
# Преобразование количества сделки
Транспортная модель использует поле
```text
size
```
которое отражает терминологию API биржи.
В канонической модели используется единое наименование
```text
quantity
```
Во время Mapping выполняются одновременно два действия:
- преобразование транспортного значения в `Decimal`;
- переименование транспортного поля в соответствии с внутренней моделью предметной области.
После завершения Mapping дальнейшая работа системы полностью абстрагируется от терминологии конкретной биржи.
---
# Преобразование временной метки
Поле
```text
timestamp
```
в транспортной модели представляет собой количество миллисекунд, прошедших с начала эпохи Unix.
Подобное представление удобно для передачи данных по сети, однако не является внутренним представлением времени в Dzentra.
Во время Mapping используется существующая helper-функция преобразования времени.
В результате каноническая модель получает объект
```text
datetime
```
в часовом поясе UTC.
Таким образом все внутренние компоненты системы используют единый формат представления времени независимо от способа передачи данных биржей.
---
# Преобразование стороны агрессора
Во время архитектурного аудита отдельно анализировалась семантика транспортного поля
```text
buyer
```
В WebSocket API Dzengi данное поле определяет сторону покупателя сделки.
Однако внутренняя модель Dzentra использует перечисление
```text
TradeAggressorSide
```
Поэтому Mapper выполняет явное преобразование транспортного признака в каноническое перечисление.
Используется следующее соответствие.
```text
buyer = True
TradeAggressorSide.BUY
```
```text
buyer = False
TradeAggressorSide.SELL
```
Подобное преобразование делает внутреннюю модель полностью независимой от конкретного транспортного соглашения биржи.
---
# Преобразование источника данных
Каноническая модель
```text
Trade
```
содержит поле
```text
source
```
которое используется для идентификации происхождения события.
Во время Mapping данное поле получает фиксированное значение
```text
dzengi_websocket_trade
```
Использование отдельного идентификатора источника позволяет последующим уровням системы различать происхождение канонических моделей без анализа транспортного Pipeline.
---
# Почему order_id отсутствует в канонической модели
Транспортная модель WebSocket содержит дополнительное поле
```text
order_id
```
которое используется исключительно транспортным контрактом биржи.
Во время архитектурного проектирования отдельно анализировался вопрос необходимости переноса данного значения в каноническую модель.
По результатам анализа было принято решение отказаться от подобного преобразования.
Основные причины:
- идентификатор ордера отсутствует в модели предметной области;
- последующие уровни системы не используют данное значение;
- сохранение транспортного идентификатора нарушило бы независимость канонической модели.
В результате поле
```text
order_id
```
полностью завершается на уровне адаптера и не попадает в модель
```text
Trade
```
---
# Повторное использование существующих helper-функций
Build 060.13 не вводит новых механизмов преобразования числовых значений и времени.
Вместо этого повторно используются уже существующие helper-функции проекта.
Для преобразования числовых представлений применяется существующий механизм преобразования в
```text
Decimal
```
Для преобразования временной метки используется существующая функция построения объекта
```text
datetime
```
Подобный подход обеспечивает единообразное поведение всех Mapper проекта независимо от типа источника данных.
---
# Использование TradeMappingError
Все ошибки преобразования используют существующее исключение
```text
TradeMappingError
```
Build не вводит новых типов исключений.
Это сохраняет единый механизм обработки ошибок Mapping во всей подсистеме Market Data Acquisition.
Value Validation продолжает использовать
```text
TradeValueError
```
а Mapper использует исключительно
```text
TradeMappingError
```
Тем самым достигается чёткое разделение ошибок проверки данных и ошибок преобразования транспортной модели.
---
# Изменённые файлы
В рамках Build были изменены только два файла.
## Mapper
```text
src/market_data/acquisition/adapters/dzengi/mapper.py
```
Добавлена функция
```text
map_dzengi_websocket_trade_to_trade(...)
```
а также вспомогательная функция преобразования стороны агрессора WebSocket Trade.
Существующая логика Mapping Quote, OHLC и REST Trade не изменялась.
---
## Unit-тесты
```text
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_trade_mapper.py
```
Добавлен полный набор unit-тестов нового уровня Mapping.
---
# Добавленные тесты
В рамках Build реализовано двадцать два unit-теста, полностью покрывающих новую функциональность.
## Проверка корректного Mapping
Подтверждается успешное создание канонической модели
```text
Trade
```
из полностью корректной транспортной модели.
---
## Проверка преобразования стороны агрессора
Отдельные тесты подтверждают корректное преобразование обоих допустимых значений:
- `buyer=True`;
- `buyer=False`.
---
## Проверка преобразования числовых представлений
Параметризованные тесты подтверждают корректную обработку:
- строк;
- целых чисел;
- чисел с плавающей точкой.
для полей:
- `price`;
- `size`.
---
## Проверка преобразования времени
Подтверждается корректное построение объекта
```text
datetime
```
в часовом поясе UTC.
---
## Проверка канонических имён полей
Отдельные тесты подтверждают:
- преобразование `size → quantity`;
- удаление пробельных символов из `symbol`;
- заполнение поля `source`;
- отсутствие поля `order_id` в канонической модели.
---
## Проверка неизменяемости моделей
Отдельные тесты подтверждают:
- неизменяемость транспортной модели после Mapping;
- неизменяемость созданной модели `Trade`.
---
## Проверка ошибок преобразования
Реализованы проверки генерации
```text
TradeMappingError
```
при невозможности преобразования:
- числовых значений;
- специальных числовых представлений;
- недопустимого значения timestamp.
# Результаты тестирования
После завершения реализации выполнен целевой запуск нового набора unit-тестов.
```bash
python -m pytest \
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_trade_mapper.py \
-q
```
Результат:
```text
22 passed in 0.04s
```
Все проверки новой функциональности успешно завершены.
Новый набор тестов полностью покрывает:
- успешное построение канонической модели `Trade`;
- преобразование транспортных числовых представлений;
- преобразование временной метки в `datetime`;
- преобразование стороны агрессора;
- переименование транспортных полей;
- заполнение источника данных;
- генерацию исключений для всех типов ошибок Mapping;
- неизменяемость транспортной и канонической моделей.
Параметризованные тесты позволили существенно сократить объём тестового кода без уменьшения покрытия и обеспечили единообразную проверку различных вариантов входных данных.
---
# Регрессионное тестирование
После завершения реализации выполнен полный запуск набора unit-тестов проекта.
```bash
python -m pytest -q
```
Результат:
```text
1230 passed in 2.00s
```
Регрессий существующей функциональности не обнаружено.
Все ранее реализованные Build продолжают работать без каких-либо изменений.
Это подтверждает, что добавленная функциональность полностью изолирована и не влияет на существующие конвейеры обработки Quote, OHLC, REST Trade и остальные подсистемы проекта.
---
# Проверка компиляции
После завершения реализации выполнена полная проверка компиляции проекта.
```bash
python -m compileall src tests
```
Компиляция завершилась успешно.
Ошибок синтаксиса не обнаружено.
Все изменённые файлы успешно компилируются и не нарушают целостность проекта.
---
# Проверка Git diff
После завершения реализации выполнена финальная проверка изменений.
```bash
git diff --check
```
Результат:
```text
без замечаний
```
Проверка подтвердила отсутствие:
- trailing whitespace;
- ошибок окончания строк;
- конфликтов diff;
- нарушений форматирования.
---
# Scope Build 060.13
В рамках данного Build реализован исключительно уровень
```text
WebSocket Trade Mapper
```
Build **не включает**:
- WebSocket Trade Schema Validation;
- WebSocket Trade Parser;
- WebSocket Trade Value Validation;
- WebSocket Trade Adapter;
- Runtime Integration;
- Unified Routing;
- Trades Feed.
Подобное ограничение полностью соответствует принятому принципу атомарной реализации Build.
Каждый этап дорожной карты реализует только один архитектурный уровень Pipeline.
---
# Архитектурный результат
После завершения Build система содержит полностью реализованный транспортный Pipeline WebSocket Trade вплоть до создания канонической модели предметной области.
Конвейер обработки принимает следующий вид.
```text
Raw WebSocket Trade
WebSocket Trade Schema Validation
ValidatedWebSocketTradeDocument
WebSocket Trade Parser
DzengiWebSocketTradeEvent
WebSocket Trade Value Validation
DzengiWebSocketTradeEvent
WebSocket Trade Mapper
Trade
```
Таким образом архитектура WebSocket Trade теперь полностью повторяет ранее реализованные конвейеры обработки Quote, OHLC и REST Trade.
После завершения Mapping дальнейшие уровни системы больше не используют транспортную модель биржи.
Все последующие компоненты работают исключительно с канонической моделью
```text
Trade
```
что полностью соответствует базовым архитектурным принципам Dzentra.
---
# Состояние WebSocket Trade Pipeline
После завершения Build 060.13 конвейер имеет следующий вид.
```text
Raw WebSocket Trade
Schema Validation
ValidatedWebSocketTradeDocument
Parser
DzengiWebSocketTradeEvent
Value Validation
DzengiWebSocketTradeEvent
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 | ✔ Completed |
| WebSocket Trade Mapper | 060.13 | ✔ Completed |
| WebSocket Trade Adapter | 060.14 | Pending |
| Unified WebSocket Routing | 060.15 | Pending |
---
# Соблюдение архитектурных принципов
В рамках Build полностью сохранены архитектурные инварианты Dzentra.
## Локальность изменений
Изменены только:
- `adapters/dzengi/mapper.py`;
- unit-тесты нового уровня Mapping.
Существующая логика Quote, OHLC и REST Trade не изменялась.
---
## Повторное использование архитектуры
Новая реализация полностью повторяет существующий архитектурный шаблон Mapper.
Новая архитектура не проектировалась.
Использован уже существующий подход, применяемый для остальных типов рыночных данных.
---
## Повторное использование инфраструктуры
Для преобразования числовых значений, временных меток и канонических перечислений повторно использованы существующие helper-функции проекта.
Build не вводит новых механизмов преобразования и не дублирует уже реализованную функциональность.
Это обеспечивает единообразное поведение всех Mapper проекта.
---
## Разделение ответственности
Mapper отвечает исключительно за преобразование транспортной модели в каноническую модель предметной области.
Build не выполняет:
- Schema Validation;
- Value Validation;
- Runtime Integration;
- бизнес-логику;
- маршрутизацию событий.
Все перечисленные задачи остаются ответственностью предыдущих либо последующих уровней Pipeline.
---
## Обратная совместимость
Существующая обработка:
- Quote;
- OHLC;
- REST Trade;
не изменилась.
Добавленная функциональность полностью изолирована и не оказывает влияния на ранее реализованные компоненты системы.
---
# Архитектурные решения Build (ADR)
## ADR-060.13-001
**Mapper всегда создаёт новую каноническую модель.**
После завершения преобразования транспортная модель больше не используется последующими уровнями системы.
Это обеспечивает полную изоляцию внутренней архитектуры от API конкретной биржи.
---
## ADR-060.13-002
**Все преобразования транспортных типов выполняются исключительно внутри Mapper.**
Преобразование транспортных числовых представлений в `Decimal`, временной метки в `datetime` и транспортных признаков сделки в канонические перечисления не допускается ни на одном другом уровне Pipeline.
---
## ADR-060.13-003
**Транспортные поля, отсутствующие в предметной области, не переносятся в каноническую модель.**
Поле
```text
order_id
```
является частью транспортного контракта биржи и не включается в модель
```text
Trade
```
---
## ADR-060.13-004
**Mapper использует существующее исключение TradeMappingError.**
Build не вводит новых типов исключений.
Все ошибки преобразования продолжают использовать единый механизм обработки ошибок Mapping.
---
# Критерии завершения Build
Build 060.13 считается завершённым, поскольку выполнены все поставленные задачи.
- ✔ реализована функция `map_dzengi_websocket_trade_to_trade()`;
- ✔ реализовано преобразование транспортной модели в каноническую модель `Trade`;
- ✔ реализовано преобразование транспортных числовых представлений в `Decimal`;
- ✔ реализовано преобразование временной метки в `datetime`;
- ✔ реализовано преобразование стороны агрессора;
- ✔ реализовано заполнение поля `source`;
- ✔ исключено транспортное поле `order_id`;
- ✔ повторно использованы существующие helper-функции;
- ✔ используется существующее исключение `TradeMappingError`;
- ✔ транспортная модель остаётся неизменяемой;
- ✔ реализовано 22 unit-теста;
- ✔ целевой набор тестов успешно проходит;
- ✔ полное регрессионное тестирование успешно завершено;
- ✔ проект успешно компилируется;
-`git diff --check` не выявил замечаний;
- ✔ изменения полностью укладываются в согласованный scope Build.
---
# Следующий этап
Следующим этапом дорожной карты является
```text
Build 060.14 — WebSocket Trade Adapter
```
Цель следующего Build:
- объединить Schema Validation, Parser, Value Validation и Mapper в единый адаптер;
- реализовать единую точку обработки WebSocket Trade;
- завершить адаптер получения канонической модели `Trade` из сырого WebSocket-сообщения;
- подготовить основу для последующей интеграции в Unified WebSocket Routing.
---
# Итог
Build 060.13 завершил реализацию уровня **WebSocket Trade Mapper** и сделал транспортный Pipeline WebSocket Trade полностью независимым от формата данных биржи Dzengi.
Новая реализация основана на уже существующих архитектурных принципах Dzentra, повторно использует существующую инфраструктуру преобразования данных, создаёт каноническую модель предметной области и сохраняет строгое разделение ответственности между уровнями Pipeline.
Build ограничен согласованным scope, успешно прошёл целевое и полное регрессионное тестирование, подтвердил отсутствие регрессий и завершил построение транспортного конвейера WebSocket Trade до уровня канонической модели.
Следующим этапом развития серии является **Build 060.14 — WebSocket Trade Adapter**, который объединит все реализованные уровни Pipeline в единый компонент обработки входящих WebSocket-сообщений.