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