Files
dzentra_bot/docs/migrations/build_028.md

591 lines
16 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 028 — Dzengi REST Quote Models, Parser и Validation
## Статус
**Завершён**
---
## 1. Цель Build 028
Цель Build 028 — реализовать специализированный слой приёма, разбора и первичной проверки REST-ответа Dzengi для текущей рыночной котировки инструмента, не изменяя существующее поведение работающего бота и не подключая новую реализацию к production runtime до следующих этапов миграции.
Build является частью поэтапной миграции подсистемы:
**Quotes Feed**
в новую архитектуру:
```text
src/market_data/acquisition/
```
На данном этапе реализованы:
- модель сырого REST-ответа Dzengi;
- parser REST-ответа `/api/v1/ticker/24hr`;
- проверка структуры входящего payload;
- проверка допустимости значений котировки;
- специализированные ошибки обработки quote payload.
Подключение mapper, handler, feed, service, store и перевод runtime-потребителей в данный Build не входят.
---
## 2. Место Build 028 в плане миграции 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 028 продолжает фундамент, созданный в Build 027.
Целевая цепочка после завершения следующих этапов:
```text
Dzengi REST /api/v1/ticker/24hr
adapters/dzengi/rest.py
adapters/dzengi/parser.py
adapters/dzengi/models.py
validation/schema.py
validation/values.py
adapters/dzengi/mapper.py
handlers/quotes_handler.py
feeds/quotes_feed.py
acquisition/service.py
Quote Store
потребители платформы
```
---
## 3. Исходные данные
Для проектирования реализации использован реальный успешный ответ Dzengi:
```json
{
"askPrice": "64159.55",
"bidPrice": "64159.45",
"closeTime": 1783887270312,
"highPrice": "64261.45",
"lastPrice": "64159.45",
"lastQty": "5.0",
"lowPrice": "63590.7",
"openPrice": "63785.75",
"openTime": 1783814400000,
"prevClosePrice": "63785.75",
"priceChange": "368.85",
"priceChangePercent": "0.57822",
"quoteVolume": "616402.92146",
"symbol": "BTC/USD_LEVERAGE",
"volume": "9.6002",
"weightedAvgPrice": "64159.50"
}
```
Для базовой модели текущей котировки используются поля:
```text
symbol
lastPrice
bidPrice
askPrice
closeTime
```
Остальные поля ответа `/api/v1/ticker/24hr` относятся к расширенной 24-часовой статистике рынка и не включаются в базовую модель `Quote`.
Это сохраняет правильное разделение ответственностей между:
- текущей котировкой;
- рыночной статистикой;
- OHLCV;
- trades;
- order book;
- другими специализированными типами рыночных данных.
---
## 4. Реализованные компоненты
В рамках 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
```
Добавлены специализированные тесты:
```text
tests/unit/market_data/acquisition/adapters/dzengi/test_quote_parser.py
tests/unit/market_data/acquisition/validation/test_quote_schema.py
tests/unit/market_data/acquisition/validation/test_quote_values.py
```
---
## 5. Dzengi REST Quote Model
В файле:
```text
src/market_data/acquisition/adapters/dzengi/models.py
```
реализована модель сырой котировки Dzengi.
Её ответственность:
- представить уже разобранные поля ответа Dzengi;
- сохранить биржевую семантику полей;
- не зависеть от legacy-моделей `TickerPrice` и `MarketPriceSnapshot`;
- не выполнять бизнес-интерпретацию;
- не выполнять преобразование во внутреннюю каноническую модель `Quote`.
Архитектурная граница:
```text
Dzengi API payload
Dzengi REST quote model
mapper
canonical Quote
```
Модель адаптера является специфичной для Dzengi и не должна использоваться напрямую верхними слоями платформы.
---
## 6. REST Quote Parser
В файле:
```text
src/market_data/acquisition/adapters/dzengi/parser.py
```
реализован специализированный parser REST-котировки.
Его ответственность:
1. принять необработанный ответ API;
2. определить фактический quote payload;
3. поддержать прямую структуру ответа;
4. поддержать wrapped payload;
5. проверить структуру через schema validation;
6. извлечь необходимые поля;
7. проверить значения через value validation;
8. вернуть специализированную Dzengi quote model.
Поддерживаемые формы payload:
```text
Прямой payload
```
```json
{
"symbol": "BTC/USD_LEVERAGE",
"lastPrice": "64159.45",
"bidPrice": "64159.45",
"askPrice": "64159.55",
"closeTime": 1783887270312
}
```
и wrapped payload:
```json
{
"payload": {
"symbol": "BTC/USD_LEVERAGE",
"lastPrice": "64159.45",
"bidPrice": "64159.45",
"askPrice": "64159.55",
"closeTime": 1783887270312
}
}
```
Parser не создаёт канонический `Quote`. Это ответственность mapper, реализуемого в Build 029.
---
## 7. Schema Validation
В файле:
```text
src/market_data/acquisition/validation/schema.py
```
реализована проверка структуры quote payload.
Проверяются обязательные поля:
```text
symbol
lastPrice
bidPrice
askPrice
closeTime
```
Schema validation отвечает только на вопрос:
> Имеет ли входящее сообщение необходимую структуру для дальнейшей обработки?
Она не должна:
- преобразовывать значения;
- вычислять midpoint;
- определять freshness;
- создавать `Quote`;
- обращаться к сети;
- обращаться к store;
- зависеть от `ExchangeService`.
---
## 8. Value Validation
В файле:
```text
src/market_data/acquisition/validation/values.py
```
реализована проверка допустимости значений REST-котировки.
Контролируются следующие инварианты:
```text
symbol != empty
last_price > 0
bid_price > 0
ask_price > 0
close_time >= 0
bid_price <= ask_price
```
Проверка:
```text
bid_price <= ask_price
```
является важным базовым инвариантом котировки.
Payload, в котором:
```text
bid_price > ask_price
```
не должен бесконтрольно попадать в канонический слой платформы.
---
## 9. Исключения
В файле:
```text
src/market_data/acquisition/exceptions.py
```
используются специализированные исключения Acquisition layer для ошибок обработки рыночных данных.
Ошибки quote parsing и validation не должны зависеть от:
```text
src/integrations/exchange/exceptions.py
```
Это необходимо для соблюдения направления зависимостей:
```text
market_data/acquisition
X
integrations/exchange legacy layer
```
Новая подсистема Acquisition не должна архитектурно зависеть от legacy `ExchangeService`.
---
## 10. Архитектурные решения Build 028
### 10.1. Каноническая модель не зависит от формата Dzengi
Поля API:
```text
lastPrice
bidPrice
askPrice
closeTime
```
существуют только внутри Dzengi adapter layer.
Во внутренних слоях платформы используются канонические имена:
```text
last_price
bid_price
ask_price
source_timestamp_ms
```
Преобразование между ними является ответственностью mapper.
---
### 10.2. Parser не выполняет mapping
Разделение сохраняется строго:
```text
parser
разбирает внешний payload
validation
проверяет структуру и значения
mapper
преобразует adapter model в canonical model
```
Это предотвращает смешивание:
- API-specific parsing;
- validation;
- domain mapping.
---
### 10.3. REST quote не зависит от legacy TickerPrice
Новая цепочка не использует:
```text
src.integrations.exchange.models.TickerPrice
```
`TickerPrice` остаётся временной legacy-моделью и будет удалён только после перевода всех потребителей согласно плану миграции.
---
### 10.4. REST quote не зависит от MarketPriceCache
Build 028 не изменяет:
```text
src/integrations/exchange/market_cache.py
```
и не записывает данные в:
```text
MarketPriceCache
```
Миграция хранения выполняется отдельно:
```text
Build 032 — Канонический Quote Store
Build 033 — Перенос MarketPriceCache на Quote Store
```
---
### 10.5. Runtime-поведение бота не изменено
На этапе Build 028:
- новый parser не подключён к production runtime;
- `ExchangeService` продолжает работать по прежнему интерфейсу;
- `MarketPriceCache` не изменён;
- `MarketDataRunner` не изменён;
- execution-потребители не изменены;
- UI-потребители не изменены.
Таким образом, Build 028 является безопасным additive-этапом миграции.
---
## 11. Что намеренно не реализовано
В Build 028 не входят:
```text
Dzengi mapper
Quotes Handler
Quotes Feed
регистрация Quotes Feed
подключение к Acquisition Service
подключение к ExchangeService facade
Quote Store
перенос MarketPriceCache
WebSocket quote parser
перевод MarketDataRunner
перевод UI-потребителей
перевод execution-потребителей
удаление TickerPrice
удаление market snapshot dict layer
удаление MarketPriceCache
```
Эти изменения выполняются только в соответствующих последующих Build.
---
## 12. Проверки
Выполнена синтаксическая проверка:
```bash
python -m py_compile \
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 \
tests/unit/market_data/acquisition/adapters/dzengi/test_quote_parser.py \
tests/unit/market_data/acquisition/validation/test_quote_schema.py \
tests/unit/market_data/acquisition/validation/test_quote_values.py
```
Результат:
```text
Успешно.
```
Выполнены специализированные тесты Build 028:
```bash
python -m pytest \
tests/unit/market_data/acquisition/adapters/dzengi/test_quote_parser.py \
tests/unit/market_data/acquisition/validation/test_quote_schema.py \
tests/unit/market_data/acquisition/validation/test_quote_values.py \
-q
```
Результат:
```text
22 passed in 0.02s
```
Выполнена полная регрессия проекта:
```bash
python -m pytest -q
```
Результат:
```text
445 passed in 0.27s
```
Регрессий не обнаружено.
---
## 13. Критерии завершения Build 028
Build 028 считается завершённым, поскольку выполнены все необходимые условия:
- [x] получен реальный успешный ответ `/api/v1/ticker/24hr`;
- [x] определён минимальный набор полей текущей котировки;
- [x] реализована специализированная Dzengi REST quote model;
- [x] реализован REST quote parser;
- [x] поддержан прямой payload;
- [x] поддержан wrapped payload;
- [x] реализована schema validation;
- [x] реализована value validation;
- [x] проверяются положительные цены;
- [x] проверяется временная метка;
- [x] проверяется инвариант `bid_price <= ask_price`;
- [x] новая реализация не зависит от legacy `TickerPrice`;
- [x] новая реализация не зависит от `MarketPriceCache`;
- [x] production runtime не изменён;
- [x] специализированные тесты проходят;
- [x] полная регрессия проходит.
---
## 14. Итог
В результате Build 028 создан специализированный входной контур для REST-котировок Dzengi:
```text
Dzengi /api/v1/ticker/24hr
raw payload
schema validation
Dzengi quote parser
value validation
Dzengi REST quote model
```
При этом сохранены ключевые архитектурные свойства миграции:
- новая реализация добавлена параллельно legacy-контуру;
- работающий бот не сломан;
- публичное поведение `ExchangeService` не изменено;
- отсутствует зависимость новой Acquisition subsystem от legacy quote models;
- parsing, validation и будущий mapping разделены по ответственности;
- сохранена возможность безопасного поэтапного переключения потребителей.
**Build 028 завершён.**
Следующий этап:
```text
Build 029 — Dzengi mapper и Quotes Handler
```