19 KiB
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 и отвечает за:
- выбор default symbol;
- поддержку mock mode;
- валидацию символа;
- нормализацию
runtime_key; - чтение канонической котировки из MarketPriceCache;
- проверку свежести;
- REST fallback при cache miss или stale quote;
- сохранение свежей котировки в Quote Store через MarketPriceCache;
- возврат канонического
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 разделены две разные сущности:
- текущая рыночная котировка;
- 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.