Files
dzentra_bot/docs/migrations/build_038.md

567 lines
13 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 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 завершён и зафиксирован.
Система готова к следующему этапу миграции.