Files
dzentra_bot/docs/migrations/build_059_6.md

16 KiB
Raw Blame History

Build 059.6 — WebSocket OHLC Mapper

Проект: Dzentra Подсистема: Market Data Acquisition Этап: 059.6 Статус: Completed


Цель

Реализовать преобразование проверенного WebSocket OHLC-события Dzengi во внутреннюю модель Dzentra:

CandleCloseEvent

Mapper должен стать последней стадией обработки WebSocket-сообщения перед передачей события во внутренний runtime.


Причина изменения

После завершения Build 059.5 система уже содержит:

Raw WebSocket JSON
        ↓
Schema Validation
        ↓
ValidatedWebSocketOhlcDocument
        ↓
Parser
        ↓
DzengiWebSocketOhlcEvent
        ↓
Value Validation

Однако transport-модель адаптера:

DzengiWebSocketOhlcEvent

не должна использоваться за пределами слоя интеграции с Dzengi.

Для внутренних компонентов требуется полностью source-independent модель:

CandleCloseEvent

Build 059.6 добавляет именно этот переход.


Реализовано

Изменены файлы:

src/market_data/acquisition/adapters/dzengi/mapper.py
src/market_data/acquisition/exceptions.py

Создан файл:

tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_mapper.py

Новый mapper

Добавлена функция:

map_dzengi_websocket_ohlc_to_candle_close_event()

Назначение функции:

DzengiWebSocketOhlcEvent
        ↓
CandleCloseEvent

Mapper предполагает, что ранее уже были выполнены:

  • schema validation;
  • parsing;
  • value validation.

Повторная проверка предметной корректности данных не выполняется.


Выполняемые преобразования

Mapper выполняет только преобразования типов.

Timestamp

Поле:

t

преобразуется из:

int (milliseconds)

в:

datetime (UTC)

OHLC

Поля:

o
h
l
c

преобразуются из:

str | int | float

в:

Decimal

Все вычисления внутри runtime далее выполняются только с использованием Decimal.


received_at

Mapper принимает дополнительный аргумент:

received_at

Тип:

datetime

Обязательное требование:

timezone-aware datetime

Naive datetime считается ошибкой mapper.


source

Mapper автоматически устанавливает источник:

dzengi_websocket_ohlc

Тем самым внутренние модели больше не зависят от транспортного контракта адаптера.


Новый тип ошибки

Добавлено исключение:

CandleWebSocketMappingError

Исключение используется исключительно на этапе mapping.

Это позволяет разделить ошибки различных стадий обработки.

Схема обработки стала следующей:

Transport
        ↓
CandleTransportError

Schema
        ↓
CandleWebSocketSchemaError

Parser
        ↓
CandleWebSocketParseError

Value Validation
        ↓
CandleWebSocketValueError

Mapper
        ↓
CandleWebSocketMappingError

Таким образом каждая стадия имеет собственный независимый контракт ошибок.


Почему не используется CandleMappingError

В проекте уже существует:

CandleMappingError

Однако он относится исключительно к REST Candles Feed.

REST mapper строит:

Canonical Candle

WebSocket mapper строит:

CandleCloseEvent

Это разные сущности.

Использование общего исключения привело бы к смешиванию двух различных контрактов.

Поэтому был введён отдельный тип ошибки.


Использование существующих helper-функций

В mapper уже присутствуют helper-функции для REST-свечей.

Например:

_required_candle_decimal()

Они намеренно не переиспользуются.

Причина:

они выбрасывают:

CandleMappingError

WebSocket mapper обязан выбрасывать:

CandleWebSocketMappingError

Поэтому были реализованы отдельные helper-функции.


Новые helper-функции

Добавлены:

_required_websocket_ohlc_decimal()

_websocket_ohlc_timestamp_ms_to_utc_datetime()

_require_websocket_ohlc_aware_datetime()

Они полностью изолированы от REST mapper.

Это исключает смешивание различных источников данных и их контрактов.


Преобразование времени

Поле:

t

содержит Unix timestamp в миллисекундах.

Mapper преобразует его через:

datetime.fromtimestamp(
    value / 1000,
    tz=timezone.utc,
)

В результате:

open_time

становится объектом:

datetime UTC

Внутренние слои больше не работают с миллисекундными timestamp.


Преобразование Decimal

Каждое поле:

open
high
low
close

проходит преобразование:

str | int | float
        ↓
Decimal

Дополнительно проверяется:

  • корректность числа;
  • конечность значения.

Контракт mapper

Mapper не выполняет:

  • schema validation;
  • parsing;
  • value validation;
  • REST reconciliation;
  • получение объёма;
  • создание канонической Candle.

Единственная задача mapper:

построить внутреннюю модель
CandleCloseEvent
из уже проверенного
DzengiWebSocketOhlcEvent

Что mapper НЕ делает

Mapper намеренно не выполняет следующие действия.

Не создаёт volume

Фактический runtime-контракт Dzengi содержит:

symbol
interval
type
t
o
h
l
c

Поля:

volume

не существует.

Поэтому mapper не должен:

  • подставлять 0;
  • использовать None;
  • вычислять объём самостоятельно.

Полный объём будет получен позднее через REST reconciliation.


Не создаёт Candle

Mapper не строит:

Candle

Причина проста.

Каноническая модель требует:

volume

которого WebSocket не содержит.

Следовательно единственным корректным результатом mapper является:

CandleCloseEvent

Не выполняет REST reconciliation

Build 059.6 заканчивается на построении внутреннего события.

Дальнейший pipeline выглядит следующим образом:

CandleCloseEvent
        ↓
REST reconciliation
        ↓
Canonical Candle

Сам reconciliation будет реализован отдельным этапом.


Поддерживаемые типы свечей

Mapper сохраняет значение:

candle_type

без изменения.

Поддерживаемые runtime-значения:

classic
heikin-ashi

Mapper не интерпретирует содержимое свечи.

Он лишь переносит значение в внутреннюю модель.


Обработка received_at

Поле:

received_at

не вычисляется автоматически.

Оно передаётся извне.

Это решение позволяет:

  • использовать единый момент получения сообщения;
  • исключить влияние задержек внутри mapper;
  • обеспечить единый источник времени для всей системы.

Mapper только проверяет:

timezone-aware datetime

Naive datetime приводит к ошибке:

CandleWebSocketMappingError

Конвейер после Build 059.6

После завершения этапа полный pipeline выглядит следующим образом:

Raw WebSocket message
        ↓
Schema Validation
        ↓
ValidatedWebSocketOhlcDocument
        ↓
Parser
        ↓
DzengiWebSocketOhlcEvent
        ↓
Value Validation
        ↓
Mapper
        ↓
CandleCloseEvent

На этом Build 059.6 завершается.

Следующий этап добавит использование новой модели внутри runtime.


Unit Tests

Создан файл:

tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_mapper.py

Проверяются:

  • построение CandleCloseEvent;
  • преобразование всех полей;
  • преобразование timestamp;
  • преобразование числовых типов в Decimal;
  • сохранение received_at;
  • поддержка heikin-ashi;
  • обрезка пробелов у symbol;
  • проверка timezone-aware datetime;
  • ошибка при невозможности преобразовать timestamp;
  • ошибка при некорректном Decimal;
  • ошибка при бесконечных значениях (NaN, Infinity);
  • отсутствие создания volume.

Всего выполнено:

16 tests

Проверка синтаксиса

Выполнена команда:

python -m compileall \
  src/market_data/acquisition/exceptions.py \
  src/market_data/acquisition/adapters/dzengi/mapper.py \
  tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_mapper.py

Результат:

успешно

Целевые тесты

Выполнена команда:

python -m pytest -q \
  tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_mapper.py

Результат:

16 passed

Регрессия mapper

Выполнена команда:

python -m pytest -q \
  tests/unit/market_data/acquisition/adapters/dzengi/test_mapper.py \
  tests/unit/market_data/acquisition/adapters/dzengi/test_quote_mapper.py \
  tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_quote_mapper.py \
  tests/unit/market_data/acquisition/adapters/dzengi/test_candle_mapper.py \
  tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_mapper.py

Результат:

52 passed

Дополнительная регрессия

Выполнена команда:

python -m pytest -q \
  tests/unit/market_data/acquisition/models \
  tests/unit/market_data/acquisition/validation/test_websocket_ohlc_values.py

Результат:

126 passed

Проверка форматирования

Выполнена команда:

git diff --check

Ошибок форматирования не обнаружено.


Изменённые файлы

src/market_data/acquisition/adapters/dzengi/mapper.py

src/market_data/acquisition/exceptions.py

Создан файл:

tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_ohlc_mapper.py

Документация:

docs/migrations/build_059_6.md

Совместимость

Build 059.6 полностью обратно совместим.

Не изменены:

  • REST Candles Feed;
  • REST Candle mapper;
  • Quote mapper;
  • Instrument mapper;
  • ExchangeService;
  • runtime;
  • Trading;
  • Market Analysis;
  • каноническая модель Candle.

Новый mapper пока не используется существующим runtime и не влияет на работу действующего торгового бота.


Архитектурное значение

Build 059.6 завершает формирование полного внутреннего конвейера преобразования WebSocket OHLC.

После этого этапа система имеет чёткое разделение уровней ответственности:

Transport Layer
        │
        ▼
DzengiWebSocketOhlcEvent
        │
        ▼
Validation Layer
        │
        ▼
Mapper Layer
        │
        ▼
CandleCloseEvent

Каждый уровень отвечает только за собственную задачу:

Уровень Ответственность
Schema Validation Проверка структуры входящего сообщения
Parser Построение transport-модели
Value Validation Проверка допустимости значений
Mapper Преобразование transport-модели во внутреннюю модель
Runtime Использование внутренней модели

Такое разделение соответствует общей архитектуре Dzentra и исключает смешивание обязанностей между слоями.


Ограничения этапа

Build 059.6 намеренно не реализует:

  • подписку на WebSocket;
  • публикацию события в runtime;
  • REST reconciliation;
  • получение объёма свечи;
  • создание канонической Candle;
  • проверку соответствия REST и WebSocket OHLC;
  • восстановление после разрыва соединения;
  • повторную синхронизацию истории свечей.

Все перечисленные задачи относятся к последующим этапам Build 059.


Итог

Build 059.6 добавляет полноценный mapper:

DzengiWebSocketOhlcEvent
        ↓
CandleCloseEvent

В результате система получила:

  • источник-независимую внутреннюю модель события закрытия свечи;
  • корректное преобразование timestamp в datetime (UTC);
  • преобразование цен в Decimal;
  • собственный контракт ошибок (CandleWebSocketMappingError);
  • полную изоляцию WebSocket-контракта Dzengi от внутренних компонентов Dzentra;
  • набор unit-тестов, подтверждающих корректность преобразования и обработки ошибок.

После Build 059.6 цепочка обработки WebSocket OHLC стала полностью завершённой вплоть до внутреннего события Dzentra.


Следующий этап

Build 059.7 — WebSocket Adapter

На этом этапе новый mapper будет встроен в адаптер получения рыночных данных, чтобы при поступлении подтверждённого WebSocket OHLC-сообщения формировался CandleCloseEvent, который затем сможет быть передан в механизм REST reconciliation.