# Build 034 — Dzengi WebSocket quote parsing и адаптер **Статус:** Завершён **Подсистема:** Market Data **Контур:** Market Data Acquisition / Quotes Feed **Проект:** Dzentra **Язык документации:** Русский --- ## 1. Цель Build Цель Build 034 — создать специализированный контур обработки WebSocket-сообщений котировок Dzengi и преобразования их в каноническую модель `Quote`. Build должен был изолировать знание транспортных форматов Dzengi WebSocket от канонического слоя Market Data и подготовить архитектурную основу для последующего перевода market runtime на новый Quotes Feed. В рамках Build реализована цепочка: ```text Dzengi WebSocket message ↓ schema validation ↓ parser ↓ DzengiWebSocketQuoteResponse ↓ value validation ↓ mapper ↓ DzengiWebSocketQuoteAdapter ↓ canonical Quote ``` --- ## 2. Архитектурный результат После Build 034 обработка WebSocket-котировок Dzengi получила специализированный адаптерный контур внутри: ```text src/market_data/acquisition/ ``` Транспортные особенности Dzengi WebSocket больше не должны распространяться на каноническую модель `Quote` и будущих потребителей Quotes Feed. Архитектурная граница имеет следующий вид: ```text Dzengi-specific transport formats ↓ adapters/dzengi ↓ canonical Quote ↓ Quotes Feed / Quote Store / consumers ``` Каноническая модель: ```text src/market_data/acquisition/models/quote.py ``` остаётся независимой от: ```text payload Payload symbolName bid ask ofr bids asks price p bidPrice askPrice ``` Эти имена являются особенностями внешнего транспорта Dzengi и обрабатываются внутри адаптерного слоя. --- ## 3. Изменённые файлы В рамках Build 034 изменены следующие файлы: ```text src/market_data/acquisition/adapters/dzengi/models.py src/market_data/acquisition/adapters/dzengi/parser.py src/market_data/acquisition/adapters/dzengi/mapper.py src/market_data/acquisition/validation/schema.py src/market_data/acquisition/validation/values.py ``` Добавлен специализированный WebSocket-адаптер: ```text src/market_data/acquisition/adapters/dzengi/websocket.py ``` --- ## 4. Добавленные тесты Добавлены следующие специализированные тестовые файлы: ```text tests/unit/market_data/acquisition/validation/test_websocket_quote_schema.py tests/unit/market_data/acquisition/validation/test_websocket_quote_values.py 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 ``` --- ## 5. Поддерживаемые WebSocket-форматы Новый адаптерный контур поддерживает транспортные варианты, существовавшие в legacy-реализации Dzengi WebSocket. ### 5.1. Оболочки сообщения Поддерживаются: ```text payload Payload ``` а также вложенная двойная оболочка. Примеры допустимой структуры: ```json { "payload": { "symbolName": "BTC/USD_LEVERAGE", "bid": "64159.45", "ask": "64159.55" } } ``` и: ```json { "Payload": { "Payload": { "symbolName": "BTC/USD_LEVERAGE", "bids": [ ["64159.45", "1.0"] ], "asks": [ ["64159.55", "1.0"] ] } } } ``` --- ## 6. Поддерживаемые поля символа Адаптер поддерживает следующие транспортные имена символа: ```text symbolName symbol ``` После обработки внешнее представление преобразуется в каноническое поле: ```text Quote.symbol ``` --- ## 7. Поддерживаемые представления bid и ask Поддерживаются прямые поля: ```text bid ask ``` вариант Dzengi: ```text bid ofr ``` а также depth-представление: ```text bids asks ``` Для элементов depth поддерживаются представления в виде: ```text list dict ``` Поддерживаемые имена поля цены внутри depth-элементов: ```text price p bidPrice askPrice ``` --- ## 8. Семантика last_price для depth-сообщений Для WebSocket depth-сообщений, содержащих лучшие цены bid и ask, сохранена legacy-семантика: ```text last_price = midpoint(best_bid, best_ask) ``` То есть каноническое значение `Quote.last_price` определяется как середина между лучшей ценой покупки и лучшей ценой продажи. Это решение сохраняет обратную совместимость с существующим поведением market runtime до его последующего архитектурного перевода. --- ## 9. Обработка timestamp WebSocket timestamp является необязательным. Если транспортное сообщение содержит допустимый timestamp биржи, он преобразуется в: ```text Quote.exchange_timestamp ``` Если timestamp отсутствует, каноническая модель допускает: ```text exchange_timestamp = None ``` Время фактического получения и обработки котировки фиксируется отдельно: ```text Quote.received_at ``` Таким образом, сохраняется разделение двух временных характеристик: ```text exchange_timestamp время события по данным биржи received_at время получения котировки системой Dzentra ``` --- ## 10. Schema validation Schema validation отвечает исключительно за структурную корректность WebSocket-документа. На этом этапе проверяется возможность извлечения необходимых частей сообщения без переноса бизнес-логики в транспортный слой. Schema validation не должна: ```text создавать canonical Quote выполнять mapping управлять runtime записывать данные в Quote Store обращаться к MarketPriceCache ``` --- ## 11. Parser Parser преобразует структурно проверенный WebSocket-документ в специализированную raw-модель Dzengi: ```text DzengiWebSocketQuoteResponse ``` Parser сохраняет границу между: ```text сырой внешний документ ``` и: ```text типизированное транспортное представление Dzengi ``` Parser не создаёт канонический `Quote`. --- ## 12. Value validation Value validation проверяет семантическую допустимость извлечённых значений. В частности, контур должен обеспечивать корректность значений, необходимых для построения канонической котировки: ```text symbol bid price ask price timestamp, если присутствует ``` Проверка значений выполняется до mapping в каноническую модель. --- ## 13. Mapper Mapper преобразует проверенную raw-модель Dzengi WebSocket в: ```text Quote ``` На этой границе происходит переход: ```text Dzengi-specific representation ↓ canonical Dzentra representation ``` После mapping потребитель не должен зависеть от исходного формата WebSocket-сообщения. --- ## 14. DzengiWebSocketQuoteAdapter Специализированный адаптер инкапсулирует полный конвейер обработки одного WebSocket-сообщения: ```text raw document ↓ schema validation ↓ parsing ↓ value validation ↓ mapping ↓ Quote ``` Результатом успешной обработки является канонический объект: ```text Quote ``` Адаптер не отвечает за: ```text поддержание WebSocket-соединения reconnect runtime lifecycle регистрацию market runtime запись в Quote Store legacy MarketPriceCache facade ``` Эти обязанности принадлежат другим архитектурным слоям. --- ## 15. Что намеренно не изменялось В Build 034 не изменялись runtime-файлы: ```text src/integrations/exchange/ws_client.py src/integrations/exchange/market_stream.py src/integrations/exchange/market_data_runner.py ``` Также Build 034 не выполнял переключение: ```text market runtime → Quotes Feed ``` и не удалял legacy-механизмы. Это принципиальная граница Build. Build 034 создаёт новый специализированный адаптерный контур, но не переключает на него существующий runtime. Перевод runtime предусмотрен следующим этапом: ```text Build 035 — Перевод market runtime на Quotes Feed ``` --- ## 16. Обратная совместимость В Build 034 сохранены существующие транспортные варианты legacy WebSocket-контура: ```text payload / Payload двойная оболочка symbolName / symbol bid + ask bid + ofr bids + asks depth item list depth item dict price / p / bidPrice / askPrice необязательный timestamp ``` Для depth-сообщений сохранено существующее правило: ```text last_price = midpoint(best_bid, best_ask) ``` Таким образом, Build не требует одномоментного удаления legacy runtime и подготавливает безопасный переход к новой архитектуре. --- ## 17. Проверка компиляции Выполнена проверка: ```bash python -m py_compile \ src/market_data/acquisition/adapters/dzengi/models.py \ src/market_data/acquisition/adapters/dzengi/parser.py \ src/market_data/acquisition/adapters/dzengi/mapper.py \ src/market_data/acquisition/adapters/dzengi/websocket.py \ src/market_data/acquisition/validation/schema.py \ src/market_data/acquisition/validation/values.py \ tests/unit/market_data/acquisition/validation/test_websocket_quote_schema.py \ tests/unit/market_data/acquisition/validation/test_websocket_quote_values.py \ 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 ``` Результат: ```text успешно ``` --- ## 18. Специализированные тесты Выполнена команда: ```bash python -m pytest \ tests/unit/market_data/acquisition/validation/test_websocket_quote_schema.py \ tests/unit/market_data/acquisition/validation/test_websocket_quote_values.py \ 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 \ -q ``` Результат: ```text 24 passed in 0.04s ``` --- ## 19. Полная регрессия Выполнена команда: ```bash python -m pytest -q ``` Результат: ```text 602 passed in 0.30s ``` Количество тестов до Build 034: ```text 578 passed ``` Количество тестов после Build 034: ```text 602 passed ``` Добавлено: ```text 24 теста ``` Полная регрессия подтверждает отсутствие обнаруженных регрессий в существующем коде проекта. --- ## 20. Итог Build Build 034 завершён полностью. Реализованы: ```text специализированная WebSocket raw-модель Dzengi WebSocket schema validation WebSocket parser WebSocket value validation WebSocket mapper DzengiWebSocketQuoteAdapter преобразование WebSocket-сообщения в canonical Quote поддержка legacy-вариантов формата Dzengi 24 специализированных теста ``` Не выполнялись: ```text переключение market runtime изменение ws_client.py изменение market_stream.py изменение market_data_runner.py удаление legacy WebSocket parsing удаление MarketPriceCache ``` Архитектурный результат: ```text Dzengi WebSocket transport ↓ Dzengi-specific validation / parsing / mapping ↓ canonical Quote ``` --- ## 21. Следующий Build Следующий этап: ```text Build 035 — Перевод market runtime на Quotes Feed ``` Его задача — подключить существующий market runtime к новому каноническому контуру котировок, используя созданные ранее: ```text Quote Quote Store Quotes Feed Dzengi REST Quotes Feed Dzengi WebSocket quote adapter ``` При этом переход должен выполняться без преждевременного удаления legacy-механизмов и с сохранением работоспособности существующего бота до завершения последующих этапов миграции.