Files
dzentra_bot/docs/migrations/build_029.md

19 KiB
Raw Blame History

Build 029 — Dzengi Mapper и Quotes Handler

Статус

Завершён


1. Цель Build 029

Цель Build 029 — реализовать преобразование специализированной модели REST-котировки Dzengi в каноническую модель Quote и создать обработчик полного цикла преобразования сырого REST-документа в проверенную внутреннюю модель котировки.

Build является частью поэтапной миграции подсистемы:

Quotes Feed

в новую архитектуру:

src/market_data/acquisition/

На данном этапе реализованы:

  • специализированный Dzengi quote mapper;
  • преобразование DzengiTicker24hrResponse в канонический Quote;
  • преобразование цен в Decimal;
  • преобразование биржевого timestamp в timezone-aware UTC datetime;
  • фиксация времени получения котировки;
  • специализированный QuotesHandler;
  • полная handler-цепочка обработки сырого REST-документа.

Подключение Quotes Feed, registry, Acquisition Service, Quote Store, ExchangeService facade и runtime-потребителей в данный Build не входит.


2. Место Build 029 в плане миграции Quotes Feed

Утверждённая последовательность:

Build 026 — Аудит текущего контура Quotes Feed
Build 027 — Каноническая модель Quote и специализированные контракты
Build 028 — Dzengi REST quote models, parser и validation
Build 029 — Dzengi mapper и Quotes Handler
Build 030 — Quotes Feed и регистрация в Acquisition Service
Build 031 — Подключение нового REST Quotes Feed под legacy ExchangeService facade
Build 032 — Канонический Quote Store
Build 033 — Перенос MarketPriceCache на Quote Store
Build 034 — Dzengi WebSocket quote parsing и адаптер
Build 035 — Перевод market runtime на Quotes Feed
Build 036 — Перевод read-only и UI-потребителей
Build 037 — Перевод execution-потребителей
Build 038 — Удаление legacy TickerPrice и market snapshot dict layer
Build 039 — Удаление legacy quote parsing и MarketPriceCache
Build 040 — Финальная архитектурная проверка Quotes Feed

Build 029 продолжает фундамент, созданный в Builds 027028.

После его завершения сформирована цепочка:

raw Dzengi REST document
        ↓
schema validation
        ↓
Dzengi quote parser
        ↓
value validation
        ↓
DzengiTicker24hrResponse
        ↓
Dzengi quote mapper
        ↓
canonical Quote

3. Исходное состояние перед Build 029

До начала Build 029 уже были реализованы:

Build 027

src/market_data/acquisition/models/quote.py
src/market_data/acquisition/protocol.py

Были определены:

  • каноническая модель Quote;
  • контракт источника сырого quote-документа;
  • контракт обработчика quote-документа;
  • контракт готового Quotes Feed.

Build 028

src/market_data/acquisition/adapters/dzengi/models.py
src/market_data/acquisition/adapters/dzengi/parser.py
src/market_data/acquisition/validation/schema.py
src/market_data/acquisition/validation/values.py
src/market_data/acquisition/exceptions.py

Были реализованы:

  • модель REST-ответа /api/v1/ticker/24hr;
  • parser quote payload;
  • schema validation;
  • value validation;
  • специализированные ошибки Acquisition layer.

Отсутствовал слой, преобразующий проверенную Dzengi-specific модель в канонический Quote, а также единая точка оркестрации всей цепочки обработки сырого документа.


4. Изменённые и добавленные файлы

В рамках Build 029 изменены только два исходных файла:

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

Добавлены два специализированных файла тестов:

tests/unit/market_data/acquisition/adapters/dzengi/test_quote_mapper.py
tests/unit/market_data/acquisition/handlers/test_quotes_handler.py

Другие файлы в рамках фактически применённого Build 029 не изменялись.


5. Dzengi Quote Mapper

В файле:

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

реализовано преобразование:

DzengiTicker24hrResponse
        ↓
Quote

Mapper является архитектурной границей между:

exchange-specific adapter model

и:

canonical Acquisition model

Его ответственность:

  • принять проверенную модель DzengiTicker24hrResponse;
  • преобразовать биржевые значения цен в канонический тип;
  • преобразовать биржевой timestamp;
  • определить источник данных;
  • зафиксировать время получения котировки;
  • создать канонический Quote.

Mapper не должен:

  • выполнять REST-запрос;
  • разбирать сырой JSON payload;
  • выполнять schema validation сырого документа;
  • управлять store или cache;
  • обращаться к ExchangeService;
  • содержать UI-логику;
  • содержать execution-логику.

6. Преобразование модели Dzengi в канонический Quote

Исходная модель адаптера содержит данные, соответствующие REST-ответу Dzengi:

symbol
lastPrice
bidPrice
askPrice
closeTime

После parsing и validation эти данные представлены специализированной моделью:

DzengiTicker24hrResponse

Mapper преобразует её в:

Quote

с канонической семантикой:

symbol
last_price
bid_price
ask_price
source_timestamp
received_at
source

Таким образом, API-specific имена:

lastPrice
bidPrice
askPrice
closeTime

не выходят за пределы Dzengi adapter layer.


7. Использование Decimal для цен

Цены преобразуются в Decimal.

Целевая семантика:

last_price: Decimal
bid_price: Decimal
ask_price: Decimal

Это решение исключает ненужную потерю точности при преобразовании рыночных цен через бинарный float.

Архитектурная цепочка:

Dzengi string price
        ↓
Decimal
        ↓
canonical Quote

Например:

"64159.45"
        ↓
Decimal("64159.45")

Mapper не должен сначала преобразовывать строку в float, а затем создавать Decimal, поскольку такой путь способен внести артефакты двоичного представления числа.


8. Преобразование биржевого timestamp

Поле Dzengi:

closeTime

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

Mapper преобразует его в timezone-aware UTC datetime.

Семантика преобразования:

closeTime milliseconds
        ↓
UTC datetime
        ↓
Quote.source_timestamp

Использование timezone-aware значения необходимо для однозначного представления времени рыночного события и последующих операций:

  • freshness calculation;
  • sequence validation;
  • event ordering;
  • диагностика задержек;
  • сопоставление данных из нескольких источников.

9. Время получения котировки

Помимо биржевого времени события, канонический Quote содержит время фактического получения данных платформой:

received_at

Разделение двух временных характеристик принципиально:

source_timestamp

означает время, указанное источником данных;

received_at

означает время, когда котировка была преобразована во внутреннюю модель платформы.

Это создаёт фундамент для последующего определения:

  • возраста котировки;
  • сетевой задержки;
  • freshness;
  • stale data;
  • задержки между биржей и локальной системой.

10. Источник котировки

Канонический Quote получает идентификатор источника:

dzengi

Это позволяет внутренней модели не зависеть от конкретного adapter-класса, сохраняя при этом происхождение рыночных данных.

Целевая модель допускает дальнейшую работу с несколькими источниками:

Dzengi
Binance
Coinbase
другие источники

При этом приоритетным источником для торговых решений остаётся биржа исполнения.


11. Quotes Handler

В файле:

src/market_data/acquisition/handlers/quotes_handler.py

реализован специализированный обработчик quote-документа.

Его ответственность — оркестрировать существующие специализированные стадии обработки:

raw document
        ↓
schema validation
        ↓
parser
        ↓
value validation
        ↓
mapper
        ↓
Quote

Handler является единой точкой преобразования:

object → Quote

Он не должен самостоятельно дублировать внутреннюю реализацию:

  • schema validation;
  • parsing;
  • value validation;
  • mapping.

Вместо этого handler координирует специализированные компоненты.


12. Полная цепочка обработки

После Build 029 полный путь REST-документа выглядит следующим образом:

{
    "askPrice": "64159.55",
    "bidPrice": "64159.45",
    "closeTime": 1783887270312,
    "lastPrice": "64159.45",
    "symbol": "BTC/USD_LEVERAGE"
}
        ↓
schema validation
        ↓
Dzengi REST quote parser
        ↓
DzengiTicker24hrResponse
        ↓
value validation
        ↓
Dzengi quote mapper
        ↓
Quote(
    symbol=...,
    last_price=...,
    bid_price=...,
    ask_price=...,
    source_timestamp=...,
    received_at=...,
    source=...
)

Таким образом, верхние слои платформы больше не обязаны знать формат ответа Dzengi.


13. Архитектурные решения Build 029

13.1. Mapper изолирует специфику Dzengi

Только adapter layer знает о:

DzengiTicker24hrResponse
lastPrice
bidPrice
askPrice
closeTime

После mapping верхние слои работают исключительно с:

Quote

13.2. Handler не зависит от ExchangeService

Новый QuotesHandler не использует:

src.integrations.exchange.service.ExchangeService

Направление зависимостей остаётся правильным:

external Dzengi payload
        ↓
Acquisition adapter
        ↓
Acquisition handler
        ↓
canonical Quote

Обратной зависимости новой подсистемы от legacy integration layer нет.


13.3. Handler не является Feed

QuotesHandler отвечает только за преобразование документа:

object → Quote

Он не отвечает за получение документа от биржи.

Получение данных будет ответственностью:

src/market_data/acquisition/feeds/quotes_feed.py

на следующем этапе миграции.


13.4. Handler не является Store

QuotesHandler не сохраняет котировки.

Хранение будет реализовано отдельно:

Build 032 — Канонический Quote Store

Такое разделение предотвращает смешивание:

acquisition
processing
storage

13.5. Build не изменяет production runtime

В Build 029 не изменены:

src/integrations/exchange/service.py
src/integrations/exchange/market_cache.py
src/integrations/exchange/market_stream.py
src/integrations/exchange/market_data_runner.py

Не переведены:

UI consumers
execution consumers
strategy consumers
diagnostics consumers

Работающий бот продолжает использовать прежний runtime-контур.


14. Что намеренно не реализовано

В Build 029 не входят:

Quotes Feed
регистрация Quotes Feed
подключение к Acquisition Service
подключение нового REST Quotes Feed к ExchangeService facade
Quote Store
перенос MarketPriceCache на Quote Store
WebSocket quote parsing
WebSocket quote adapter
перевод market runtime
перевод read-only потребителей
перевод UI-потребителей
перевод execution-потребителей
удаление TickerPrice
удаление market snapshot dict layer
удаление legacy quote parsing
удаление MarketPriceCache

Каждая из этих задач выполняется только в соответствующем последующем Build.


15. Проверки

Выполнена синтаксическая проверка:

python -m py_compile \
  src/market_data/acquisition/adapters/dzengi/mapper.py \
  src/market_data/acquisition/handlers/quotes_handler.py \
  tests/unit/market_data/acquisition/adapters/dzengi/test_quote_mapper.py \
  tests/unit/market_data/acquisition/handlers/test_quotes_handler.py

Результат:

Успешно.

Выполнены специализированные тесты Build 029:

python -m pytest \
  tests/unit/market_data/acquisition/adapters/dzengi/test_quote_mapper.py \
  tests/unit/market_data/acquisition/handlers/test_quotes_handler.py \
  -q

Результат:

12 passed in 0.03s

Выполнена полная регрессия проекта:

python -m pytest -q

Результат:

457 passed in 0.26s

Регрессий не обнаружено.

Количество тестов увеличилось:

После Build 028: 445 passed
После Build 029: 457 passed

Добавлено:

12 специализированных тестов

16. Критерии завершения Build 029

Build 029 считается завершённым, поскольку выполнены все необходимые условия:

  • реализован специализированный Dzengi quote mapper;
  • DzengiTicker24hrResponse преобразуется в канонический Quote;
  • API-specific имена не выходят за пределы adapter layer;
  • цены преобразуются в Decimal;
  • не используется промежуточное преобразование цен через float;
  • closeTime преобразуется в timezone-aware UTC datetime;
  • фиксируется received_at;
  • сохраняется источник котировки;
  • реализован специализированный QuotesHandler;
  • handler оркестрирует полный цикл обработки сырого документа;
  • handler не дублирует ответственность parser;
  • handler не дублирует ответственность validation;
  • handler не дублирует ответственность mapper;
  • новая реализация не зависит от ExchangeService;
  • новая реализация не зависит от MarketPriceCache;
  • production runtime не изменён;
  • специализированные тесты проходят;
  • полная регрессия проходит.

17. Итог

В результате Build 029 завершён слой преобразования REST-котировки Dzengi во внутреннюю каноническую модель платформы:

Dzengi REST payload
        ↓
schema validation
        ↓
parser
        ↓
value validation
        ↓
DzengiTicker24hrResponse
        ↓
mapper
        ↓
canonical Quote

Также создан единый специализированный обработчик:

QuotesHandler

который предоставляет операцию:

raw document → canonical Quote

При этом сохранены ключевые архитектурные свойства миграции:

  • новая реализация развивается параллельно legacy-контуру;
  • работающий бот не сломан;
  • ExchangeService не изменён;
  • MarketPriceCache не изменён;
  • runtime-потребители не изменены;
  • Dzengi-specific формат изолирован внутри adapter layer;
  • верхние слои получают каноническую модель Quote;
  • mapping и orchestration разделены по ответственности;
  • сохранена возможность безопасного поэтапного переключения системы.

Build 029 завершён.

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

Build 030 — Quotes Feed и регистрация в Acquisition Service