768 lines
21 KiB
Markdown
768 lines
21 KiB
Markdown
# 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-потока. |