# 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 ```