# Build 038 — Перевод strategy и runtime-потребителей на canonical Quote **Dzentra Market Data Migration** --- ## Контроль документа | Свойство | Значение | |---|---| | Документ | Build 038 — Перевод strategy и runtime-потребителей на canonical Quote | | Тип документа | Build Record | | Версия | 1.0 | | Статус | **Completed** | | Подсистема | Market Data Acquisition | | Проект | Dzentra | | Язык | Русский | | Предыдущий этап | Build 037 — Перевод execution-потребителей | | Результат полной регрессии | **626 passed** | --- ## 1. Назначение Build 038 Build 038 завершает перевод выбранных strategy- и runtime-потребителей с legacy snapshot API на каноническую модель рыночной котировки: ```text Quote ``` Цель этапа — исключить использование словарных market snapshot в следующих потребителях: ```text src/trading/auto/signal_runtime.py src/trading/strategies/trend.py src/trading/strategies/scalp.py ``` и перевести их на единый типизированный источник текущей рыночной котировки: ```text ExchangeService.get_quote() ↓ Quote ``` --- ## 2. Контекст предыдущих Build Build 038 является продолжением последовательной миграции Market Data: ```text Build 031 ↓ Canonical Quote model Build 032 ↓ Quote acquisition pipeline Build 033 ↓ Quote Store Build 034 ↓ Dzengi WebSocket quote parsing и adapter Build 035 ↓ Market runtime переведён на Quotes Feed Build 036 ↓ Read-only и UI-потребители переведены на canonical Quote Build 037 ↓ Execution-потребители переведены на typed execution snapshot Build 038 ↓ Strategy и runtime-потребители переведены на canonical Quote ``` В результате Build 038 каноническая модель `Quote` становится непосредственным источником текущей рыночной котировки для выбранных runtime- и strategy-компонентов. --- ## 3. Каноническая модель Quote Используется модель: ```python @dataclass(frozen=True, slots=True) class Quote: symbol: str last_price: Decimal bid_price: Decimal ask_price: Decimal exchange_timestamp: datetime | None received_at: datetime source: str ``` Модель расположена в: ```text src/market_data/acquisition/models/quote.py ``` Основные свойства модели: - независимость от конкретного поставщика рыночных данных; - типизированные цены через `Decimal`; - отсутствие словарного доступа к ценовым полям; - наличие времени биржи; - наличие времени получения данных; - явное указание источника. --- ## 4. Изменённые runtime-потребители ### 4.1. Auto Signal Runtime Файл: ```text src/trading/auto/signal_runtime.py ``` Legacy-путь: ```text ExchangeService.get_market_snapshot() ↓ dict[str, object] ↓ snapshot.get("bid_price") snapshot.get("ask_price") snapshot.get("last_price") ``` Новый путь: ```text ExchangeService.get_quote() ↓ Quote ↓ quote.bid_price quote.ask_price quote.last_price ``` В результате runtime больше не зависит от словарного представления текущей рыночной котировки. --- ## 5. Изменённые strategy-потребители ### 5.1. Trend Strategy Файл: ```text src/trading/strategies/trend.py ``` Стратегия переведена с legacy market snapshot на canonical `Quote`. Новый источник: ```text ExchangeService.get_quote( symbol, runtime_key="auto", ) ``` Ценовые данные теперь читаются непосредственно из типизированной модели: ```text quote.last_price quote.bid_price quote.ask_price ``` Сохранена существующая логика выбора цены анализа: ```text 1. midpoint между bid и ask; 2. last_price; 3. 0.0 при отсутствии пригодной цены. ``` Midpoint остаётся предпочтительной ценой анализа: ```text (bid + ask) / 2 ``` Это позволяет уменьшить зависимость анализа от случайного последнего исполнения сделки. --- ### 5.2. Scalp Strategy Файл: ```text src/trading/strategies/scalp.py ``` Стратегия также переведена на: ```text ExchangeService.get_quote( symbol, runtime_key="auto", ) ``` Ценовые поля теперь получаются через: ```text quote.last_price quote.bid_price quote.ask_price ``` Сохранены: - существующая логика определения analysis price; - midpoint между bid и ask; - fallback на `last_price`; - safe fallback; - существующие strategy payload; - существующие торговые пороги; - существующая логика принятия решений. --- ## 6. Обработка Decimal Canonical `Quote` использует: ```text Decimal ``` для полей: ```text last_price bid_price ask_price ``` Существующие strategy helper-функции `_safe_float()` ранее принимали: ```text NumericLike | None ``` где `Decimal` не входил в контракт `NumericLike`. Это приводило к ошибкам статической типизации Pylance: ```text Аргумент типа "Decimal" нельзя присвоить параметру "value" типа "NumericLike | None" ``` В рамках Build 038 контракт локальных strategy helper-функций расширен до: ```python value: NumericLike | Decimal | None ``` Глобальный тип: ```text NumericLike ``` не изменялся. Это сохраняет локальность изменения и не расширяет общий типовой контракт проекта без необходимости. Архитектурная граница имеет вид: ```text canonical Quote ↓ Decimal ↓ strategy helper ↓ float ↓ существующие strategy calculations и payload ``` --- ## 7. Что намеренно не изменялось Build 038 не изменяет: - структуру canonical `Quote`; - `QuoteStore`; - acquisition pipeline; - WebSocket adapter; - REST adapter; - market runtime producer; - execution pricing semantics; - торговые пороги; - логику открытия позиции; - логику закрытия позиции; - flip-логику; - risk management; - journal payload contracts; - strategy payload keys; - Telegram UI; - структуру каталогов проекта. Build 038 является локальным этапом миграции потребителей, а не изменением торговой логики. --- ## 8. Контроль legacy-зависимостей После выполнения Build 038 выполнен контрольный поиск: ```bash grep -RIn \ --exclude-dir="__pycache__" \ --exclude="*.pyc" \ -E "get_market_snapshot\(|get_fresh_market_snapshot\(|get_execution_snapshot\(|get_price\(|snapshot\.get\(|quote\.get\(|runtime_key" \ src/trading/auto/signal_runtime.py \ src/trading/strategies/trend.py \ src/trading/strategies/scalp.py ``` Результат: ```text src/trading/auto/signal_runtime.py:881: runtime_key="auto", src/trading/strategies/trend.py:244: runtime_key="auto", src/trading/strategies/scalp.py:64: runtime_key="auto", ``` Legacy-вызовы не обнаружены. В проверенных файлах отсутствуют рабочие зависимости от: ```text get_market_snapshot() get_fresh_market_snapshot() get_execution_snapshot() get_price() snapshot.get(...) quote.get(...) ``` Оставшиеся: ```text runtime_key="auto" ``` являются ожидаемой частью вызова `get_quote()` и не представляют legacy-зависимость. --- ## 9. Проверка синтаксиса Выполнена проверка: ```bash python -m py_compile \ src/trading/auto/signal_runtime.py \ src/trading/strategies/trend.py \ src/trading/strategies/scalp.py ``` Результат: ```text успешно ``` Ошибки синтаксиса отсутствуют. --- ## 10. Целевые тесты После исправления типизации `Decimal` выполнены целевые тесты: ```bash python -m pytest \ tests/unit/trading/strategies/test_trend_quote.py \ tests/unit/trading/strategies/test_scalp_quote.py \ -q ``` Результат: ```text ...... [100%] 6 passed in 0.09s ``` --- ## 11. Полная регрессия Выполнена полная проверка проекта: ```bash python -m pytest -q ``` Результат: ```text 626 passed in 0.31s ``` Регрессий не обнаружено. Для сравнения: ```text После Build 037: 618 passed После Build 038: 626 passed ``` Количество тестов увеличено на: ```text 8 ``` --- ## 12. Итоговая архитектура после Build 038 После завершения Build 038 путь текущей рыночной котировки для мигрированных strategy- и runtime-потребителей выглядит следующим образом: ```text Dzengi WebSocket ↓ DzengiWebSocketQuoteAdapter ↓ canonical Quote ↓ Quote Store ↓ ExchangeService.get_quote() ├── Auto Signal Runtime ├── Trend Strategy └── Scalp Strategy ``` Для execution-контура сохраняется специализированная типизированная граница, введённая в Build 037: ```text canonical Quote ↓ ExchangeService ↓ ExecutionPriceSnapshot ↓ Execution consumers ``` Таким образом, после Build 038 разделены два типа потребления: ```text Market / Strategy / Runtime ↓ canonical Quote Execution ↓ ExecutionPriceSnapshot ``` --- ## 13. Архитектурный результат Build 038 устраняет ещё один слой legacy market snapshot API из рабочего торгового контура. До Build 038: ```text Market Data ↓ legacy dict snapshot ↓ snapshot.get(...) ↓ runtime / strategies ``` После Build 038: ```text Market Data ↓ canonical Quote ↓ typed attributes ↓ runtime / strategies ``` Это обеспечивает: - единый канонический контракт текущей котировки; - статическую типизацию; - отказ от строковых ключей для доступа к ценам; - явную работу с `Decimal`; - уменьшение зависимости trading layer от legacy exchange representations; - подготовку к дальнейшему удалению legacy snapshot API. --- ## 14. Критерии завершения Build 038 считается завершённым, поскольку выполнены все критерии: - [x] `signal_runtime.py` переведён на canonical `Quote`; - [x] `trend.py` переведён на canonical `Quote`; - [x] `scalp.py` переведён на canonical `Quote`; - [x] legacy `get_market_snapshot()` удалён из мигрированных путей; - [x] словарный доступ `snapshot.get(...)` к текущей котировке удалён; - [x] типизация `Decimal` обработана явно; - [x] глобальный `NumericLike` не изменён; - [x] существующая торговая логика сохранена; - [x] strategy payload contracts сохранены; - [x] `py_compile` проходит успешно; - [x] целевые тесты проходят успешно; - [x] полная регрессия проходит успешно; - [x] итоговый результат — **626 passed**. --- ## 15. Статус ```text Build 038: COMPLETED ``` Build 038 завершён и зафиксирован. Система готова к следующему этапу миграции.