# Build 060.23 — Trade Stream Acquisition Integration **Engineering Migration Report** --- # Контроль документа | Свойство | Значение | |----------|----------| | Build | 060.23 | | Название | Trade Stream Acquisition Integration | | Статус | Completed | | Проект | Dzentra | | Подсистема | Market Data Acquisition | | Компонент | Trade Stream Acquisition Integration Layer | | Версия | 1.0 | --- # Связанные документы - [Build 060.23 Architecture](build_060_23_architecture.md) — архитектурная спецификация Build. - [Build 060.22 Engineering Migration Report](build_060_22.md) — предыдущий Build. - [Build 060.22 Architecture](build_060_22_architecture.md) — спецификация Acquisition Runtime Service. - [Build 060.20.1 Engineering Migration Report](build_060_20_1.md) — корректирующий Build владения состоянием Trade Stream. - [Build 060.19 Engineering Migration Report](build_060_19.md) — Trade Recovery. - [Build 060.18 Engineering Migration Report](build_060_18.md) — Trade Stream Consistency. --- # Цель Build Build 060.22 завершил создание минимального исполняемого сервисного слоя Acquisition Runtime. После предыдущего этапа в системе уже существовали: ```text AcquisitionRuntimeServiceProtocol AcquisitionRuntimeService AcquisitionRuntimeCommand AcquisitionRuntimeEvent ``` Runtime Service уже умел детерминированно исполнять инфраструктурные команды: ```text ConnectCommand DisconnectCommand SubscribeCommand UnsubscribeCommand SendTextCommand SendBinaryCommand ``` Однако Runtime по-прежнему оставался изолированным от Trade Stream Acquisition Pipeline. В системе отсутствовал отдельный компонент, который: - формировал команду подписки Trade Stream; - передавал её в Acquisition Runtime; - принимал один входящий WebSocket-документ; - преобразовывал его через Unified Adapter; - выделял канонический `Trade`; - передавал `Trade` в Consistency Layer; - возвращал согласованный результат `Trade | None`. Главной задачей Build 060.23 стало создание локального интеграционного слоя: ```text TradeStreamAcquisitionService ``` и его публичного контракта: ```text TradeStreamAcquisitionServiceProtocol ``` После завершения Build система получает: - единую сервисную точку подписки Trade Stream; - единый путь обработки входящего Trade-сообщения; - независимость сервиса от конкретной exchange-specific реализации адаптера; - интеграцию с `TradeStreamConsistencyProtocol`; - отдельное unit-покрытие координатора; - интеграционные тесты полного локального Dzengi pipeline; - подтверждённую регрессионную совместимость Runtime, Consistency, Recovery, Feeds и Dzengi Adapters. При этом Build сознательно не реализует production WebSocket transport, receive-loop, reconnect и Runtime Recovery. --- # Предпосылки К началу настоящего Build были доступны следующие подсистемы. ## Runtime Layer ```text AcquisitionRuntimeServiceProtocol AcquisitionRuntimeService AcquisitionRuntimeCommandDispatcherProtocol AcquisitionRuntimeEventPublisherProtocol ``` ## Trade Subscription Layer ```text build_trade_subscribe_command() ``` ## WebSocket Adapter Layer ```text DzengiUnifiedWebSocketAdapter DzengiWebSocketTradeAdapter ``` ## Consistency Layer ```text TradeStreamConsistencyProtocol TradeStreamConsistencyController TradeStreamStateStore ``` ## Canonical Model ```text Trade ``` Каждый компонент уже был реализован и протестирован отдельно. Однако их взаимодействие не было оформлено в единый Acquisition Service. Архитектура до Build выглядела следующим образом: ```text Subscription Builder │ ▼ SubscribeCommand │ ▼ AcquisitionRuntimeService ``` и отдельно: ```text WebSocket Document │ ▼ DzengiUnifiedWebSocketAdapter │ ▼ Trade ``` и отдельно: ```text Trade │ ▼ TradeStreamConsistencyController │ ▼ Trade | None ``` Build 060.23 должен был соединить эти независимые части без изменения их существующего поведения. --- # Результаты архитектурного аудита Перед реализацией были проанализированы: ```text src/market_data/acquisition/protocol.py src/market_data/acquisition/service.py src/market_data/acquisition/registry.py src/market_data/acquisition/feeds/trades_feed.py src/market_data/acquisition/subscriptions/trades.py src/market_data/acquisition/adapters/dzengi/websocket.py src/market_data/acquisition/runtime/acquisition_runtime_service.py src/market_data/acquisition/runtime/acquisition_runtime_service_protocol.py src/market_data/acquisition/consistency/trade_stream_protocol.py src/market_data/acquisition/consistency/trade_stream_consistency_controller.py src/market_data/acquisition/consistency/trade_stream_state_store.py ``` Дополнительно были изучены legacy-компоненты: ```text src/integrations/exchange/market_stream.py src/integrations/exchange/market_data_runner.py src/integrations/exchange/ws_client.py src/integrations/exchange/service.py ``` и соответствующие unit-тесты. Аудит подтвердил следующие выводы. --- ## Существующий TradesFeed остаётся синхронным REST Feed Текущий `TradesFeed` работает по модели: ```text TradeDocumentSource │ ▼ TradeDocumentHandler │ ▼ Trade ``` Он не владеет: - WebSocket lifecycle; - Runtime Commands; - активными подписками; - receive-loop; - reconnect; - Consistency State. Поэтому внедрение Runtime в существующий `TradesFeed` было признано нарушением Single Responsibility. Build 060.23 не изменяет `TradesFeed`. --- ## Legacy MarketDataRunner не является точкой интеграции `MarketDataRunner` уже содержит собственные: - task lifecycle; - reconnect-циклы; - REST fallback; - quote-specific runtime state; - журналирование; - status events. Подключение нового Trade Stream Acquisition Service внутрь него смешало бы новую Acquisition Architecture с существующим legacy runtime и преждевременно затронуло бы Scope Build 060.24. Поэтому `MarketDataRunner` не изменяется. --- ## Legacy market_stream.py не является Composition Root `market_stream.py` представляет отдельный quote-specific WebSocket-контур. Его запуск в `main.py` не является активной точкой новой Trade Stream Architecture. Файл не изменяется и не используется как Composition Root Build 060.23. --- ## ExchangeWebSocketClient не реализует новые Runtime Protocol `ExchangeWebSocketClient` является специализированным legacy-клиентом для depth stream. Он: - самостоятельно открывает соединение; - самостоятельно отправляет depth request; - самостоятельно выполняет ping; - содержит внутренний цикл; - возвращает raw document; - жёстко связан со стаканом. Следовательно он не является реализацией: ```text WebSocketTransportProtocol WebSocketSessionProtocol WebSocketSubscriptionManagerProtocol ``` и не адаптируется в рамках Build 060.23. --- ## Trade consumer и Trade store отсутствуют Repository-wide поиск не выявил утверждённых компонентов: ```text TradeStore TradeConsumer publish_trade() on_trade() consume_trade() ``` Поэтому новый сервис не публикует и не сохраняет Trade. Его результатом остаётся: ```text Trade | None ``` --- ## trades.unsubscribe не поддерживается биржей Предыдущий инженерный аудит Dzengi WebSocket API подтвердил отсутствие поддерживаемой операции: ```text trades.unsubscribe ``` Поэтому Build 060.23 не создаёт фиктивный exchange-specific unsubscribe builder. Завершение Trade Stream в будущем должно выполняться через lifecycle соединения либо иной подтверждённый механизм. --- # Архитектурное решение По результатам аудита утверждена следующая схема. ```text symbols │ ▼ TradeStreamAcquisitionService.subscribe() │ ▼ build_trade_subscribe_command() │ ▼ SubscribeCommand │ ▼ AcquisitionRuntimeServiceProtocol.dispatch() ``` Входящий поток: ```text raw WebSocket document │ ▼ TradeStreamMessageAdapterProtocol.map_message() │ ▼ Quote | CandleCloseEvent | Trade │ ▼ isinstance(result, Trade) │ ├── нет ──► None │ ▼ TradeStreamConsistencyProtocol.accept() │ ▼ Trade | None ``` Новый сервис является stateless-координатором. Он не создаёт транспорт, не запускает фоновые задачи и не хранит состояние Trade Stream. --- # Правило уникальности имён файлов Build 060.23 сохраняет обязательное правило проекта Dzentra: > Новые файлы должны иметь уникальные имена в масштабе репозитория независимо от каталога. Build не создаёт новые общие файлы: ```text service.py protocol.py test_service.py ``` Вместо них добавлены уникальные имена: ```text trade_stream_acquisition_protocol.py trade_stream_acquisition_service.py trade_stream_message_adapter_protocol.py test_trade_stream_acquisition_service.py test_dzengi_trade_stream_acquisition_integration.py ``` Имена однозначно отражают назначение файлов и не требуют знания родительского каталога. --- # TradeStreamAcquisitionServiceProtocol В рамках Build создан новый публичный контракт: ```text TradeStreamAcquisitionServiceProtocol ``` Файл: ```text src/market_data/acquisition/ trade_stream_acquisition_protocol.py ``` Protocol определяет две операции. ## subscribe() ```python async def subscribe( symbols: tuple[str, ...], *, correlation_id: str | None = None, ) -> None ``` Метод отвечает только за формирование и передачу команды подписки в Runtime. ## handle_message() ```python def handle_message( document: object, ) -> Trade | None ``` Метод отвечает только за обработку одного входящего документа. Другие публичные операции не вводятся. В частности отсутствуют: ```text start() stop() run() receive() reconnect() recover() publish() ``` --- # TradeStreamMessageAdapterProtocol Во время реализации было выявлено, что первоначальная зависимость сервиса от конкретного: ```text DzengiUnifiedWebSocketAdapter ``` создавала прямую exchange-specific связь внутри координационного слоя. Для устранения этой связи введён отдельный контракт: ```text TradeStreamMessageAdapterProtocol ``` Файл: ```text src/market_data/acquisition/ trade_stream_message_adapter_protocol.py ``` Protocol определяет одну операцию: ```python def map_message( document: object, ) -> TradeStreamMappedMessage ``` Тип результата: ```text TradeStreamMappedMessage ``` объединяет: ```text Quote CandleCloseEvent Trade ``` `DzengiUnifiedWebSocketAdapter` структурно удовлетворяет этому Protocol и не требует изменения. Благодаря этому `TradeStreamAcquisitionService` зависит от абстракции, а конкретный Dzengi Adapter подключается только при композиции и в интеграционных тестах. --- # TradeStreamAcquisitionService Главным production-компонентом Build является: ```text TradeStreamAcquisitionService ``` Файл: ```text src/market_data/acquisition/ trade_stream_acquisition_service.py ``` Сервис реализует: ```text TradeStreamAcquisitionServiceProtocol ``` и координирует три зависимости: ```text AcquisitionRuntimeServiceProtocol TradeStreamMessageAdapterProtocol TradeStreamConsistencyProtocol ``` Сервис не создаёт зависимости самостоятельно. --- # Зависимости сервиса Конструктор принимает: ```text runtime_service adapter consistency_controller ``` Полная схема: ```text TradeStreamAcquisitionService │ ├── AcquisitionRuntimeServiceProtocol ├── TradeStreamMessageAdapterProtocol └── TradeStreamConsistencyProtocol ``` Это обеспечивает: - независимость от конкретного Runtime implementation; - независимость от конкретной биржи; - независимость от конкретной Consistency implementation; - возможность изолированного unit-тестирования. --- # subscribe() Метод `subscribe()` использует существующий builder: ```text build_trade_subscribe_command() ``` Последовательность: ```text symbols │ ▼ build_trade_subscribe_command( symbols, correlation_id=... ) │ ▼ SubscribeCommand │ ▼ runtime_service.dispatch(command) ``` Сервис не: - сериализует JSON; - создаёт TransportTextMessage вручную; - строит subscription key самостоятельно; - нормализует символы; - хранит активные подписки; - выполняет connect; - выполняет retry. --- # correlation_id Параметр: ```text correlation_id ``` передаётся без изменения существующему Subscription Builder. Если он не задан, его создание остаётся ответственностью builder. Сервис не генерирует correlation id самостоятельно. --- # handle_message() Метод `handle_message()` обрабатывает один входящий документ. Последовательность: ```text document │ ▼ adapter.map_message(document) │ ▼ mapped result │ ▼ isinstance(mapped result, Trade) ``` Если результат не является `Trade`, сервис возвращает: ```text None ``` Если результат является `Trade`, сервис вызывает: ```text consistency_controller.accept(trade) ``` и возвращает результат без изменения. --- # Поведение для Quote Если Adapter возвращает: ```text Quote ``` сервис: - не вызывает Consistency; - не преобразует Quote; - не публикует Quote; - возвращает `None`. --- # Поведение для CandleCloseEvent Если Adapter возвращает: ```text CandleCloseEvent ``` сервис: - не вызывает Consistency; - не преобразует CandleCloseEvent; - не публикует событие; - возвращает `None`. --- # Поведение для Trade Если Adapter возвращает: ```text Trade ``` сервис передаёт тот же экземпляр в: ```text TradeStreamConsistencyProtocol.accept() ``` Сервис не копирует и не изменяет модель. --- # Поведение для дубликата Если Consistency Layer возвращает: ```text None ``` это означает корректный дубликат. Сервис сохраняет результат и также возвращает: ```text None ``` Дополнительная логика не выполняется. --- # Сохранение идентичности результата Consistency Если `TradeStreamConsistencyProtocol.accept()` возвращает отдельный экземпляр `Trade`, сервис возвращает именно этот объект. Сервис не заменяет его исходным результатом Adapter и не создаёт копию. Данный инвариант подтверждён unit-тестом. --- # Обработка исключений Build не вводит отдельную иерархию ошибок Trade Stream Acquisition Service. Исключения распространяются без обёртки. ## Ошибка Subscription Builder Ошибка формирования SubscribeCommand передаётся вызывающей стороне. ## Ошибка Runtime dispatch Ошибка `runtime_service.dispatch()` передаётся вызывающей стороне. ## Ошибка Adapter Schema, parser, value validation или mapper error распространяется без изменения. ## Ошибка Consistency `TradeConsistencyError`, `TradeOrderingError` и иные исключения Consistency Layer распространяются без изменения. Сервис не создаёт общий `TradeStreamAcquisitionError`. --- # Stateless-архитектура `TradeStreamAcquisitionService` не хранит операционное состояние. В объекте сохраняются только внедрённые зависимости. Сервис не хранит: - symbols; - correlation id; - active subscriptions; - last trade; - duplicate window; - reconnect attempts; - recovery state; - runtime status; - received documents. Состояние Consistency принадлежит `TradeStreamStateStore`. Состояние Runtime будет принадлежать будущим Session и Subscription Manager. --- # Изменённые и добавленные файлы ## Новый Acquisition Service Protocol ```text src/market_data/acquisition/ trade_stream_acquisition_protocol.py ``` Добавлен: ```text TradeStreamAcquisitionServiceProtocol ``` --- ## Новый Trade Stream Message Adapter Protocol ```text src/market_data/acquisition/ trade_stream_message_adapter_protocol.py ``` Добавлены: ```text TradeStreamMappedMessage TradeStreamMessageAdapterProtocol ``` --- ## Новый Trade Stream Acquisition Service ```text src/market_data/acquisition/ trade_stream_acquisition_service.py ``` Добавлен: ```text TradeStreamAcquisitionService ``` --- ## Unit-тест Acquisition Service ```text tests/unit/market_data/acquisition/ test_trade_stream_acquisition_service.py ``` Добавлены Fake-реализации: - `FakeRuntimeService`; - `FakeMessageAdapter`; - `FakeConsistencyController`. Проверены subscribe и handle_message без привязки к Dzengi. --- ## Интеграционный тест Dzengi pipeline ```text tests/unit/market_data/acquisition/ test_dzengi_trade_stream_acquisition_integration.py ``` Проверен реальный локальный pipeline: ```text Dzengi WebSocket document │ ▼ DzengiUnifiedWebSocketAdapter │ ▼ Canonical Trade │ ▼ TradeStreamConsistencyController │ ▼ Trade | None ``` --- ## Архитектурная документация ```text docs/migrations/ build_060_23_architecture.md ``` Документ фиксирует: - границы Build; - ответственность сервиса; - dependency direction; - ADR; - Definition of Done; - связь с последующими этапами. --- # Файлы, которые не изменялись Build не изменяет: ```text src/market_data/acquisition/feeds/trades_feed.py src/market_data/acquisition/subscriptions/trades.py src/market_data/acquisition/adapters/dzengi/websocket.py src/market_data/acquisition/runtime/acquisition_runtime_service.py src/market_data/acquisition/runtime/acquisition_runtime_service_protocol.py src/market_data/acquisition/consistency/trade_stream_protocol.py src/market_data/acquisition/consistency/trade_stream_consistency_controller.py src/market_data/acquisition/consistency/trade_stream_state_store.py src/market_data/acquisition/recovery/trade_recovery_controller.py ``` Также не изменяются: ```text src/integrations/exchange/market_stream.py src/integrations/exchange/market_data_runner.py src/integrations/exchange/ws_client.py src/integrations/exchange/service.py ``` Build не затрагивает legacy quote runtime. --- # Unit-тестирование TradeStreamAcquisitionService Новый сервис покрыт отдельным набором unit-тестов. Проверены следующие сценарии: - сервис соответствует `TradeStreamAcquisitionServiceProtocol`; - `subscribe()` передаёт одну команду Runtime; - передаётся именно `SubscribeCommand`; - symbols сохраняются в сформированной команде; - `correlation_id` сохраняется в payload; - Adapter вызывается один раз; - Trade передаётся в Consistency; - возвращается тот же объект, который вернула Consistency; - `None` для корректного дубликата сохраняется; - Quote игнорируется; - CandleCloseEvent игнорируется; - Consistency не вызывается для не-Trade; - ошибка Adapter распространяется без изменения; - ошибка Consistency распространяется без изменения. Результат совместного локального прогона unit и integration тестов: ```text 14 passed ``` --- # Интеграционное тестирование Dzengi pipeline Интеграционный тест использует настоящий: ```text DzengiUnifiedWebSocketAdapter ``` и документ реального WebSocket Trade-контракта: ```text status destination payload ``` Проверяется полный путь: ```text schema validation │ ▼ parser │ ▼ value validation │ ▼ mapper │ ▼ Trade │ ▼ Consistency ``` Подтверждены канонические поля: ```text symbol trade_id price quantity executed_at aggressor_side source ``` Для WebSocket Trade подтверждён источник: ```text dzengi_websocket_trade ``` Также реальный `TradeStreamConsistencyController` подтвердил, что повторная обработка идентичного Trade возвращает: ```text None ``` --- # Расширенное регрессионное тестирование После завершения реализации выполнен совместный прогон: ```text Runtime Consistency Recovery Feeds Dzengi Adapters Trade Stream Acquisition Unit Tests Dzengi Trade Stream Acquisition Integration Tests ``` Охвачены каталоги: ```text tests/unit/market_data/acquisition/runtime tests/unit/market_data/acquisition/consistency tests/unit/market_data/acquisition/recovery tests/unit/market_data/acquisition/feeds tests/unit/market_data/acquisition/adapters/dzengi ``` и новые файлы: ```text tests/unit/market_data/acquisition/ test_trade_stream_acquisition_service.py tests/unit/market_data/acquisition/ test_dzengi_trade_stream_acquisition_integration.py ``` Результат: ```text 496 passed ``` Тем самым подтверждено: - Runtime Service не нарушен; - Runtime Protocol Layer не нарушен; - Trade Stream Consistency не нарушена; - TradeStreamStateStore не нарушен; - Trade Recovery не нарушена; - существующие Feeds не нарушены; - REST Trade Adapter не нарушен; - WebSocket Quote Adapter не нарушен; - WebSocket OHLC Adapter не нарушен; - WebSocket Trade Adapter не нарушен; - Unified WebSocket Adapter не нарушен; - новый Acquisition Service корректно интегрируется с реальным Dzengi Adapter; - duplicate filtering работает через реальный Consistency Controller. --- # Проверка качества Git diff После реализации выполнены: ```text git diff --stat git diff --check ``` `git diff --check` не выявил ошибок форматирования в отслеживаемых изменениях. Новые production и test-файлы на момент проверки оставались untracked, поэтому не отображались в `git diff --stat`. Изменение: ```text .gitignore ``` не относится к Scope Build 060.23 и не должно включаться в коммит этапа. --- # Обратная совместимость Build сохраняет существующее поведение всех ранее реализованных подсистем. Не изменились: - `TradesFeedProtocol`; - `TradesFeed`; - `TradeDocumentSource`; - `TradeDocumentHandler`; - `Trade` model; - Runtime Commands; - Runtime Events; - Acquisition Runtime Service; - Trade Subscription Builder; - Unified Dzengi Adapter; - Trade Stream Consistency API; - Trade Recovery API; - Feed Registry; - legacy Exchange Runtime. Новый сервис добавляется как отдельный слой и не заменяет существующие REST Feed. --- # Производительность ## subscribe() Метод выполняет: - один вызов Subscription Builder; - один асинхронный вызов Runtime dispatch. Алгоритмическая сложность зависит от существующего builder и количества symbols. Сервис не создаёт дополнительных копий команд и payload. ## handle_message() Метод выполняет: - один вызов Adapter; - одну `isinstance`-проверку; - не более одного вызова Consistency. Координационные накладные расходы постоянны: ```text O(1) ``` без учёта внутренней стоимости Adapter и Consistency Layer. Сервис не вводит: - очереди; - блокировки; - фоновые задачи; - retries; - дополнительную сериализацию; - storage writes. --- # Подтверждённые архитектурные инварианты ## Runtime не знает о Trade Новые зависимости направлены из Acquisition Service в Runtime Protocol, а не обратно. --- ## TradeStreamAcquisitionService не зависит от Dzengi Production-сервис зависит от: ```text TradeStreamMessageAdapterProtocol ``` а не от конкретного `DzengiUnifiedWebSocketAdapter`. --- ## Dzengi Adapter проверяется отдельно Exchange-specific корректность подтверждена интеграционным тестом. --- ## Consistency остаётся единственной точкой проверки последовательности Сервис не реализует duplicate detection или ordering logic самостоятельно. --- ## TradeStreamStateStore остаётся единственным владельцем Consistency State Новый сервис не хранит состояния по symbols. --- ## Существующий TradesFeed не изменяется REST Feed и WebSocket Acquisition Integration остаются отдельными путями. --- ## Сервис не публикует Trade До появления утверждённого Trade Consumer результат возвращается вызывающей стороне. --- ## Сервис не владеет WebSocket lifecycle Он не реализует connect, disconnect, receive-loop или reconnect. --- ## trades.unsubscribe не создаётся Build не вводит неподдерживаемую exchange-команду. --- ## Уникальность новых имён файлов соблюдена Все новые production и test-файлы имеют уникальные имена. --- # Что не входит в Scope Build Build сознательно не реализует: - production `WebSocketTransportProtocol`; - production `WebSocketSessionProtocol`; - production `WebSocketSubscriptionManagerProtocol`; - Runtime Event Publisher implementation; - Runtime Supervisor; - receive-loop; - reconnect; - heartbeat; - scheduler; - resubscribe orchestration; - автоматический вызов Trade Recovery; - восстановление Trade Stream после разрыва; - Trade Store; - Trade Consumer; - публикацию Trade; - root Composition; - изменение `MarketDataRunner`; - изменение `market_stream.py`; - изменение `ExchangeWebSocketClient`; - изменение существующего `TradesFeed`; - биржевую команду `trades.unsubscribe`. Отсутствие перечисленных компонентов является осознанной границей этапа. --- # Архитектурное значение Build Build 060.23 впервые соединяет ранее независимые части Trades Acquisition Architecture: ```text Subscription Builder Runtime Service Unified Adapter Canonical Trade Consistency Controller ``` До Build каждый компонент существовал отдельно. После завершения Build появляется единая сервисная граница: ```text TradeStreamAcquisitionService ``` которая поддерживает оба направления интеграции: ```text outbound subscription command ``` и: ```text inbound Trade message processing ``` При этом сервис остаётся stateless, exchange-independent и не вмешивается в lifecycle Runtime. --- # Связь с последующими Build > **Ретроспективное уточнение 060.30.5 (2026-08-03).** > > Названия ниже сохраняют ранний прогноз. Фактически > [060.24](build_060_24.md) завершил внутреннюю Runtime Recovery, > [060.25](build_060_25.md) — Production Runtime Integration, > [060.26](build_060_26.md) — Integration & Regression, > [060.27](build_060_27.md)–[060.29](build_060_29.md) — > Storage/Checkpoint/Access, а Final Documentation выполняется в > [060.30](build_060_30_architecture.md). > Актуальная последовательность: > [Master Roadmap](../roadmap/master-roadmap.md). > После 060.30 утверждён только 061.00; номера следующих Feed-веток ещё > не назначены. ## Build 060.24 — Reconnect & Runtime Recovery На основе текущего сервиса должны быть построены: - Runtime reconnect orchestration; - автоматическое восстановление подписок; - восстановление Trade Stream continuity; - координация Consistency и Recovery после разрыва; - lifecycle соединения. --- ## Build 060.25 — Integration & Regression Будет выполнена полная проверка production graph: ```text Runtime Trade Stream Acquisition Consistency Recovery Trades Feed ``` после появления реальных Runtime implementations. --- ## Build 060.26 — Final Documentation Будет подготовлена итоговая документация всей ветки Trades Feed Runtime. --- # Заключение Build 060.23 завершает service-level интеграцию Trade Stream с Acquisition Runtime. В рамках этапа реализованы: ```text TradeStreamAcquisitionServiceProtocol TradeStreamMessageAdapterProtocol TradeStreamAcquisitionService ``` Новый сервис: - формирует Trade SubscribeCommand через существующий builder; - передаёт команду в `AcquisitionRuntimeServiceProtocol`; - обрабатывает один WebSocket-документ через Adapter Protocol; - пропускает только канонический `Trade`; - делегирует проверку последовательности Consistency Layer; - возвращает `Trade | None`; - не хранит состояние; - не содержит exchange-specific логики; - не реализует reconnect и Recovery. Unit-тесты подтверждают поведение координатора независимо от биржи. Интеграционные тесты подтверждают полный локальный Dzengi pipeline и duplicate filtering через реальный Consistency Controller. Расширенная регрессия завершена результатом: ```text 496 passed ``` --- # Итог Build После завершения Build 060.23 система обладает следующими возможностями. ✓ Создан `TradeStreamAcquisitionServiceProtocol`. ✓ Создан `TradeStreamMessageAdapterProtocol`. ✓ Реализован `TradeStreamAcquisitionService`. ✓ Подписка Trade Stream проходит через существующий Subscription Builder. ✓ SubscribeCommand передаётся через `AcquisitionRuntimeServiceProtocol`. ✓ Входящие сообщения проходят через Adapter Protocol. ✓ `DzengiUnifiedWebSocketAdapter` структурно совместим с новым Protocol. ✓ Только `Trade` передаётся в Consistency. ✓ Quote и CandleCloseEvent игнорируются без побочных эффектов. ✓ Дубликат возвращается как `None`. ✓ Исключения Adapter и Consistency распространяются без изменения. ✓ Проверена идентичность результата Consistency. ✓ Реальный Dzengi WebSocket Trade document проходит полный pipeline. ✓ Реальный Consistency Controller отклоняет идентичный дубликат. ✓ Существующий `TradesFeed` не изменён. ✓ Legacy Exchange Runtime не изменён. ✓ Все новые файлы имеют уникальные имена. ✓ Локальный функциональный прогон завершён результатом `14 passed`. ✓ Расширенный regression завершён результатом `496 passed`. Build **060.23 — Trade Stream Acquisition Integration** считается полностью завершённым и готовым к фиксации в Git.