Files
dzentra_bot/docs/migrations/build_037.md

608 lines
15 KiB
Markdown
Raw 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 037 — Перевод execution-потребителей на canonical Quote
**Статус:** завершён
**Тип изменения:** миграция / архитектурный рефакторинг
**Подсистема:** Market Data / Exchange Integration / Trading Execution
**Дата завершения:** 13 июля 2026
---
## 1. Цель Build
Цель Build 037 — перевести execution-потребителей с legacy-представлений рыночной котировки на каноническую модель `Quote`, сохранив существующее поведение торговой системы и обратную совместимость переходного периода.
Build продолжает миграционную последовательность:
```text
Build 031 — Quotes Feed
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 037 execution-контур получает рыночную котировку через канонический `Quote`, а специализированный execution boundary предоставляет типизированное представление `ExecutionPriceSnapshot`.
---
## 2. Архитектурный принцип
В рамках Build 037 зафиксировано следующее направление зависимости:
```text
Dzengi REST / WebSocket
Market Data Acquisition
canonical Quote
Quote Store
ExchangeService
ExecutionPriceSnapshot
Execution consumers
```
Execution-потребители не должны самостоятельно:
- разбирать сырой payload биржи;
- знать формат Dzengi REST или WebSocket;
- обращаться напрямую к `Quote Store`;
- зависеть от legacy `MarketPriceSnapshot`;
- использовать dict-based market snapshot там, где необходим типизированный execution-контракт;
- самостоятельно определять источник котировки.
Канонический объект рыночной котировки:
```python
Quote
```
Типизированное представление для execution-контура:
```python
ExecutionPriceSnapshot
```
---
## 3. Область изменений
В рамках Build 037 изменены следующие production-файлы:
```text
src/integrations/exchange/service.py
src/trading/auto/execution_quality.py
src/trading/debug/execution.py
```
Добавлены специализированные тесты:
```text
tests/unit/integrations/exchange/test_service_execution_quote.py
tests/unit/trading/auto/test_execution_quality.py
tests/unit/trading/debug/test_execution.py
```
Файл:
```text
src/trading/execution/pricing.py
```
был проанализирован, но не потребовал production-изменений, поскольку уже использовал типизированный boundary:
```python
ExchangeService().get_execution_snapshot(symbol)
```
---
## 4. Изменения в ExchangeService
### 4.1. Execution snapshot теперь строится из canonical Quote
Метод:
```python
get_execution_snapshot()
```
переведён на получение канонической котировки через:
```python
get_quote()
```
Таким образом, execution boundary больше не зависит от legacy `MarketPriceSnapshot`.
Целевая цепочка:
```text
Quote Store
canonical Quote
ExchangeService.get_quote()
ExchangeService.get_execution_snapshot()
ExecutionPriceSnapshot
```
---
### 4.2. Сохранён типизированный execution-контракт
Execution-потребители получают:
```python
ExecutionPriceSnapshot
```
с полями:
```text
symbol
last_price
bid_price
ask_price
updated_at
source
is_fresh
age_seconds
```
Это позволяет execution-слою работать с явным типизированным контрактом вместо произвольного словаря.
---
### 4.3. Сохранена семантика freshness
При построении `ExecutionPriceSnapshot` сохраняется информация о возрасте котировки:
```text
age_seconds
```
и состоянии актуальности:
```text
is_fresh
```
Execution-контур продолжает использовать существующие ограничения максимального возраста котировки.
В частности:
```python
_max_execution_snapshot_age_seconds = 5
```
остаётся execution-policy и не переносится в слой Market Data.
Это соответствует разделению ответственности:
```text
Market Data
предоставляет Quote и объективный возраст данных
Execution
определяет, допустим ли этот возраст для исполнения сделки
```
---
## 5. Изменения в execution quality
Файл:
```text
src/trading/auto/execution_quality.py
```
переведён с legacy dict-based market snapshot на типизированный:
```python
ExecutionPriceSnapshot
```
Ранее execution quality зависел от:
```python
get_market_snapshot()
```
и извлекал значения через:
```python
snapshot.get("bid_price")
snapshot.get("ask_price")
snapshot.get("last_price")
snapshot.get("age_seconds")
snapshot.get("is_fresh")
```
После Build 037 используется типизированный execution boundary:
```python
get_execution_snapshot()
```
и атрибуты:
```python
snapshot.bid_price
snapshot.ask_price
snapshot.last_price
snapshot.age_seconds
snapshot.is_fresh
```
Это устраняет зависимость execution quality от legacy dict-based представления котировки.
---
## 6. Устранение legacy fallback через get_price()
В execution quality существовал fallback через:
```python
ExchangeService().get_price(...)
```
В рамках Build 037 этот путь устранён из execution-потребителя.
Fallback теперь также проходит через типизированный execution boundary:
```python
ExchangeService().get_execution_snapshot(...)
```
Таким образом, основной и fallback-пути используют единый контракт данных.
Целевая схема:
```text
canonical Quote
ExecutionPriceSnapshot
├── основной execution path
└── fallback execution path
```
---
## 7. Изменения debug execution
Файл:
```text
src/trading/debug/execution.py
```
переведён с:
```python
get_fresh_market_snapshot()
```
на:
```python
get_execution_snapshot()
```
Debug execution теперь использует тот же типизированный execution boundary, что и production execution.
Это устраняет архитектурное расхождение:
```text
production execution → ExecutionPriceSnapshot
debug execution → legacy dict snapshot
```
и заменяет его единым подходом:
```text
production execution → ExecutionPriceSnapshot
debug execution → ExecutionPriceSnapshot
```
---
## 8. Side-aware execution pricing
Build 037 сохраняет существующую семантику выбора цены исполнения.
Для входа в LONG:
```text
ask_price
↓ fallback
last_price
```
Для входа в SHORT:
```text
bid_price
↓ fallback
last_price
```
Для выхода из LONG:
```text
bid_price
↓ fallback
last_price
```
Для выхода из SHORT:
```text
ask_price
↓ fallback
last_price
```
Для общего получения текущей рыночной цены:
```text
last_price
```
Это поведение не изменялось в рамках Build 037.
---
## 9. Что намеренно не изменялось
Build 037 ограничен execution-потребителями.
В рамках данного Build намеренно не переводились:
```text
src/trading/auto/signal_runtime.py
src/trading/strategies/trend.py
src/trading/strategies/scalp.py
```
Эти файлы относятся к следующему этапу:
```text
Build 038 — Перевод strategy и runtime-потребителей на canonical Quote
```
Также не выполнялись:
- удаление legacy `get_market_snapshot()`;
- удаление legacy `get_fresh_market_snapshot()`;
- удаление `MarketPriceCache`;
- глобальная перестройка `ExchangeService`;
- изменение утверждённой структуры каталогов;
- изменение торговой стратегии;
- изменение execution thresholds;
- изменение логики открытия, закрытия или flip позиции.
---
## 10. Обратная совместимость
Build 037 выполнен как безопасный миграционный этап.
Legacy API не удалялись, поскольку некоторые потребители ещё используют переходные методы.
Сохраняются:
```text
get_market_snapshot()
get_fresh_market_snapshot()
get_price()
MarketPriceCache
```
Их удаление допустимо только после полного перевода всех production-потребителей и отдельного контрольного `grep`.
---
## 11. Тестовое покрытие
Для Build 037 добавлены специализированные unit-тесты:
```text
tests/unit/integrations/exchange/test_service_execution_quote.py
tests/unit/trading/auto/test_execution_quality.py
tests/unit/trading/debug/test_execution.py
```
Тестами подтверждено:
- `get_execution_snapshot()` использует canonical `Quote`;
- execution service не зависит от legacy market snapshot;
- execution quality использует `ExecutionPriceSnapshot`;
- legacy `get_market_snapshot()` не используется новым execution quality path;
- debug execution использует `get_execution_snapshot()`;
- legacy `get_fresh_market_snapshot()` не используется новым debug execution path;
- сохраняется корректная передача `last_price`;
- сохраняется корректная передача `bid_price`;
- сохраняется корректная передача `ask_price`;
- сохраняется информация о возрасте котировки;
- сохраняется freshness semantics.
---
## 12. Результаты проверки
Проверка синтаксиса:
```bash
python -m py_compile \
src/integrations/exchange/service.py \
src/trading/auto/execution_quality.py \
src/trading/debug/execution.py \
tests/unit/integrations/exchange/test_service_execution_quote.py \
tests/unit/trading/auto/test_execution_quality.py \
tests/unit/trading/debug/test_execution.py
```
Результат:
```text
успешно
```
Специализированные тесты:
```bash
python -m pytest \
tests/unit/integrations/exchange/test_service_execution_quote.py \
tests/unit/trading/auto/test_execution_quality.py \
tests/unit/trading/debug/test_execution.py \
-q
```
Результат:
```text
4 passed in 0.08s
```
Полная регрессия:
```bash
python -m pytest -q
```
Результат:
```text
618 passed in 0.28s
```
---
## 13. Изменение количества тестов
До Build 037:
```text
614 passed
```
После Build 037:
```text
618 passed
```
Добавлено:
```text
4 теста
```
---
## 14. Итоговая архитектура после Build 037
После завершения Build 037 execution-контур выглядит следующим образом:
```text
Dzengi REST / WebSocket
Market Data Acquisition
canonical Quote
Quote Store
ExchangeService.get_quote()
ExchangeService.get_execution_snapshot()
ExecutionPriceSnapshot
├── trading/execution/pricing.py
├── trading/auto/execution_quality.py
└── trading/debug/execution.py
```
Legacy market snapshot больше не является обязательным источником данных для переведённых execution-потребителей.
---
## 15. Критерии завершения Build 037
Build 037 считается завершённым, поскольку выполнены все обязательные условия:
- [x] `get_execution_snapshot()` строится из canonical `Quote`;
- [x] execution quality переведён на `ExecutionPriceSnapshot`;
- [x] debug execution переведён на `ExecutionPriceSnapshot`;
- [x] legacy `get_price()` устранён из изменённого execution fallback path;
- [x] `pricing.py` проверен и уже использует typed execution boundary;
- [x] side-aware pricing сохранён;
- [x] freshness semantics сохранена;
- [x] legacy API не удалены преждевременно;
- [x] специализированные тесты проходят;
- [x] полная регрессия проходит;
- [x] существующая торговая логика не изменена.
---
## 16. Следующий этап
Следующий этап миграции:
```text
Build 038 — Перевод strategy и runtime-потребителей на canonical Quote
```
Основные кандидаты следующего Build:
```text
src/trading/auto/signal_runtime.py
src/trading/strategies/trend.py
src/trading/strategies/scalp.py
```
Цель Build 038:
```text
убрать зависимость strategy и runtime-потребителей
от legacy dict-based market snapshot и перевести их
на canonical Quote с сохранением существующей торговой семантики.
```
После Build 038 должен быть выполнен новый контрольный `grep` для определения оставшихся production-зависимостей от:
```text
get_market_snapshot()
get_fresh_market_snapshot()
get_price()
MarketPriceCache
```
---
## 17. Статус
```text
Build 037 — ЗАВЕРШЁН
```
Следующий Build:
```text
Build 038 — Перевод strategy и runtime-потребителей на canonical Quote
```