Files
dzentra_bot/docs/migrations/build_036.md

739 lines
19 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 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.