Files
dzentra_bot/docs/migrations/build_038.md

13 KiB
Raw Blame History

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 на каноническую модель рыночной котировки:

Quote

Цель этапа — исключить использование словарных market snapshot в следующих потребителях:

src/trading/auto/signal_runtime.py
src/trading/strategies/trend.py
src/trading/strategies/scalp.py

и перевести их на единый типизированный источник текущей рыночной котировки:

ExchangeService.get_quote()
    ↓
Quote

2. Контекст предыдущих Build

Build 038 является продолжением последовательной миграции Market Data:

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

Используется модель:

@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

Модель расположена в:

src/market_data/acquisition/models/quote.py

Основные свойства модели:

  • независимость от конкретного поставщика рыночных данных;
  • типизированные цены через Decimal;
  • отсутствие словарного доступа к ценовым полям;
  • наличие времени биржи;
  • наличие времени получения данных;
  • явное указание источника.

4. Изменённые runtime-потребители

4.1. Auto Signal Runtime

Файл:

src/trading/auto/signal_runtime.py

Legacy-путь:

ExchangeService.get_market_snapshot()
    ↓
dict[str, object]
    ↓
snapshot.get("bid_price")
snapshot.get("ask_price")
snapshot.get("last_price")

Новый путь:

ExchangeService.get_quote()
    ↓
Quote
    ↓
quote.bid_price
quote.ask_price
quote.last_price

В результате runtime больше не зависит от словарного представления текущей рыночной котировки.


5. Изменённые strategy-потребители

5.1. Trend Strategy

Файл:

src/trading/strategies/trend.py

Стратегия переведена с legacy market snapshot на canonical Quote.

Новый источник:

ExchangeService.get_quote(
    symbol,
    runtime_key="auto",
)

Ценовые данные теперь читаются непосредственно из типизированной модели:

quote.last_price
quote.bid_price
quote.ask_price

Сохранена существующая логика выбора цены анализа:

1. midpoint между bid и ask;
2. last_price;
3. 0.0 при отсутствии пригодной цены.

Midpoint остаётся предпочтительной ценой анализа:

(bid + ask) / 2

Это позволяет уменьшить зависимость анализа от случайного последнего исполнения сделки.


5.2. Scalp Strategy

Файл:

src/trading/strategies/scalp.py

Стратегия также переведена на:

ExchangeService.get_quote(
    symbol,
    runtime_key="auto",
)

Ценовые поля теперь получаются через:

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 использует:

Decimal

для полей:

last_price
bid_price
ask_price

Существующие strategy helper-функции _safe_float() ранее принимали:

NumericLike | None

где Decimal не входил в контракт NumericLike.

Это приводило к ошибкам статической типизации Pylance:

Аргумент типа "Decimal" нельзя присвоить параметру "value"
типа "NumericLike | None"

В рамках Build 038 контракт локальных strategy helper-функций расширен до:

value: NumericLike | Decimal | None

Глобальный тип:

NumericLike

не изменялся.

Это сохраняет локальность изменения и не расширяет общий типовой контракт проекта без необходимости.

Архитектурная граница имеет вид:

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 выполнен контрольный поиск:

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

Результат:

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-вызовы не обнаружены.

В проверенных файлах отсутствуют рабочие зависимости от:

get_market_snapshot()
get_fresh_market_snapshot()
get_execution_snapshot()
get_price()
snapshot.get(...)
quote.get(...)

Оставшиеся:

runtime_key="auto"

являются ожидаемой частью вызова get_quote() и не представляют legacy-зависимость.


9. Проверка синтаксиса

Выполнена проверка:

python -m py_compile \
  src/trading/auto/signal_runtime.py \
  src/trading/strategies/trend.py \
  src/trading/strategies/scalp.py

Результат:

успешно

Ошибки синтаксиса отсутствуют.


10. Целевые тесты

После исправления типизации Decimal выполнены целевые тесты:

python -m pytest \
  tests/unit/trading/strategies/test_trend_quote.py \
  tests/unit/trading/strategies/test_scalp_quote.py \
  -q

Результат:

...... [100%]

6 passed in 0.09s

11. Полная регрессия

Выполнена полная проверка проекта:

python -m pytest -q

Результат:

626 passed in 0.31s

Регрессий не обнаружено.

Для сравнения:

После Build 037: 618 passed
После Build 038: 626 passed

Количество тестов увеличено на:

8

12. Итоговая архитектура после Build 038

После завершения Build 038 путь текущей рыночной котировки для мигрированных strategy- и runtime-потребителей выглядит следующим образом:

Dzengi WebSocket
    ↓
DzengiWebSocketQuoteAdapter
    ↓
canonical Quote
    ↓
Quote Store
    ↓
ExchangeService.get_quote()
    ├── Auto Signal Runtime
    ├── Trend Strategy
    └── Scalp Strategy

Для execution-контура сохраняется специализированная типизированная граница, введённая в Build 037:

canonical Quote
    ↓
ExchangeService
    ↓
ExecutionPriceSnapshot
    ↓
Execution consumers

Таким образом, после Build 038 разделены два типа потребления:

Market / Strategy / Runtime
    ↓
canonical Quote

Execution
    ↓
ExecutionPriceSnapshot

13. Архитектурный результат

Build 038 устраняет ещё один слой legacy market snapshot API из рабочего торгового контура.

До Build 038:

Market Data
    ↓
legacy dict snapshot
    ↓
snapshot.get(...)
    ↓
runtime / strategies

После Build 038:

Market Data
    ↓
canonical Quote
    ↓
typed attributes
    ↓
runtime / strategies

Это обеспечивает:

  • единый канонический контракт текущей котировки;
  • статическую типизацию;
  • отказ от строковых ключей для доступа к ценам;
  • явную работу с Decimal;
  • уменьшение зависимости trading layer от legacy exchange representations;
  • подготовку к дальнейшему удалению legacy snapshot API.

14. Критерии завершения

Build 038 считается завершённым, поскольку выполнены все критерии:

  • signal_runtime.py переведён на canonical Quote;
  • trend.py переведён на canonical Quote;
  • scalp.py переведён на canonical Quote;
  • legacy get_market_snapshot() удалён из мигрированных путей;
  • словарный доступ snapshot.get(...) к текущей котировке удалён;
  • типизация Decimal обработана явно;
  • глобальный NumericLike не изменён;
  • существующая торговая логика сохранена;
  • strategy payload contracts сохранены;
  • py_compile проходит успешно;
  • целевые тесты проходят успешно;
  • полная регрессия проходит успешно;
  • итоговый результат — 626 passed.

15. Статус

Build 038: COMPLETED

Build 038 завершён и зафиксирован.

Система готова к следующему этапу миграции.