Files
dzentra_bot/docs/migrations/build_029.md

717 lines
19 KiB
Markdown
Raw Permalink 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 029 — Dzengi Mapper и Quotes Handler
## Статус
**Завершён**
---
## 1. Цель Build 029
Цель Build 029 — реализовать преобразование специализированной модели REST-котировки Dzengi в каноническую модель `Quote` и создать обработчик полного цикла преобразования сырого REST-документа в проверенную внутреннюю модель котировки.
Build является частью поэтапной миграции подсистемы:
**Quotes Feed**
в новую архитектуру:
```text
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
Утверждённая последовательность:
```text
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.
После его завершения сформирована цепочка:
```text
raw Dzengi REST document
schema validation
Dzengi quote parser
value validation
DzengiTicker24hrResponse
Dzengi quote mapper
canonical Quote
```
---
## 3. Исходное состояние перед Build 029
До начала Build 029 уже были реализованы:
### Build 027
```text
src/market_data/acquisition/models/quote.py
src/market_data/acquisition/protocol.py
```
Были определены:
- каноническая модель `Quote`;
- контракт источника сырого quote-документа;
- контракт обработчика quote-документа;
- контракт готового `Quotes Feed`.
### Build 028
```text
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 изменены только два исходных файла:
```text
src/market_data/acquisition/adapters/dzengi/mapper.py
src/market_data/acquisition/handlers/quotes_handler.py
```
Добавлены два специализированных файла тестов:
```text
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
В файле:
```text
src/market_data/acquisition/adapters/dzengi/mapper.py
```
реализовано преобразование:
```text
DzengiTicker24hrResponse
Quote
```
Mapper является архитектурной границей между:
```text
exchange-specific adapter model
```
и:
```text
canonical Acquisition model
```
Его ответственность:
- принять проверенную модель `DzengiTicker24hrResponse`;
- преобразовать биржевые значения цен в канонический тип;
- преобразовать биржевой timestamp;
- определить источник данных;
- зафиксировать время получения котировки;
- создать канонический `Quote`.
Mapper не должен:
- выполнять REST-запрос;
- разбирать сырой JSON payload;
- выполнять schema validation сырого документа;
- управлять store или cache;
- обращаться к `ExchangeService`;
- содержать UI-логику;
- содержать execution-логику.
---
## 6. Преобразование модели Dzengi в канонический Quote
Исходная модель адаптера содержит данные, соответствующие REST-ответу Dzengi:
```text
symbol
lastPrice
bidPrice
askPrice
closeTime
```
После parsing и validation эти данные представлены специализированной моделью:
```text
DzengiTicker24hrResponse
```
Mapper преобразует её в:
```text
Quote
```
с канонической семантикой:
```text
symbol
last_price
bid_price
ask_price
source_timestamp
received_at
source
```
Таким образом, API-specific имена:
```text
lastPrice
bidPrice
askPrice
closeTime
```
не выходят за пределы Dzengi adapter layer.
---
## 7. Использование Decimal для цен
Цены преобразуются в `Decimal`.
Целевая семантика:
```text
last_price: Decimal
bid_price: Decimal
ask_price: Decimal
```
Это решение исключает ненужную потерю точности при преобразовании рыночных цен через бинарный `float`.
Архитектурная цепочка:
```text
Dzengi string price
Decimal
canonical Quote
```
Например:
```text
"64159.45"
Decimal("64159.45")
```
Mapper не должен сначала преобразовывать строку в `float`, а затем создавать `Decimal`, поскольку такой путь способен внести артефакты двоичного представления числа.
---
## 8. Преобразование биржевого timestamp
Поле Dzengi:
```text
closeTime
```
содержит Unix timestamp в миллисекундах.
Mapper преобразует его в timezone-aware UTC `datetime`.
Семантика преобразования:
```text
closeTime milliseconds
UTC datetime
Quote.source_timestamp
```
Использование timezone-aware значения необходимо для однозначного представления времени рыночного события и последующих операций:
- freshness calculation;
- sequence validation;
- event ordering;
- диагностика задержек;
- сопоставление данных из нескольких источников.
---
## 9. Время получения котировки
Помимо биржевого времени события, канонический `Quote` содержит время фактического получения данных платформой:
```text
received_at
```
Разделение двух временных характеристик принципиально:
```text
source_timestamp
```
означает время, указанное источником данных;
```text
received_at
```
означает время, когда котировка была преобразована во внутреннюю модель платформы.
Это создаёт фундамент для последующего определения:
- возраста котировки;
- сетевой задержки;
- freshness;
- stale data;
- задержки между биржей и локальной системой.
---
## 10. Источник котировки
Канонический `Quote` получает идентификатор источника:
```text
dzengi
```
Это позволяет внутренней модели не зависеть от конкретного adapter-класса, сохраняя при этом происхождение рыночных данных.
Целевая модель допускает дальнейшую работу с несколькими источниками:
```text
Dzengi
Binance
Coinbase
другие источники
```
При этом приоритетным источником для торговых решений остаётся биржа исполнения.
---
## 11. Quotes Handler
В файле:
```text
src/market_data/acquisition/handlers/quotes_handler.py
```
реализован специализированный обработчик quote-документа.
Его ответственность — оркестрировать существующие специализированные стадии обработки:
```text
raw document
schema validation
parser
value validation
mapper
Quote
```
Handler является единой точкой преобразования:
```text
object → Quote
```
Он не должен самостоятельно дублировать внутреннюю реализацию:
- schema validation;
- parsing;
- value validation;
- mapping.
Вместо этого handler координирует специализированные компоненты.
---
## 12. Полная цепочка обработки
После Build 029 полный путь REST-документа выглядит следующим образом:
```text
{
"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 знает о:
```text
DzengiTicker24hrResponse
lastPrice
bidPrice
askPrice
closeTime
```
После mapping верхние слои работают исключительно с:
```text
Quote
```
---
### 13.2. Handler не зависит от ExchangeService
Новый `QuotesHandler` не использует:
```text
src.integrations.exchange.service.ExchangeService
```
Направление зависимостей остаётся правильным:
```text
external Dzengi payload
Acquisition adapter
Acquisition handler
canonical Quote
```
Обратной зависимости новой подсистемы от legacy integration layer нет.
---
### 13.3. Handler не является Feed
`QuotesHandler` отвечает только за преобразование документа:
```text
object → Quote
```
Он не отвечает за получение документа от биржи.
Получение данных будет ответственностью:
```text
src/market_data/acquisition/feeds/quotes_feed.py
```
на следующем этапе миграции.
---
### 13.4. Handler не является Store
`QuotesHandler` не сохраняет котировки.
Хранение будет реализовано отдельно:
```text
Build 032 — Канонический Quote Store
```
Такое разделение предотвращает смешивание:
```text
acquisition
processing
storage
```
---
### 13.5. Build не изменяет production runtime
В Build 029 не изменены:
```text
src/integrations/exchange/service.py
src/integrations/exchange/market_cache.py
src/integrations/exchange/market_stream.py
src/integrations/exchange/market_data_runner.py
```
Не переведены:
```text
UI consumers
execution consumers
strategy consumers
diagnostics consumers
```
Работающий бот продолжает использовать прежний runtime-контур.
---
## 14. Что намеренно не реализовано
В Build 029 не входят:
```text
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. Проверки
Выполнена синтаксическая проверка:
```bash
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
```
Результат:
```text
Успешно.
```
Выполнены специализированные тесты Build 029:
```bash
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
```
Результат:
```text
12 passed in 0.03s
```
Выполнена полная регрессия проекта:
```bash
python -m pytest -q
```
Результат:
```text
457 passed in 0.26s
```
Регрессий не обнаружено.
Количество тестов увеличилось:
```text
После Build 028: 445 passed
После Build 029: 457 passed
```
Добавлено:
```text
12 специализированных тестов
```
---
## 16. Критерии завершения Build 029
Build 029 считается завершённым, поскольку выполнены все необходимые условия:
- [x] реализован специализированный Dzengi quote mapper;
- [x] `DzengiTicker24hrResponse` преобразуется в канонический `Quote`;
- [x] API-specific имена не выходят за пределы adapter layer;
- [x] цены преобразуются в `Decimal`;
- [x] не используется промежуточное преобразование цен через `float`;
- [x] `closeTime` преобразуется в timezone-aware UTC `datetime`;
- [x] фиксируется `received_at`;
- [x] сохраняется источник котировки;
- [x] реализован специализированный `QuotesHandler`;
- [x] handler оркестрирует полный цикл обработки сырого документа;
- [x] handler не дублирует ответственность parser;
- [x] handler не дублирует ответственность validation;
- [x] handler не дублирует ответственность mapper;
- [x] новая реализация не зависит от `ExchangeService`;
- [x] новая реализация не зависит от `MarketPriceCache`;
- [x] production runtime не изменён;
- [x] специализированные тесты проходят;
- [x] полная регрессия проходит.
---
## 17. Итог
В результате Build 029 завершён слой преобразования REST-котировки Dzengi во внутреннюю каноническую модель платформы:
```text
Dzengi REST payload
schema validation
parser
value validation
DzengiTicker24hrResponse
mapper
canonical Quote
```
Также создан единый специализированный обработчик:
```text
QuotesHandler
```
который предоставляет операцию:
```text
raw document → canonical Quote
```
При этом сохранены ключевые архитектурные свойства миграции:
- новая реализация развивается параллельно legacy-контуру;
- работающий бот не сломан;
- `ExchangeService` не изменён;
- `MarketPriceCache` не изменён;
- runtime-потребители не изменены;
- Dzengi-specific формат изолирован внутри adapter layer;
- верхние слои получают каноническую модель `Quote`;
- mapping и orchestration разделены по ответственности;
- сохранена возможность безопасного поэтапного переключения системы.
**Build 029 завершён.**
Следующий этап:
```text
Build 030 — Quotes Feed и регистрация в Acquisition Service
```