Files
dzentra_bot/docs/migrations/build_036.md

19 KiB
Raw Permalink Blame History

Build 036 — Перевод read-only и UI-потребителей

Статус: Завершён
Результат: Успешно
Полная регрессия: 614 passed


1. Назначение Build

Цель Build 036 — перевести read-only и UI-потребителей рыночной котировки с legacy-представлений:

  • TickerPrice;
  • dict[str, object] из get_market_snapshot();

на каноническую внутреннюю модель:

Quote

Build продолжает миграцию подсистемы рыночных данных на целевую архитектуру:

Market Data
    ↓
Market Intelligence
    ↓
Decision
    ↓
Execution
    ↓
Exchange

В рамках Build 036 изменяется исключительно read-only контур.

Execution pricing, торговые стратегии, signal runtime и execution quality не переводятся и не изменяются.


2. Исходное состояние

До Build 036 read-only и UI-потребители получали текущую рыночную котировку через несколько legacy-интерфейсов.

Основные варианты:

UI / diagnostics
        ↓
ExchangeService.get_price()
        ↓
TickerPrice

или:

UI / diagnostics
        ↓
ExchangeService.get_market_snapshot()
        ↓
dict[str, object]

При этом после предыдущих Build каноническая модель Quote уже существовала и использовалась внутри новой инфраструктуры:

REST Quotes Feed
        ↓
Quote

WebSocket quote adapter
        ↓
Quote

MarketPriceCache
        ↓
Quote Store
        ↓
Quote

Таким образом, read-only потребители продолжали зависеть от compatibility-представлений, несмотря на наличие канонической модели котировки.


3. Целевое состояние

После Build 036 read-only и UI-потребители получают канонический объект:

Quote

через публичный facade:

ExchangeService.get_quote()

Целевая цепочка чтения:

read-only / UI consumer
        ↓
ExchangeService.get_quote()
        ↓
MarketPriceCache.get_quote()
        ↓
Quote Store
        ↓
canonical Quote

При отсутствии свежей котировки используется REST fallback:

ExchangeService.get_quote()
        ↓
REST Quotes Feed
        ↓
canonical Quote
        ↓
MarketPriceCache.set_quote()
        ↓
Quote Store
        ↓
canonical Quote

4. Архитектурный принцип

Read-only и UI-потребители не обращаются напрямую к:

Quote Store

Доступ выполняется через:

ExchangeService.get_quote()

Это позволяет сохранить единый facade, отвечающий за:

  • выбор default symbol;
  • нормализацию и валидацию символа;
  • нормализацию runtime_key;
  • поддержку mock mode;
  • чтение канонической котировки из cache/store;
  • проверку свежести;
  • REST fallback;
  • преобразование внутренних ошибок в ExchangeError.

Целевая граница:

UI / diagnostics
        ↓
ExchangeService
        ↓
MarketPriceCache
        ↓
Quote Store

UI не должен самостоятельно знать:

  • структуру ключей Quote Store;
  • source_name;
  • правила runtime_key;
  • freshness policy;
  • правила REST fallback;
  • внутреннюю обработку ошибок Acquisition Layer.

5. Изменённые production-файлы

В рамках Build 036 изменены:

src/integrations/exchange/market_cache.py
src/integrations/exchange/service.py

src/telegram/ui/currency_ui.py
src/telegram/handlers/auto/ui.py
src/telegram/handlers/debug_auto/ui.py

src/trading/diagnostics/snapshot.py

6. Изменённые и добавленные тесты

Изменены:

tests/unit/integrations/exchange/test_market_cache.py
tests/unit/telegram/ui/test_currency_ui.py

Добавлен:

tests/unit/integrations/exchange/test_service_quote.py

После Build 036 общее количество тестов увеличилось:

после Build 035: 608 passed
после Build 036: 614 passed

Добавлено:

6 тестов

7. Изменения в MarketPriceCache

Файл:

src/integrations/exchange/market_cache.py

Добавлен канонический read API:

MarketPriceCache.get_quote()

Его назначение — вернуть непосредственно канонический объект Quote, сохранённый в Quote Store.

Цепочка:

MarketPriceCache.get_quote()
        ↓
QuoteStore.get()
        ↓
Quote | None

Метод:

  • нормализует symbol;
  • нормализует runtime_key;
  • читает котировку из собственного namespace Quote Store;
  • возвращает канонический Quote;
  • не создаёт MarketPriceSnapshot;
  • не выполняет преобразование Decimal в float;
  • сохраняет канонический объект котировки.

Legacy API:

MarketPriceCache.get_price()

сохранён для compatibility-потребителей, которые ещё не переведены на Quote.


8. Изменения в ExchangeService

Файл:

src/integrations/exchange/service.py

Добавлен публичный канонический read API:

ExchangeService.get_quote()

Метод сохраняет обязанности facade и отвечает за:

  1. выбор default symbol;
  2. поддержку mock mode;
  3. валидацию символа;
  4. нормализацию runtime_key;
  5. чтение канонической котировки из MarketPriceCache;
  6. проверку свежести;
  7. REST fallback при cache miss или stale quote;
  8. сохранение свежей котировки в Quote Store через MarketPriceCache;
  9. возврат канонического Quote.

Целевая цепочка:

ExchangeService.get_quote()
        ↓
validate_symbol()
        ↓
MarketPriceCache.get_quote()
        ↓
freshness check
        ↓
Quote

При отсутствии свежей котировки:

ExchangeService.get_quote()
        ↓
REST Quotes Feed
        ↓
Quote
        ↓
MarketPriceCache.set_quote()
        ↓
Quote Store

9. Политика свежести

Для read-only и UI-потребителей сохранена существующая политика свежести рыночной котировки.

Возраст котировки определяется по:

Quote.received_at

Если сохранённая котировка достаточно свежая, возвращается существующий канонический объект.

Если котировка отсутствует или устарела, выполняется REST fallback через новый Quotes Feed.

Таким образом:

fresh cached Quote
        ↓
вернуть Quote без REST-запроса

stale cached Quote
        ↓
REST Quotes Feed
        ↓
сохранить новый Quote
        ↓
вернуть новый Quote

10. REST fallback

REST fallback использует новую каноническую цепочку Acquisition Layer:

Dzengi REST API
        ↓
GET /api/v1/ticker/24hr
        ↓
DzengiQuoteDocumentSource
        ↓
DzengiQuoteDocumentHandler
        ↓
schema validation
        ↓
parsing
        ↓
value validation
        ↓
mapping
        ↓
canonical Quote

Полученный объект сохраняется:

Quote
    ↓
MarketPriceCache.set_quote()
    ↓
Quote Store

Не используется лишний цикл преобразований:

Quote
    ↓
dict
    ↓
float
    ↓
новый Quote

Канонический объект остаётся Quote на всём новом пути.


11. Mock mode

ExchangeService.get_quote() сохраняет поддержку режима:

exchange_enabled = False

В этом режиме потребителю также возвращается канонический:

Quote

Mock-котировка содержит:

  • symbol;
  • last_price;
  • bid_price;
  • ask_price;
  • exchange_timestamp;
  • received_at;
  • source.

Таким образом, потребители get_quote() не должны знать, работает приложение с реальной биржей или в mock mode.


12. Обработка ошибок

Новый canonical read API сохраняет существующую границу ошибок ExchangeService.

Ошибки Acquisition Layer:

  • не передаются напрямую UI-потребителям;
  • логируются в контексте ticker/24hr;
  • преобразуются во внешний ExchangeError;
  • сохраняют исходную ошибку через __cause__.

Граница остаётся следующей:

Acquisition error
        ↓
ExchangeService
        ↓
ExchangeError
        ↓
UI / diagnostics consumer

13. Перевод currency_ui.py

Файл:

src/telegram/ui/currency_ui.py

До Build 036 использовался legacy API:

ExchangeService.get_price()

Возвращаемая модель:

TickerPrice

Для расчёта использовалось:

ticker.price

После Build 036 используется:

ExchangeService.get_quote()

и каноническое поле:

quote.last_price

Целевая цепочка:

currency_ui
    ↓
ExchangeService.get_quote()
    ↓
Quote.last_price

Локальная логика расчёта стоимости баланса, обработка ExchangeError и кэширование рассчитанных цен сохранены.


14. Перевод auto/ui.py

Файл:

src/telegram/handlers/auto/ui.py

До Build 036 UI получал legacy market snapshot:

ExchangeService.get_market_snapshot()

и работал с:

dict[str, object]

Основные поля:

  • last_price;
  • bid_price;
  • ask_price.

После Build 036 UI получает:

Quote

через:

ExchangeService.get_quote()

Используются канонические поля:

  • quote.last_price;
  • quote.bid_price;
  • quote.ask_price.

В результате UI больше не зависит от legacy market snapshot dict для получения текущей рыночной котировки.


15. Перевод debug_auto/ui.py

Файл:

src/telegram/handlers/debug_auto/ui.py

В debug UI разделены две разные сущности:

  1. текущая рыночная котировка;
  2. execution snapshot.

После Build 036 market-секция использует:

Quote

и читает:

  • last_price;
  • bid_price;
  • ask_price;
  • source;
  • received_at;
  • exchange_timestamp.

Execution-секция продолжает использовать:

ExecutionPriceSnapshot

Это намеренное разделение.

Build 036 не изменяет execution semantics.

Целевая схема:

Debug UI
   ├── Market section
   │       ↓
   │     Quote
   │
   └── Execution section
           ↓
      ExecutionPriceSnapshot

16. Перевод trading/diagnostics/snapshot.py

Файл:

src/trading/diagnostics/snapshot.py

До Build 036 диагностика использовала:

ExchangeService.get_market_snapshot()

После Build 036 используется:

ExchangeService.get_quote()

Для выбора диагностической цены сохраняется существующая семантика:

BUY
    ↓
quote.ask_price

SELL
    ↓
quote.bid_price

другое состояние
    ↓
quote.last_price

Build не изменяет торговые решения и используется только для read-only диагностики.


17. Сохранённые legacy API

Build 036 не удаляет:

  • ExchangeService.get_price();
  • ExchangeService.get_market_snapshot();
  • ExchangeService.get_execution_snapshot();
  • ExchangeService.get_fresh_market_snapshot();
  • MarketPriceCache.get_price().

Они сохраняются для ещё не переведённых compatibility-потребителей.

Удаление legacy API возможно только после полного перевода всех зависимых компонентов и отдельной проверки использования.


18. Что намеренно не изменялось

Build 036 не затрагивает:

src/trading/execution/pricing.py
src/trading/auto/signal_runtime.py
src/trading/auto/execution_quality.py
src/trading/strategies/trend.py
src/trading/strategies/scalp.py
src/trading/debug/execution.py

Эти компоненты относятся к:

  • execution pricing;
  • signal runtime;
  • execution quality;
  • strategy decisions;
  • debug execution semantics.

Их перевод должен выполняться отдельно.


19. Что не входит в Build 036

В рамках Build 036 не выполнялись:

  • перевод execution pricing;
  • перевод signal runtime;
  • перевод execution quality;
  • перевод торговых стратегий;
  • удаление TickerPrice;
  • удаление get_price();
  • удаление get_market_snapshot();
  • удаление legacy market snapshot dict layer;
  • удаление ExecutionPriceSnapshot;
  • удаление MarketPriceCache;
  • удаление legacy parsing helpers.

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

Выполнена команда:

python -m py_compile \
  src/integrations/exchange/market_cache.py \
  src/integrations/exchange/service.py \
  src/telegram/ui/currency_ui.py \
  src/telegram/handlers/auto/ui.py \
  src/telegram/handlers/debug_auto/ui.py \
  src/trading/diagnostics/snapshot.py \
  tests/unit/integrations/exchange/test_market_cache.py \
  tests/unit/integrations/exchange/test_service_quote.py \
  tests/unit/telegram/ui/test_currency_ui.py

Результат:

Успешно

Ошибок синтаксиса не обнаружено.


21. Специализированные тесты

Выполнена команда:

python -m pytest \
  tests/unit/integrations/exchange/test_market_cache.py \
  tests/unit/integrations/exchange/test_service_quote.py \
  tests/unit/telegram/ui/test_currency_ui.py \
  -q

Результат:

44 passed in 0.29s


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

Выполнена команда:

python -m pytest -q

Результат:

614 passed in 0.27s

Полная тестовая регрессия проекта успешно пройдена.


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

После завершения Build 036 read-only и UI-контур использует следующую архитектуру:

┌──────────────────────────────┐
│ currency_ui                  │
│ auto UI                      │
│ debug UI market section      │
│ trading diagnostics          │
└──────────────┬───────────────┘
               ↓
     ExchangeService.get_quote()
               ↓
     MarketPriceCache.get_quote()
               ↓
          Quote Store
               ↓
        canonical Quote

При cache miss или stale quote:

ExchangeService.get_quote()
        ↓
REST Quotes Feed
        ↓
canonical Quote
        ↓
MarketPriceCache.set_quote()
        ↓
Quote Store

24. Результат Build

Build 036 успешно завершён.

Достигнуты следующие результаты:

  • добавлен канонический read API MarketPriceCache.get_quote();
  • добавлен публичный facade ExchangeService.get_quote();
  • сохранены symbol validation, mock mode, freshness policy и REST fallback;
  • currency_ui.py переведён с TickerPrice на Quote;
  • auto/ui.py переведён с legacy market snapshot dict на Quote;
  • market-секция debug UI переведена на Quote;
  • execution-секция debug UI оставлена на ExecutionPriceSnapshot;
  • trading diagnostics переведена на Quote;
  • execution-контур не изменён;
  • legacy API сохранены для последующей миграции;
  • специализированные тесты успешно пройдены;
  • полная регрессия успешно пройдена.

Итог:

До Build 036:

UI / diagnostics
        ↓
TickerPrice / market snapshot dict

После Build 036:

UI / diagnostics
        ↓
ExchangeService.get_quote()
        ↓
canonical Quote

25. Следующий этап

Следующий этап миграции:

Build 037 — Перевод execution-потребителей

Его задача — отдельно проанализировать и перевести execution-sensitive потребителей канонической рыночной котировки без изменения торговой семантики и без преждевременного удаления legacy compatibility API.