build 059.7: add websocket OHLC adapter
This commit is contained in:
768
docs/migrations/build_059_7.md
Normal file
768
docs/migrations/build_059_7.md
Normal file
@@ -0,0 +1,768 @@
|
||||
# Build 059.7 — WebSocket OHLC Adapter
|
||||
|
||||
**Проект:** Dzentra
|
||||
**Подсистема:** Market Data Acquisition
|
||||
**Этап:** 059.7
|
||||
**Статус:** Completed
|
||||
|
||||
---
|
||||
|
||||
# Цель
|
||||
|
||||
Добавить верхнеуровневый адаптер обработки WebSocket OHLC-событий Dzengi, объединяющий все ранее реализованные стадии обработки в единый конвейер.
|
||||
|
||||
Результатом работы адаптера должна стать полностью подготовленная внутренняя модель Dzentra:
|
||||
|
||||
```text
|
||||
CandleCloseEvent
|
||||
```
|
||||
|
||||
Адаптер становится единственной точкой входа для преобразования WebSocket-сообщения в событие, пригодное для дальнейшей обработки внутри runtime.
|
||||
|
||||
---
|
||||
|
||||
# Причина изменения
|
||||
|
||||
После завершения Build 059.6 система уже содержала полный набор независимых компонентов обработки WebSocket OHLC.
|
||||
|
||||
К этому моменту были реализованы:
|
||||
|
||||
```text
|
||||
Schema Validation
|
||||
```
|
||||
|
||||
↓
|
||||
|
||||
```text
|
||||
Parser
|
||||
```
|
||||
|
||||
↓
|
||||
|
||||
```text
|
||||
Value Validation
|
||||
```
|
||||
|
||||
↓
|
||||
|
||||
```text
|
||||
Mapper
|
||||
```
|
||||
|
||||
Каждый компонент решал строго одну задачу и обладал собственным контрактом ответственности.
|
||||
|
||||
Однако использование этих компонентов требовало их последовательного вызова вручную.
|
||||
|
||||
Подобный подход приводил к нескольким архитектурным недостаткам:
|
||||
|
||||
- отсутствовала единая точка входа;
|
||||
- порядок выполнения этапов мог быть нарушен;
|
||||
- различные части системы могли использовать разные последовательности обработки;
|
||||
- возрастал риск появления дублирующего кода.
|
||||
|
||||
Build 059.7 устраняет эти недостатки.
|
||||
|
||||
Теперь вся последовательность обработки инкапсулирована внутри одного адаптера.
|
||||
|
||||
---
|
||||
|
||||
# Реализовано
|
||||
|
||||
Изменён файл:
|
||||
|
||||
```text
|
||||
src/market_data/acquisition/adapters/dzengi/websocket.py
|
||||
```
|
||||
|
||||
Создан файл:
|
||||
|
||||
```text
|
||||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_adapter.py
|
||||
```
|
||||
|
||||
Добавлена документация:
|
||||
|
||||
```text
|
||||
docs/migrations/build_059_7.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Новый адаптер
|
||||
|
||||
Добавлен класс:
|
||||
|
||||
```text
|
||||
DzengiWebSocketOhlcAdapter
|
||||
```
|
||||
|
||||
Основной публичный метод:
|
||||
|
||||
```text
|
||||
map_message()
|
||||
```
|
||||
|
||||
Назначение метода:
|
||||
|
||||
```text
|
||||
Raw WebSocket document
|
||||
↓
|
||||
CandleCloseEvent
|
||||
```
|
||||
|
||||
Адаптер объединяет все ранее реализованные стадии обработки и предоставляет единый программный интерфейс для остальных компонентов системы.
|
||||
|
||||
---
|
||||
|
||||
# Конвейер обработки
|
||||
|
||||
После завершения Build 059.7 полный pipeline выглядит следующим образом:
|
||||
|
||||
```text
|
||||
Raw WebSocket message
|
||||
↓
|
||||
Schema Validation
|
||||
↓
|
||||
ValidatedWebSocketOhlcDocument
|
||||
↓
|
||||
Parser
|
||||
↓
|
||||
DzengiWebSocketOhlcEvent
|
||||
↓
|
||||
Value Validation
|
||||
↓
|
||||
Mapper
|
||||
↓
|
||||
CandleCloseEvent
|
||||
```
|
||||
|
||||
Все стадии выполняются строго последовательно.
|
||||
|
||||
Порядок выполнения не может быть изменён вызывающей стороной.
|
||||
|
||||
---
|
||||
|
||||
# Последовательность обработки
|
||||
|
||||
Внутри метода `map_message()` выполняются следующие действия.
|
||||
|
||||
## 1. Schema Validation
|
||||
|
||||
Выполняется проверка структуры входящего сообщения.
|
||||
|
||||
Проверяется наличие обязательных полей транспортного контракта Dzengi:
|
||||
|
||||
```text
|
||||
status
|
||||
destination
|
||||
payload
|
||||
```
|
||||
|
||||
После успешной проверки строится модель:
|
||||
|
||||
```text
|
||||
ValidatedWebSocketOhlcDocument
|
||||
```
|
||||
|
||||
Ошибки данного этапа приводят к возникновению:
|
||||
|
||||
```text
|
||||
CandleWebSocketSchemaError
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Parser
|
||||
|
||||
Проверенный документ передаётся parser.
|
||||
|
||||
Parser преобразует транспортную модель в специализированную внутреннюю модель интеграционного слоя:
|
||||
|
||||
```text
|
||||
DzengiWebSocketOhlcEvent
|
||||
```
|
||||
|
||||
На этом этапе выполняется преобразование структуры сообщения.
|
||||
|
||||
Проверка предметной корректности значений не производится.
|
||||
|
||||
Ошибки parser приводят к возникновению:
|
||||
|
||||
```text
|
||||
CandleWebSocketParseError
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Value Validation
|
||||
|
||||
Полученная transport-модель проходит предметную проверку.
|
||||
|
||||
Контролируются:
|
||||
|
||||
- корректность временной метки;
|
||||
- допустимость цен;
|
||||
- допустимость типа свечи;
|
||||
- непротиворечивость OHLC.
|
||||
|
||||
При обнаружении ошибок формируется:
|
||||
|
||||
```text
|
||||
CandleWebSocketValueError
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Mapper
|
||||
|
||||
Последней стадией является mapper.
|
||||
|
||||
Он преобразует:
|
||||
|
||||
```text
|
||||
DzengiWebSocketOhlcEvent
|
||||
```
|
||||
|
||||
в:
|
||||
|
||||
```text
|
||||
CandleCloseEvent
|
||||
```
|
||||
|
||||
При этом выполняются:
|
||||
|
||||
- преобразование timestamp;
|
||||
- преобразование цен в `Decimal`;
|
||||
- установка `source`;
|
||||
- перенос `received_at`.
|
||||
|
||||
После завершения mapper транспортный контракт Dzengi полностью исчезает из дальнейшего pipeline.
|
||||
|
||||
Все последующие компоненты работают исключительно с внутренней моделью Dzentra.
|
||||
|
||||
---
|
||||
|
||||
# Обработка received_at
|
||||
|
||||
Метод `map_message()` принимает дополнительный необязательный параметр:
|
||||
|
||||
```text
|
||||
received_at
|
||||
```
|
||||
|
||||
Тип параметра:
|
||||
|
||||
```text
|
||||
datetime | None
|
||||
```
|
||||
|
||||
Возможны два режима работы.
|
||||
|
||||
---
|
||||
|
||||
## received_at передан
|
||||
|
||||
Если вызывающая сторона уже зафиксировала момент получения сообщения, адаптер использует именно это значение.
|
||||
|
||||
Никаких дополнительных преобразований времени не выполняется.
|
||||
|
||||
Это позволяет сохранить единый момент получения данных на всём протяжении обработки сообщения.
|
||||
|
||||
---
|
||||
|
||||
## received_at отсутствует
|
||||
|
||||
Если параметр не передан, адаптер автоматически создаёт значение:
|
||||
|
||||
```python
|
||||
datetime.now(timezone.utc)
|
||||
```
|
||||
|
||||
Таким образом каждое успешно обработанное событие всегда содержит корректное время получения.
|
||||
|
||||
Использование UTC обеспечивает единый формат времени независимо от локальной временной зоны системы.
|
||||
|
||||
---
|
||||
|
||||
# Контракт адаптера
|
||||
|
||||
Build 059.7 не вводит новых транспортных моделей.
|
||||
|
||||
Адаптер использует исключительно ранее реализованные компоненты.
|
||||
|
||||
Контракт адаптера можно представить следующим образом:
|
||||
|
||||
```text
|
||||
Input:
|
||||
|
||||
Raw WebSocket document
|
||||
```
|
||||
|
||||
↓
|
||||
|
||||
```text
|
||||
Output:
|
||||
|
||||
CandleCloseEvent
|
||||
```
|
||||
|
||||
При этом адаптер гарантирует строго фиксированную последовательность выполнения всех промежуточных стадий.
|
||||
|
||||
---
|
||||
|
||||
# Контракт ошибок
|
||||
|
||||
Адаптер намеренно не перехватывает исключения нижележащих компонентов.
|
||||
|
||||
Каждая стадия обработки продолжает использовать собственный тип ошибки.
|
||||
|
||||
Полный контракт выглядит следующим образом:
|
||||
|
||||
```text
|
||||
Transport
|
||||
↓
|
||||
CandleTransportError
|
||||
|
||||
Schema Validation
|
||||
↓
|
||||
CandleWebSocketSchemaError
|
||||
|
||||
Parser
|
||||
↓
|
||||
CandleWebSocketParseError
|
||||
|
||||
Value Validation
|
||||
↓
|
||||
CandleWebSocketValueError
|
||||
|
||||
Mapper
|
||||
↓
|
||||
CandleWebSocketMappingError
|
||||
```
|
||||
|
||||
При возникновении любой ошибки выполнение pipeline немедленно прекращается.
|
||||
|
||||
Исключение передаётся вызывающей стороне без изменения типа.
|
||||
|
||||
Такой подход сохраняет независимость отдельных слоёв системы и упрощает диагностику ошибок.
|
||||
|
||||
---
|
||||
|
||||
# Почему адаптер не перехватывает исключения
|
||||
|
||||
Может показаться, что адаптер должен преобразовывать все ошибки в единое исключение.
|
||||
|
||||
В Build 059.7 это намеренно не реализовано.
|
||||
|
||||
Причины следующие.
|
||||
|
||||
Во-первых, каждая стадия уже обладает собственным контрактом.
|
||||
|
||||
Во-вторых, сохранение исходного типа исключения позволяет точно определить место возникновения ошибки.
|
||||
|
||||
В-третьих, дополнительное оборачивание исключений не несёт новой информации и лишь усложняет диагностику.
|
||||
|
||||
Поэтому адаптер выступает исключительно координатором последовательного выполнения pipeline.
|
||||
|
||||
---
|
||||
|
||||
# Ответственность адаптера
|
||||
|
||||
После завершения Build 059.7 ответственность адаптера ограничивается следующими задачами.
|
||||
|
||||
Он обязан:
|
||||
|
||||
- принять исходное WebSocket-сообщение;
|
||||
- выполнить полный pipeline обработки;
|
||||
- при необходимости создать `received_at`;
|
||||
- вернуть `CandleCloseEvent`.
|
||||
|
||||
На этом обязанности адаптера заканчиваются.
|
||||
|
||||
---
|
||||
|
||||
# Что адаптер НЕ делает
|
||||
|
||||
Build 059.7 намеренно не расширяет область ответственности адаптера.
|
||||
|
||||
Адаптер не выполняет:
|
||||
|
||||
- подключение к WebSocket;
|
||||
- получение сообщений из сети;
|
||||
- управление соединением;
|
||||
- подписку на каналы;
|
||||
- публикацию событий в runtime;
|
||||
- REST reconciliation;
|
||||
- получение объёма свечи;
|
||||
- построение канонической модели `Candle`;
|
||||
- повторную синхронизацию истории;
|
||||
- обработку разрыва соединения.
|
||||
|
||||
Все перечисленные задачи относятся к последующим этапам реализации.
|
||||
|
||||
---
|
||||
|
||||
# Использование существующих компонентов
|
||||
|
||||
Build 059.7 не реализует собственную бизнес-логику обработки свечей.
|
||||
|
||||
Вместо этого адаптер объединяет ранее созданные компоненты:
|
||||
|
||||
```text
|
||||
Schema Validator
|
||||
```
|
||||
|
||||
↓
|
||||
|
||||
```text
|
||||
Parser
|
||||
```
|
||||
|
||||
↓
|
||||
|
||||
```text
|
||||
Value Validator
|
||||
```
|
||||
|
||||
↓
|
||||
|
||||
```text
|
||||
Mapper
|
||||
```
|
||||
|
||||
Таким образом Build 059.7 не дублирует уже реализованную функциональность.
|
||||
|
||||
Он только формирует единый программный интерфейс для использования существующего pipeline.
|
||||
|
||||
---
|
||||
|
||||
# Архитектурное значение
|
||||
|
||||
После появления адаптера остальные части системы больше не обязаны знать внутреннее устройство обработки WebSocket OHLC.
|
||||
|
||||
Для них существует единственная операция:
|
||||
|
||||
```text
|
||||
Raw WebSocket message
|
||||
↓
|
||||
CandleCloseEvent
|
||||
```
|
||||
|
||||
Вся остальная логика полностью скрыта внутри адаптера.
|
||||
|
||||
Это соответствует принципу инкапсуляции и уменьшает связанность между подсистемами.
|
||||
|
||||
---
|
||||
|
||||
# Unit Tests
|
||||
|
||||
Создан файл:
|
||||
|
||||
```text
|
||||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_adapter.py
|
||||
```
|
||||
|
||||
Проверяются следующие сценарии.
|
||||
|
||||
---
|
||||
|
||||
## Построение CandleCloseEvent
|
||||
|
||||
Проверяется полный pipeline обработки.
|
||||
|
||||
Исходное WebSocket-сообщение должно успешно пройти:
|
||||
|
||||
```text
|
||||
Schema Validation
|
||||
↓
|
||||
Parser
|
||||
↓
|
||||
Value Validation
|
||||
↓
|
||||
Mapper
|
||||
↓
|
||||
CandleCloseEvent
|
||||
```
|
||||
|
||||
Проверяется корректность всех основных полей:
|
||||
|
||||
- symbol;
|
||||
- interval;
|
||||
- candle_type;
|
||||
- open_price;
|
||||
- high_price;
|
||||
- low_price;
|
||||
- close_price;
|
||||
- source;
|
||||
- received_at.
|
||||
|
||||
---
|
||||
|
||||
## Автоматическая установка received_at
|
||||
|
||||
Если вызывающая сторона не передала параметр:
|
||||
|
||||
```text
|
||||
received_at
|
||||
```
|
||||
|
||||
адаптер обязан автоматически установить текущее UTC-время.
|
||||
|
||||
Тест подтверждает наличие значения и корректность его типа.
|
||||
|
||||
---
|
||||
|
||||
## Сохранение ошибок Value Validation
|
||||
|
||||
Если входное сообщение содержит недопустимые значения, адаптер не должен скрывать возникшее исключение.
|
||||
|
||||
Проверяется, что наружу передаётся:
|
||||
|
||||
```text
|
||||
CandleWebSocketValueError
|
||||
```
|
||||
|
||||
без изменения типа ошибки.
|
||||
|
||||
---
|
||||
|
||||
## Проверка полного pipeline
|
||||
|
||||
Отдельный тест подтверждает, что адаптер действительно выполняет все стадии обработки.
|
||||
|
||||
В результате вызывающая сторона получает уже полностью построенный объект:
|
||||
|
||||
```text
|
||||
CandleCloseEvent
|
||||
```
|
||||
|
||||
без необходимости самостоятельно вызывать parser, validator и mapper.
|
||||
|
||||
---
|
||||
|
||||
Всего выполнено:
|
||||
|
||||
```text
|
||||
4 tests
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Проверка синтаксиса
|
||||
|
||||
Выполнена команда:
|
||||
|
||||
```bash
|
||||
python -m compileall \
|
||||
src/market_data/acquisition/adapters/dzengi/websocket.py \
|
||||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_adapter.py
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
успешно
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Целевые тесты
|
||||
|
||||
Выполнена команда:
|
||||
|
||||
```bash
|
||||
python -m pytest -q \
|
||||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_adapter.py
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
4 passed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Регрессия адаптеров
|
||||
|
||||
Выполнена команда:
|
||||
|
||||
```bash
|
||||
python -m pytest -q \
|
||||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_quote_adapter.py \
|
||||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_adapter.py
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
6 passed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Регрессия полного WebSocket pipeline
|
||||
|
||||
Выполнена команда:
|
||||
|
||||
```bash
|
||||
python -m pytest -q \
|
||||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_quote_parser.py \
|
||||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_quote_mapper.py \
|
||||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_quote_adapter.py \
|
||||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_parser.py \
|
||||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_mapper.py \
|
||||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_adapter.py
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
65 passed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Проверка форматирования
|
||||
|
||||
Выполнена команда:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Ошибок форматирования не обнаружено.
|
||||
|
||||
---
|
||||
|
||||
# Изменённые файлы
|
||||
|
||||
```text
|
||||
src/market_data/acquisition/adapters/dzengi/websocket.py
|
||||
```
|
||||
|
||||
Создан файл:
|
||||
|
||||
```text
|
||||
tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_adapter.py
|
||||
```
|
||||
|
||||
Документация:
|
||||
|
||||
```text
|
||||
docs/migrations/build_059_7.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Совместимость
|
||||
|
||||
Build 059.7 полностью обратно совместим.
|
||||
|
||||
Не изменены:
|
||||
|
||||
- REST Candles Feed;
|
||||
- REST Candle mapper;
|
||||
- Quote parser;
|
||||
- Quote mapper;
|
||||
- WebSocket Quote Adapter;
|
||||
- ExchangeService;
|
||||
- runtime;
|
||||
- Trading;
|
||||
- Market Analysis;
|
||||
- каноническая модель `Candle`.
|
||||
|
||||
Новый адаптер пока не встроен в существующий runtime и не влияет на работу действующего торгового бота.
|
||||
|
||||
---
|
||||
|
||||
# Архитектурное значение
|
||||
|
||||
Build 059.7 завершает формирование верхнего уровня обработки WebSocket OHLC.
|
||||
|
||||
После предыдущих этапов система уже обладала всеми необходимыми компонентами:
|
||||
|
||||
```text
|
||||
Schema Validation
|
||||
Parser
|
||||
Value Validation
|
||||
Mapper
|
||||
```
|
||||
|
||||
Теперь появился единый компонент, объединяющий их в законченный конвейер.
|
||||
|
||||
После Build 059.7 архитектура обработки выглядит следующим образом:
|
||||
|
||||
```text
|
||||
Raw WebSocket Message
|
||||
│
|
||||
▼
|
||||
WebSocket OHLC Adapter
|
||||
│
|
||||
▼
|
||||
Schema Validation
|
||||
│
|
||||
▼
|
||||
Parser
|
||||
│
|
||||
▼
|
||||
Value Validation
|
||||
│
|
||||
▼
|
||||
Mapper
|
||||
│
|
||||
▼
|
||||
CandleCloseEvent
|
||||
```
|
||||
|
||||
Таким образом внешний код взаимодействует только с адаптером.
|
||||
|
||||
Внутренние детали обработки полностью инкапсулированы.
|
||||
|
||||
Это уменьшает связанность компонентов и соответствует архитектурным принципам Dzentra.
|
||||
|
||||
---
|
||||
|
||||
# Ограничения этапа
|
||||
|
||||
Build 059.7 намеренно **не реализует**:
|
||||
|
||||
- подписку на WebSocket;
|
||||
- управление соединением;
|
||||
- автоматическое получение сообщений;
|
||||
- публикацию событий в runtime;
|
||||
- REST reconciliation;
|
||||
- получение объёма свечи;
|
||||
- построение канонической модели `Candle`;
|
||||
- проверку соответствия REST и WebSocket данных;
|
||||
- восстановление после разрыва соединения;
|
||||
- повторную синхронизацию истории.
|
||||
|
||||
Все перечисленные задачи относятся к следующим этапам серии Build 059.
|
||||
|
||||
---
|
||||
|
||||
# Итог
|
||||
|
||||
Build 059.7 добавляет полноценный верхнеуровневый адаптер обработки WebSocket OHLC.
|
||||
|
||||
В результате система получила:
|
||||
|
||||
- единую точку входа для обработки WebSocket OHLC;
|
||||
- полностью инкапсулированный pipeline обработки;
|
||||
- автоматическое создание `received_at`;
|
||||
- единый интерфейс получения `CandleCloseEvent`;
|
||||
- сохранение независимых контрактов ошибок всех стадий обработки;
|
||||
- полный набор unit-тестов и успешное прохождение регрессии.
|
||||
|
||||
После завершения Build 059.7 конвейер обработки WebSocket OHLC полностью готов к интеграции с механизмом подписки и дальнейшей публикации событий внутри runtime.
|
||||
|
||||
---
|
||||
|
||||
# Следующий этап
|
||||
|
||||
```text
|
||||
Build 059.8 — Unified Adapter
|
||||
```
|
||||
|
||||
На этом этапе будет реализован единый адаптер верхнего уровня, способный принимать различные типы сообщений Dzengi WebSocket, определять их тип и направлять в соответствующий специализированный адаптер, формируя унифицированную точку входа для всего WebSocket-потока.
|
||||
Reference in New Issue
Block a user