# Build 041 — Аудит и удаление остатков legacy quote parsing **Статус:** завершён **Подсистема:** Market Data Acquisition / Quotes Feed **Проект:** Dzentra **Тип изменения:** архитектурная миграция без изменения рабочего поведения **Основное требование:** не изменять логику работы текущих файлов и наблюдаемое поведение бота --- ## 1. Цель Build Цель Build 041 — выполнить аудит оставшихся production-зависимостей от legacy-разбора котировок и удалить подтверждённо неиспользуемую дублирующую логику WebSocket quote parsing из legacy integration layer. Build продолжает миграцию Quotes Feed после: - Build 039 — удаления legacy `TickerPrice` и market snapshot dict layer; - Build 040 — восстановления и фиксации REST fallback через канонический Quotes acquisition pipeline. Основной архитектурный принцип Build 041: ~~~text Raw Dzengi WebSocket payload ↓ DzengiWebSocketQuoteAdapter ↓ schema validation ↓ parser ↓ value validation ↓ mapper ↓ canonical Quote ↓ runtime consumers ~~~ Production-код вне `market_data/acquisition` не должен самостоятельно интерпретировать структуру WebSocket payload для получения `bid_price` и `ask_price`. --- ## 2. Основное ограничение Главное требование Build 041: > **Не менять логику работы текущих файлов.** В рамках Build запрещалось: - изменять торговую логику; - изменять алгоритмы принятия решений; - изменять execution pricing semantics; - изменять формат канонической модели `Quote`; - изменять REST Quotes Feed; - изменять WebSocket transport; - изменять алгоритм переподключения WebSocket; - изменять runtime lifecycle; - изменять журналирование; - изменять UI; - изменять обработку торговых сигналов; - изменять формат runtime-состояния; - удалять код без доказанного отсутствия production-зависимостей. Допускалось только удаление legacy quote parsing, если одновременно подтверждены следующие условия: 1. production-код уже использует канонический `DzengiWebSocketQuoteAdapter`; 2. удаляемая функция больше не вызывается; 3. соответствующая логика уже реализована в `market_data/acquisition`; 4. полный набор тестов подтверждает отсутствие регрессий. --- ## 3. Предварительный аудит Перед изменениями был выполнен аудит всех известных признаков legacy quote parsing. Проверялись: - прямое чтение `lastPrice`; - прямое чтение `bidPrice`; - прямое чтение `askPrice`; - прямое чтение `closeTime`; - прямое чтение `eventTime`; - прямое чтение `bids`; - прямое чтение `asks`; - функции извлечения best bid / best ask; - старые market snapshot helpers; - старые ticker price helpers; - прямое создание `Quote`; - REST endpoint `/api/v1/ticker/24hr`; - все production-потребители `DzengiWebSocketQuoteAdapter`; - все production-потребители `QuotesFeed`; - все production-потребители `QuoteAcquisitionService`. Аудит показал, что корректный разбор REST-котировок уже сосредоточен в: ~~~text src/market_data/acquisition/ ├── adapters/dzengi/models.py ├── adapters/dzengi/parser.py ├── adapters/dzengi/mapper.py ├── adapters/dzengi/rest.py ├── adapters/dzengi/websocket.py ├── validation/schema.py ├── validation/values.py ├── handlers/quotes_handler.py ├── feeds/quotes_feed.py └── service.py ~~~ При этом в legacy integration layer оставались отдельные helpers, самостоятельно интерпретировавшие структуру WebSocket payload. --- ## 4. Обнаруженные legacy helpers Аудит выявил следующие legacy-функции. ### `src/integrations/exchange/market_stream.py` ~~~python def _extract_depth_prices( event: JsonDict, ) -> tuple[float | None, float | None]: ... ~~~ ~~~python def _extract_first_price( value: object, ) -> float | None: ... ~~~ Эти функции самостоятельно разбирали: ~~~text bids asks bidPrice askPrice ~~~ и тем самым дублировали ответственность канонического Dzengi WebSocket quote adapter. ### `src/integrations/exchange/market_data_runner.py` ~~~python def _extract_best_price( ... ) -> float | None: ... ~~~ Функция также содержала legacy-логику извлечения цены непосредственно из сырой структуры WebSocket payload. --- ## 5. Подтверждение канонического пути До удаления legacy helpers было подтверждено, что рабочий runtime уже использует: ~~~python DzengiWebSocketQuoteAdapter ~~~ Канонический путь обработки WebSocket-котировки: ~~~text ExchangeWebSocketClient ↓ raw WebSocket message ↓ DzengiWebSocketQuoteAdapter.map_message() ↓ validate_dzengi_websocket_quote_schema() ↓ parse_dzengi_websocket_quote() ↓ validate_dzengi_websocket_quote_values() ↓ map_dzengi_websocket_quote_to_quote() ↓ Quote ~~~ Таким образом, production runtime получает уже каноническую модель: ~~~python Quote ~~~ и не должен повторно разбирать сырой Dzengi payload. --- ## 6. Выполненные изменения В Build 041 удалена подтверждённо избыточная legacy quote parsing logic. Изменения затронули: ~~~text app/src/integrations/exchange/market_stream.py app/src/integrations/exchange/market_data_runner.py app/tests/unit/integrations/exchange/test_market_stream.py app/tests/unit/integrations/exchange/test_market_data_runner.py ~~~ Удалены legacy helpers: ~~~text _extract_depth_prices _extract_first_price _extract_best_price _extract_market_event ~~~ Также удалены остаточные прямые обращения вида: ~~~text first.get("bidPrice") first.get("askPrice") ~~~ в проверяемом legacy integration-контуре. --- ## 7. Что не изменялось Build 041 не изменял: ### REST Quotes Feed Не изменялись: ~~~text DzengiQuoteDocumentSource DzengiQuoteDocumentHandler QuotesFeed QuoteAcquisitionService ~~~ Не изменялся endpoint: ~~~text /api/v1/ticker/24hr ~~~ Не изменялся REST fallback, восстановленный и зафиксированный в Build 040. ### Каноническая модель Quote Не изменялись: ~~~text symbol last_price bid_price ask_price source exchange_timestamp received_at ~~~ ### WebSocket acquisition pipeline Не изменялись: ~~~text DzengiWebSocketQuoteAdapter validate_dzengi_websocket_quote_schema parse_dzengi_websocket_quote validate_dzengi_websocket_quote_values map_dzengi_websocket_quote_to_quote ~~~ ### Runtime semantics Не изменялись: - выбор торгового инструмента; - запуск и остановка market runtime; - reconnect logic; - обработка WebSocket-соединения; - вычисление spread; - формирование runtime-состояния; - execution pricing; - стратегия; - автоторговля; - Telegram UI; - журнал. --- ## 8. Архитектурный результат До Build 041 часть legacy integration layer всё ещё содержала собственную интерпретацию WebSocket payload: ~~~text Raw WebSocket payload ├──→ legacy parser in market_stream ├──→ legacy parser in market_data_runner └──→ canonical DzengiWebSocketQuoteAdapter ~~~ После Build 041: ~~~text Raw WebSocket payload ↓ DzengiWebSocketQuoteAdapter ↓ Quote ↓ market_stream / market_data_runner ~~~ Таким образом: - разбор Dzengi WebSocket payload имеет единственный канонический путь; - transport-specific parsing находится в `market_data/acquisition`; - integration layer работает с канонической моделью `Quote`; - дублирующая legacy parsing logic удалена; - runtime-поведение сохранено. --- ## 9. Проверки После изменений выполнены целевые тесты integration layer: ~~~bash python -m pytest -q \ tests/unit/integrations/exchange/test_market_data_runner.py \ tests/unit/integrations/exchange/test_market_stream.py ~~~ Результат: ~~~text 15 passed in 0.10s ~~~ Выполнены целевые тесты канонического WebSocket quote acquisition pipeline: ~~~bash python -m pytest -q \ tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_quote_parser.py \ tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_quote_mapper.py \ tests/unit/market_data/acquisition/adapters/dzengi/test_websocket_quote_adapter.py \ tests/unit/market_data/acquisition/validation/test_websocket_quote_schema.py \ tests/unit/market_data/acquisition/validation/test_websocket_quote_values.py ~~~ Результат: ~~~text 24 passed in 0.02s ~~~ Выполнен полный набор тестов проекта: ~~~bash python -m pytest -q ~~~ Результат: ~~~text 614 passed in 1.77s ~~~ --- ## 10. Финальный grep-контроль После удаления legacy quote parsing выполнена проверка: ~~~bash grep -RIn \ --exclude-dir="__pycache__" \ --exclude="*.pyc" \ -E "_extract_depth_prices|_extract_first_price|_extract_best_price|_extract_market_event|first\.get\(\"bidPrice\"\)|first\.get\(\"askPrice\"\)" \ src tests ~~~ Результат: ~~~text совпадений нет ~~~ Это подтверждает отсутствие проверяемых legacy helpers и прямого parsing `bidPrice` / `askPrice` через старый integration-код. --- ## 11. Сохранённые допустимые transport-specific поля После Build 041 в проекте по-прежнему существуют обращения к: ~~~text lastPrice bidPrice askPrice closeTime eventTime bids asks ~~~ Их наличие само по себе не является legacy-зависимостью. Они допустимы внутри канонического Dzengi acquisition adapter: ~~~text market_data/acquisition/adapters/dzengi/ market_data/acquisition/validation/ ~~~ поскольку именно этот слой отвечает за: - знание transport-specific формата Dzengi; - structural validation; - parsing; - value validation; - mapping в каноническую модель `Quote`. Также такие поля допустимы в unit-тестах соответствующего transport adapter. --- ## 12. Критерии завершения Build Build 041 считается завершённым, поскольку выполнены все критерии: - [x] проведён аудит legacy quote parsing; - [x] подтверждён единственный канонический WebSocket parsing pipeline; - [x] удалён `_extract_depth_prices`; - [x] удалён `_extract_first_price`; - [x] удалён `_extract_best_price`; - [x] удалён `_extract_market_event`; - [x] удалены проверяемые прямые обращения к `bidPrice` и `askPrice` в legacy integration layer; - [x] REST Quotes Feed не изменён; - [x] REST fallback Build 040 сохранён; - [x] каноническая модель `Quote` не изменена; - [x] торговая логика не изменена; - [x] runtime semantics не изменены; - [x] целевые integration-тесты проходят; - [x] целевые acquisition-тесты проходят; - [x] полный набор из 614 тестов проходит; - [x] финальный grep не обнаруживает удалённые legacy helpers. --- ## 13. Commit Build зафиксирован коммитом: ~~~text 24a4da9 build 041: remove legacy websocket quote parsing ~~~ Предыдущий Build: ~~~text e68ed7f build 040: restore quote REST fallback after build 039 ~~~ --- ## 14. Итог Build 041 завершает удаление подтверждённых остатков legacy WebSocket quote parsing из integration layer. Итоговая архитектура: ~~~text Dzengi REST ticker/24hr ↓ DzengiQuoteDocumentSource ↓ DzengiQuoteDocumentHandler ↓ QuotesFeed ↓ QuoteAcquisitionService ↓ Quote Dzengi WebSocket message ↓ DzengiWebSocketQuoteAdapter ↓ Quote Quote ↓ runtime / execution / strategy / UI consumers ~~~ После Build 041: - REST и WebSocket transport-specific parsing сосредоточен в `market_data/acquisition`; - runtime получает каноническую модель `Quote`; - legacy WebSocket parsing helpers удалены; - дублирующая интерпретация raw payload устранена; - рабочая логика текущих файлов сохранена; - полный test suite подтверждает отсутствие обнаруженных регрессий. Build 041 завершён.