1902 lines
53 KiB
Markdown
1902 lines
53 KiB
Markdown
# Build 060.8 — REST Trades Document Source
|
||
|
||
**Engineering Migration Report**
|
||
|
||
---
|
||
|
||
# Контроль документа
|
||
|
||
| Свойство | Значение |
|
||
|---|---|
|
||
| Build | 060.8 |
|
||
| Название | REST Trades Document Source |
|
||
| Статус | Завершён |
|
||
| Проект | Dzentra |
|
||
| Подсистема | Market Data Acquisition |
|
||
| Компонент | Trades Feed / Time & Sales |
|
||
| Версия документа | 1.0 |
|
||
| Дата завершения | 2026-07-19 |
|
||
|
||
---
|
||
|
||
# Цель Build
|
||
|
||
После завершения Build 060.7 подсистема обработки исторических сделок уже содержала полностью сформированный конвейер преобразования REST-документа в канонические сделки.
|
||
|
||
К этому моменту были реализованы:
|
||
|
||
- транспортная модель агрегированной сделки;
|
||
- Schema Validation;
|
||
- Parser;
|
||
- Value Validation;
|
||
- Mapper;
|
||
- REST Trade Adapter.
|
||
|
||
Pipeline обработки документа уже имел следующий вид:
|
||
|
||
```text
|
||
ValidatedRestAggTradesDocument
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Trade Adapter
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Parser
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Value Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Mapper
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[Trade]
|
||
```
|
||
|
||
Однако данный pipeline начинался уже после получения документа.
|
||
|
||
В архитектуре отсутствовал компонент, отвечающий исключительно за взаимодействие с REST API биржи.
|
||
|
||
Верхние уровни системы по-прежнему должны были самостоятельно:
|
||
|
||
- знать REST endpoint;
|
||
- формировать параметры HTTP-запроса;
|
||
- работать с ExchangeRestClient;
|
||
- создавать HTTP-клиент;
|
||
- обрабатывать транспортные ошибки.
|
||
|
||
Таким образом отсутствовал отдельный Source Layer для получения документа.
|
||
|
||
Это противоречило архитектуре остальных компонентов Acquisition Layer, где получение данных и их обработка являются независимыми слоями.
|
||
|
||
Следовательно возникла необходимость реализовать специализированный REST Source, который будет отвечать исключительно за получение документа агрегированных сделок.
|
||
|
||
Именно эту задачу решает Build 060.8.
|
||
|
||
Build намеренно **не включает**:
|
||
|
||
- Schema Validation;
|
||
- Parser;
|
||
- Value Validation;
|
||
- Mapper;
|
||
- REST Trade Adapter;
|
||
- REST polling;
|
||
- Trades Feed;
|
||
- Runtime Integration;
|
||
- Registry;
|
||
- Acquisition Service;
|
||
- объединение REST и WebSocket сделок;
|
||
- дедупликацию;
|
||
- сортировку;
|
||
- хранение истории;
|
||
- агрегацию;
|
||
- аналитическую обработку;
|
||
- production-интеграцию.
|
||
|
||
Все перечисленные задачи относятся к другим архитектурным слоям и будут реализованы отдельными Build.
|
||
|
||
---
|
||
|
||
# Архитектурный контекст
|
||
|
||
Во всей подсистеме Market Data Acquisition применяется единая архитектурная модель разделения ответственности.
|
||
|
||
Получение документа никогда не совмещается с его обработкой.
|
||
|
||
Полный pipeline Acquisition выглядит следующим образом.
|
||
|
||
```text
|
||
Exchange
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Client
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Document Source
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Raw Document
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Schema Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Validated Document
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Adapter
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Canonical Model
|
||
```
|
||
|
||
Именно такое разделение уже используется для остальных источников данных проекта.
|
||
|
||
Например:
|
||
|
||
- Instrument Document Source;
|
||
- Quote Document Source;
|
||
- Candles Document Source.
|
||
|
||
Каждый из этих компонентов отвечает исключительно за получение транспортного документа.
|
||
|
||
Ни один из них:
|
||
|
||
- не выполняет Schema Validation;
|
||
- не создаёт предметные модели;
|
||
- не анализирует содержимое документа;
|
||
- не выполняет бизнес-логику.
|
||
|
||
REST Trades должны полностью следовать этому архитектурному принципу.
|
||
|
||
После завершения Build 060.8 архитектура приобретает следующий вид.
|
||
|
||
```text
|
||
Exchange
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
ExchangeRestClient
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
DzengiTradesDocumentSource
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Raw REST Document
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Schema Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
ValidatedRestAggTradesDocument
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Trade Adapter
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Parser
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Value Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Mapper
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[Trade]
|
||
```
|
||
|
||
Таким образом Build завершает формирование Source Layer для REST Trade Pipeline.
|
||
|
||
---
|
||
|
||
# Исходное состояние
|
||
|
||
До начала Build 060.8 проект уже содержал следующие REST Source.
|
||
|
||
```text
|
||
DzengiInstrumentDocumentSource
|
||
|
||
DzengiQuoteDocumentSource
|
||
|
||
DzengiCandlesDocumentSource
|
||
```
|
||
|
||
Все три компонента использовали единый архитектурный подход.
|
||
|
||
Каждый Source:
|
||
|
||
- инкапсулировал ExchangeRestClient;
|
||
- выполнял HTTP GET;
|
||
- формировал параметры запроса;
|
||
- преобразовывал транспортные исключения;
|
||
- возвращал необработанный REST-документ.
|
||
|
||
При этом отдельный Source для получения агрегированных сделок отсутствовал.
|
||
|
||
Получение документа должно было выполняться напрямую через ExchangeRestClient.
|
||
|
||
Подобная схема нарушала единообразие архитектуры Acquisition Layer.
|
||
|
||
Кроме того, отсутствовал специализированный тип транспортной ошибки для REST Trade.
|
||
|
||
В системе уже существовали:
|
||
|
||
```text
|
||
InstrumentReferenceTransportError
|
||
|
||
QuoteTransportError
|
||
|
||
CandleTransportError
|
||
```
|
||
|
||
Но отсутствовал:
|
||
|
||
```text
|
||
TradeTransportError
|
||
```
|
||
|
||
Таким образом Build 060.8 должен был устранить оба архитектурных пробела:
|
||
|
||
- добавить REST Source;
|
||
- добавить специализированное транспортное исключение.
|
||
|
||
При этом никакие существующие компоненты проекта не должны были изменять собственную ответственность.
|
||
|
||
Build должен был остаться полностью **Local Additive Change**.
|
||
|
||
---
|
||
|
||
# Предварительный архитектурный аудит
|
||
|
||
Перед началом реализации был выполнен повторный аудит существующей реализации REST Source.
|
||
|
||
Были проанализированы следующие файлы.
|
||
|
||
```text
|
||
app/src/market_data/acquisition/adapters/dzengi/rest.py
|
||
|
||
app/src/integrations/exchange/rest_client.py
|
||
|
||
app/src/market_data/acquisition/exceptions.py
|
||
|
||
docs/migrations/build_060_7.md
|
||
```
|
||
|
||
Кроме анализа исходного кода был повторно проверен полный REST Pipeline Acquisition.
|
||
|
||
По результатам аудита были подтверждены следующие архитектурные выводы.
|
||
|
||
- ExchangeRestClient уже является универсальным HTTP-транспортом.
|
||
- REST Source не должен знать структуру документа.
|
||
- REST Source не должен выполнять Schema Validation.
|
||
- REST Source не должен выполнять Parser.
|
||
- REST Source не должен выполнять Value Validation.
|
||
- REST Source не должен выполнять Mapper.
|
||
- REST Source отвечает исключительно за получение документа и преобразование транспортных ошибок.
|
||
|
||
Именно эти выводы стали основой проектирования Build 060.8.
|
||
|
||
# Архитектурная задача Build
|
||
|
||
Главная задача Build 060.8 заключается не в добавлении новой логики обработки сделок.
|
||
|
||
Вся обработка документа уже полностью реализована предыдущими Build.
|
||
|
||
Задача Build состоит в создании официального Source Layer для REST Trade Pipeline.
|
||
|
||
После завершения Build вызывающий код должен работать только с одним специализированным компонентом:
|
||
|
||
```python
|
||
DzengiTradesDocumentSource
|
||
```
|
||
|
||
Именно он становится единственной точкой получения документа агрегированных сделок из REST API биржи.
|
||
|
||
После получения документа ответственность Source заканчивается.
|
||
|
||
Дальнейшая обработка выполняется следующими архитектурными слоями.
|
||
|
||
```text
|
||
Schema Validation
|
||
|
||
↓
|
||
|
||
REST Trade Adapter
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
Value Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
```
|
||
|
||
Таким образом каждый слой сохраняет собственную область ответственности.
|
||
|
||
---
|
||
|
||
# Рассмотренные архитектурные решения
|
||
|
||
Перед началом реализации было рассмотрено несколько вариантов построения нового компонента.
|
||
|
||
Основной вопрос заключался не в выборе способа выполнения HTTP-запроса.
|
||
|
||
Главной задачей было определить,
|
||
|
||
какая именно архитектурная ответственность должна принадлежать Source Layer.
|
||
|
||
Именно на этом этапе были рассмотрены несколько вариантов реализации.
|
||
|
||
---
|
||
|
||
## Вариант 1
|
||
|
||
Получать REST-документ напрямую через ExchangeRestClient.
|
||
|
||
Например:
|
||
|
||
```python
|
||
client = ExchangeRestClient()
|
||
|
||
document = client.get_payload(...)
|
||
```
|
||
|
||
Такой вариант первоначально выглядел самым простым.
|
||
|
||
Однако после анализа существующей архитектуры проекта он был отклонён.
|
||
|
||
Причины.
|
||
|
||
В этом случае каждый вызывающий компонент обязан самостоятельно:
|
||
|
||
- знать endpoint;
|
||
- знать параметры запроса;
|
||
- создавать HTTP-клиент;
|
||
- обрабатывать транспортные ошибки.
|
||
|
||
Таким образом логика получения документа начинает дублироваться во многих местах системы.
|
||
|
||
Кроме того,
|
||
|
||
подобный подход полностью противоречит архитектуре остальных REST Source проекта.
|
||
|
||
Следовательно данный вариант был отклонён.
|
||
|
||
---
|
||
|
||
## Вариант 2
|
||
|
||
Разместить получение документа внутри REST Trade Adapter.
|
||
|
||
Например:
|
||
|
||
```text
|
||
HTTP
|
||
|
||
↓
|
||
|
||
JSON
|
||
|
||
↓
|
||
|
||
REST Trade Adapter
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
Validation
|
||
|
||
↓
|
||
|
||
Mapper
|
||
```
|
||
|
||
На первый взгляд подобное решение кажется логичным.
|
||
|
||
Однако после повторного анализа архитектуры было подтверждено,
|
||
|
||
что Adapter отвечает исключительно за обработку уже полученного документа.
|
||
|
||
Получение документа относится к Source Layer.
|
||
|
||
Если Adapter начнёт самостоятельно выполнять HTTP-запрос,
|
||
|
||
он одновременно получит две независимые ответственности:
|
||
|
||
- получение данных;
|
||
- обработку данных.
|
||
|
||
Это нарушает принцип Single Responsibility.
|
||
|
||
Поэтому данный вариант был отклонён.
|
||
|
||
---
|
||
|
||
## Вариант 3
|
||
|
||
Создать специализированный REST Source.
|
||
|
||
Именно этот вариант оказался полностью совместимым с архитектурой проекта.
|
||
|
||
Новый компонент отвечает исключительно за:
|
||
|
||
- получение REST-документа;
|
||
- подготовку параметров запроса;
|
||
- работу с ExchangeRestClient;
|
||
- преобразование транспортных ошибок.
|
||
|
||
После получения документа управление полностью передаётся следующему архитектурному уровню.
|
||
|
||
Именно этот вариант был утверждён для реализации Build 060.8.
|
||
|
||
---
|
||
|
||
# Почему выбран отдельный REST Source
|
||
|
||
Во время проектирования обсуждался вопрос,
|
||
|
||
нужно ли вообще создавать отдельный компонент,
|
||
|
||
если ExchangeRestClient уже умеет выполнять HTTP GET.
|
||
|
||
После анализа существующего проекта ответ оказался однозначным.
|
||
|
||
ExchangeRestClient представляет собой универсальный транспортный механизм.
|
||
|
||
Он ничего не знает:
|
||
|
||
- о сделках;
|
||
- о свечах;
|
||
- о котировках;
|
||
- о торговых инструментах.
|
||
|
||
Напротив,
|
||
|
||
Source Layer знает предметную область.
|
||
|
||
Именно Source определяет:
|
||
|
||
- какой endpoint использовать;
|
||
- какие параметры допустимы;
|
||
- какие исключения относятся к данному виду данных.
|
||
|
||
Следовательно REST Source является не транспортом,
|
||
|
||
а специализированной предметной оболочкой над универсальным транспортом.
|
||
|
||
Именно такое разделение уже используется всеми существующими REST Source проекта.
|
||
|
||
---
|
||
|
||
# Почему Trades Source реализован классом
|
||
|
||
Во время реализации отдельно обсуждался вопрос,
|
||
|
||
следует ли использовать функцию,
|
||
|
||
как это было сделано в Build 060.7,
|
||
|
||
или класс.
|
||
|
||
На первый взгляд мог появиться следующий API.
|
||
|
||
```python
|
||
fetch_trades_document(...)
|
||
```
|
||
|
||
После анализа архитектуры данный вариант был отклонён.
|
||
|
||
Причина заключается в том,
|
||
|
||
что Source инкапсулирует зависимость.
|
||
|
||
Он содержит экземпляр:
|
||
|
||
```python
|
||
ExchangeRestClient
|
||
```
|
||
|
||
или получает его через Dependency Injection.
|
||
|
||
Таким образом объект Source обладает состоянием.
|
||
|
||
Даже если это состояние состоит только из одной зависимости,
|
||
|
||
оно всё равно является частью жизненного цикла компонента.
|
||
|
||
Следовательно данный компонент относится к категории Stateful.
|
||
|
||
В соответствии с утверждённым архитектурным принципом проекта:
|
||
|
||
```text
|
||
Stateful
|
||
|
||
↓
|
||
|
||
Class
|
||
```
|
||
|
||
Source должен быть реализован именно классом.
|
||
|
||
---
|
||
|
||
# Почему используется Dependency Injection
|
||
|
||
Все существующие REST Source проекта допускают передачу собственного экземпляра клиента.
|
||
|
||
Например:
|
||
|
||
```python
|
||
DzengiQuoteDocumentSource(
|
||
client=...
|
||
)
|
||
```
|
||
|
||
Новый компонент полностью сохраняет данную архитектурную модель.
|
||
|
||
Если клиент передан извне,
|
||
|
||
используется именно он.
|
||
|
||
Если клиент отсутствует,
|
||
|
||
Source самостоятельно создаёт:
|
||
|
||
```python
|
||
ExchangeRestClient()
|
||
```
|
||
|
||
Такой подход обеспечивает сразу несколько преимуществ.
|
||
|
||
Во-первых,
|
||
|
||
упрощается unit-тестирование.
|
||
|
||
Во-вторых,
|
||
|
||
Source не зависит от конкретной реализации клиента.
|
||
|
||
В-третьих,
|
||
|
||
архитектура остаётся полностью совместимой с уже существующими Source Layer.
|
||
|
||
---
|
||
|
||
# Почему используется ленивое создание клиента
|
||
|
||
Во время реализации обсуждалось,
|
||
|
||
следует ли создавать ExchangeRestClient непосредственно в конструкторе.
|
||
|
||
Например:
|
||
|
||
```python
|
||
self._client = ExchangeRestClient()
|
||
```
|
||
|
||
Подобный вариант был отклонён.
|
||
|
||
Причины.
|
||
|
||
Во-первых,
|
||
|
||
если клиент передаётся через Dependency Injection,
|
||
|
||
создание собственного экземпляра становится бессмысленным.
|
||
|
||
Во-вторых,
|
||
|
||
ленивое создание позволяет не создавать HTTP-клиент,
|
||
|
||
если Source был создан,
|
||
|
||
но фактически ещё ни разу не использовался.
|
||
|
||
Именно поэтому применяется следующая схема.
|
||
|
||
```text
|
||
client отсутствует
|
||
|
||
↓
|
||
|
||
первый вызов fetch_trades_document()
|
||
|
||
↓
|
||
|
||
создание ExchangeRestClient
|
||
```
|
||
|
||
Такой подход полностью соответствует реализации остальных REST Source проекта.
|
||
|
||
---
|
||
|
||
# Почему Source не выполняет Schema Validation
|
||
|
||
Во время проектирования отдельно обсуждался вопрос,
|
||
|
||
следует ли REST Source сразу возвращать:
|
||
|
||
```text
|
||
ValidatedRestAggTradesDocument
|
||
```
|
||
|
||
После анализа архитектуры данный вариант был отклонён.
|
||
|
||
Причины.
|
||
|
||
Schema Validation уже является самостоятельным архитектурным уровнем.
|
||
|
||
В системе существуют независимые функции:
|
||
|
||
```python
|
||
validate_quote_schema()
|
||
|
||
validate_candle_schema()
|
||
|
||
validate_rest_agg_trades_schema()
|
||
```
|
||
|
||
Если Source начнёт самостоятельно выполнять проверку структуры,
|
||
|
||
он одновременно получит две независимые ответственности:
|
||
|
||
- получение документа;
|
||
- проверку структуры документа.
|
||
|
||
Это нарушает уже сформированную архитектуру Acquisition Layer.
|
||
|
||
Следовательно REST Source возвращает исключительно необработанный REST-документ.
|
||
|
||
Следующий слой самостоятельно выполняет Schema Validation.
|
||
|
||
Полный Pipeline после завершения Build принимает следующий вид.
|
||
|
||
```text
|
||
Exchange
|
||
|
||
↓
|
||
|
||
ExchangeRestClient
|
||
|
||
↓
|
||
|
||
DzengiTradesDocumentSource
|
||
|
||
↓
|
||
|
||
Raw REST Document
|
||
|
||
↓
|
||
|
||
Schema Validation
|
||
|
||
↓
|
||
|
||
ValidatedRestAggTradesDocument
|
||
```
|
||
|
||
Именно такое разделение полностью соответствует архитектурным принципам проекта.
|
||
|
||
# Почему Source не выполняет Parser
|
||
|
||
Во время проектирования рассматривался вариант,
|
||
|
||
при котором REST Source сразу возвращал бы транспортные модели.
|
||
|
||
Например:
|
||
|
||
```text
|
||
REST JSON
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
tuple[DzengiRestAggTrade]
|
||
```
|
||
|
||
На первый взгляд подобный подход выглядит привлекательным,
|
||
|
||
поскольку вызывающий код получает уже разобранные объекты.
|
||
|
||
Однако после анализа архитектуры данный вариант был отклонён.
|
||
|
||
Причины.
|
||
|
||
Parser представляет собой самостоятельный уровень Acquisition Pipeline.
|
||
|
||
Его задача — исключительно преобразование уже проверенного документа в транспортные модели.
|
||
|
||
Если Parser переносится внутрь Source,
|
||
|
||
Source начинает одновременно выполнять две независимые задачи:
|
||
|
||
- получение документа;
|
||
- преобразование документа.
|
||
|
||
Это нарушает принцип разделения ответственности.
|
||
|
||
Кроме того,
|
||
|
||
остальные Source проекта не содержат Parser.
|
||
|
||
Следовательно новый компонент также не должен выполнять данную функцию.
|
||
|
||
---
|
||
|
||
# Почему Source не выполняет Value Validation
|
||
|
||
После отказа от Parser обсуждался ещё один вариант.
|
||
|
||
Можно было бы возвращать транспортные модели,
|
||
|
||
которые уже прошли проверку корректности значений.
|
||
|
||
Например:
|
||
|
||
```text
|
||
HTTP
|
||
|
||
↓
|
||
|
||
REST Source
|
||
|
||
↓
|
||
|
||
Parser
|
||
|
||
↓
|
||
|
||
Value Validation
|
||
|
||
↓
|
||
|
||
tuple[DzengiRestAggTrade]
|
||
```
|
||
|
||
Данный вариант также был отклонён.
|
||
|
||
Причины.
|
||
|
||
Value Validation представляет собой отдельный архитектурный слой.
|
||
|
||
Его задача — проверка содержимого транспортной модели.
|
||
|
||
Source не должен знать,
|
||
|
||
какие именно ограничения существуют для:
|
||
|
||
- цены;
|
||
- количества;
|
||
- времени;
|
||
- идентификаторов сделок.
|
||
|
||
Все подобные проверки относятся исключительно к Validation Layer.
|
||
|
||
Следовательно Source обязан оставаться полностью независимым от предметной модели.
|
||
|
||
---
|
||
|
||
# Почему Source не выполняет Mapper
|
||
|
||
Во время проектирования обсуждалась возможность,
|
||
|
||
при которой REST Source сразу возвращал бы:
|
||
|
||
```python
|
||
tuple[Trade]
|
||
```
|
||
|
||
Подобная схема выглядела следующим образом.
|
||
|
||
```text
|
||
Exchange
|
||
|
||
↓
|
||
|
||
REST Source
|
||
|
||
↓
|
||
|
||
Canonical Trade
|
||
```
|
||
|
||
После анализа архитектуры данный вариант был отклонён.
|
||
|
||
Причины очевидны.
|
||
|
||
Source отвечает исключительно за получение транспортного документа.
|
||
|
||
Создание канонической модели относится к Mapper Layer.
|
||
|
||
Если Source начинает создавать экземпляры:
|
||
|
||
```text
|
||
Trade
|
||
```
|
||
|
||
он становится зависимым:
|
||
|
||
- от предметной модели;
|
||
- от правил отображения;
|
||
- от внутренней структуры Canonical Layer.
|
||
|
||
Подобная зависимость нарушает архитектурную изоляцию между транспортным и предметным уровнями.
|
||
|
||
Следовательно создание Canonical Trade остаётся исключительной ответственностью Mapper.
|
||
|
||
---
|
||
|
||
# Почему введён отдельный TradeTransportError
|
||
|
||
До начала Build система уже содержала специализированные транспортные исключения.
|
||
|
||
Например:
|
||
|
||
```text
|
||
InstrumentReferenceTransportError
|
||
|
||
QuoteTransportError
|
||
|
||
CandleTransportError
|
||
```
|
||
|
||
Однако для REST Trade использовался общий тип исключений,
|
||
|
||
либо транспортные ошибки пробрасывались без предметной семантики.
|
||
|
||
Во время проектирования обсуждалось,
|
||
|
||
следует ли использовать существующий общий тип.
|
||
|
||
После анализа было принято решение отказаться от этого подхода.
|
||
|
||
Причины.
|
||
|
||
Каждый Source проекта использует собственное специализированное исключение.
|
||
|
||
Это позволяет вызывающему коду:
|
||
|
||
- сразу определить источник ошибки;
|
||
- принимать различные решения для разных видов данных;
|
||
- сохранять единообразие архитектуры.
|
||
|
||
Поэтому был введён новый тип:
|
||
|
||
```python
|
||
TradeTransportError
|
||
```
|
||
|
||
который становится официальным транспортным исключением REST Trade Pipeline.
|
||
|
||
---
|
||
|
||
# Новый публичный API
|
||
|
||
После завершения Build 060.8 в системе появляется новый публичный компонент.
|
||
|
||
```python
|
||
DzengiTradesDocumentSource
|
||
```
|
||
|
||
Он предоставляет единственную публичную операцию.
|
||
|
||
```python
|
||
fetch_trades_document(...)
|
||
```
|
||
|
||
Назначение метода.
|
||
|
||
- принимает параметры REST-запроса;
|
||
- формирует параметры HTTP GET;
|
||
- обращается к ExchangeRestClient;
|
||
- возвращает необработанный REST-документ;
|
||
- преобразует транспортные исключения в TradeTransportError.
|
||
|
||
На этом ответственность Source полностью заканчивается.
|
||
|
||
---
|
||
|
||
# Семантика REST Trades Source
|
||
|
||
После завершения Build новый компонент становится официальной точкой получения агрегированных сделок из REST API.
|
||
|
||
Полная последовательность работы выглядит следующим образом.
|
||
|
||
```text
|
||
ExchangeRestClient
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
HTTP GET
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST JSON
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
DzengiTradesDocumentSource
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Raw Document
|
||
```
|
||
|
||
Source намеренно не анализирует содержимое ответа.
|
||
|
||
Он не проверяет:
|
||
|
||
- наличие обязательных полей;
|
||
- корректность структуры;
|
||
- корректность значений;
|
||
- сортировку;
|
||
- наличие дубликатов;
|
||
- непрерывность последовательности сделок.
|
||
|
||
Все перечисленные задачи относятся к следующим архитектурным слоям.
|
||
|
||
Таким образом Source остаётся максимально простым и полностью соответствует принципу Single Responsibility.
|
||
|
||
---
|
||
|
||
# Что намеренно не делает Source
|
||
|
||
Build 060.8 специально не расширяет область ответственности нового компонента.
|
||
|
||
REST Source намеренно:
|
||
|
||
не выполняет Schema Validation;
|
||
|
||
не выполняет Parser;
|
||
|
||
не выполняет Value Validation;
|
||
|
||
не выполняет Mapper;
|
||
|
||
не создаёт Canonical Trade;
|
||
|
||
не агрегирует сделки;
|
||
|
||
не сортирует данные;
|
||
|
||
не удаляет дубликаты;
|
||
|
||
не вычисляет статистику;
|
||
|
||
не создаёт свечи;
|
||
|
||
не объединяет REST и WebSocket сделки;
|
||
|
||
не взаимодействует с Runtime;
|
||
|
||
не взаимодействует с Feed;
|
||
|
||
не взаимодействует с Registry;
|
||
|
||
не сохраняет историю;
|
||
|
||
не выполняет повторные HTTP-запросы;
|
||
|
||
не реализует polling.
|
||
|
||
Все перечисленные задачи относятся к другим Build и намеренно исключены из области ответственности Source.
|
||
|
||
# Изменённые файлы
|
||
|
||
В рамках Build 060.8 были изменены два файла проекта.
|
||
|
||
Добавлен новый REST Source.
|
||
|
||
```text
|
||
app/src/market_data/acquisition/adapters/dzengi/rest.py
|
||
```
|
||
|
||
В существующий модуль исключений добавлен новый специализированный тип транспортной ошибки.
|
||
|
||
```text
|
||
app/src/market_data/acquisition/exceptions.py
|
||
```
|
||
|
||
Кроме того, был добавлен новый набор unit-тестов.
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/adapters/dzengi/test_trade_rest.py
|
||
```
|
||
|
||
Никакие другие компоненты проекта не изменялись.
|
||
|
||
Build полностью соответствует принципу **Local Additive Change**.
|
||
|
||
---
|
||
|
||
# Изменения в rest.py
|
||
|
||
В существующий модуль REST Source добавлен новый компонент.
|
||
|
||
```python
|
||
DzengiTradesDocumentSource
|
||
```
|
||
|
||
Он полностью повторяет архитектурный стиль уже существующих Source проекта.
|
||
|
||
Новый компонент содержит:
|
||
|
||
- поддержку Dependency Injection;
|
||
- ленивое создание ExchangeRestClient;
|
||
- специализированный REST endpoint;
|
||
- подготовку параметров запроса;
|
||
- преобразование транспортных исключений.
|
||
|
||
Также в модуле определён новый endpoint.
|
||
|
||
```text
|
||
/api/v1/aggTrades
|
||
```
|
||
|
||
Именно данный endpoint используется для получения исторических агрегированных сделок.
|
||
|
||
Никакие существующие REST Source при этом не изменялись.
|
||
|
||
---
|
||
|
||
# Изменения в exceptions.py
|
||
|
||
В рамках Build введён новый специализированный тип исключения.
|
||
|
||
```python
|
||
TradeTransportError
|
||
```
|
||
|
||
Он наследуется от существующего базового класса транспортных ошибок Acquisition Layer.
|
||
|
||
Назначение нового исключения состоит исключительно в семантическом разделении транспортных ошибок различных источников данных.
|
||
|
||
После завершения Build система содержит отдельные транспортные исключения для:
|
||
|
||
```text
|
||
Instrument
|
||
|
||
Quote
|
||
|
||
Candle
|
||
|
||
Trade
|
||
```
|
||
|
||
Тем самым достигается единообразие всей подсистемы получения рыночных данных.
|
||
|
||
---
|
||
|
||
# Изменения в unit-тестах
|
||
|
||
Для нового REST Source создан самостоятельный файл тестов.
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/adapters/dzengi/test_trade_rest.py
|
||
```
|
||
|
||
Это соответствует принятому соглашению проекта,
|
||
|
||
согласно которому каждый самостоятельный компонент Acquisition Layer имеет собственный независимый набор unit-тестов.
|
||
|
||
Существующие тесты:
|
||
|
||
- Instrument REST Source;
|
||
- Quote REST Source;
|
||
- Candle REST Source;
|
||
|
||
не изменялись.
|
||
|
||
Build полностью additive.
|
||
|
||
---
|
||
|
||
# Проверяемые сценарии
|
||
|
||
Новый набор тестов проверяет исключительно ответственность REST Source.
|
||
|
||
Подтверждаются следующие сценарии.
|
||
|
||
- успешное получение документа;
|
||
- корректное использование endpoint;
|
||
- корректная передача обязательного параметра symbol;
|
||
- корректная передача startTime;
|
||
- корректная передача endTime;
|
||
- корректная передача limit;
|
||
- отсутствие параметров со значением None;
|
||
- преобразование всех параметров в строковый формат;
|
||
- отсутствие лишних HTTP-заголовков;
|
||
- корректное использование переданного ExchangeRestClient;
|
||
- ленивое создание собственного клиента;
|
||
- преобразование транспортных ошибок в TradeTransportError;
|
||
- возврат необработанного REST-документа без изменений.
|
||
|
||
Тем самым подтверждается,
|
||
|
||
что новый Source полностью соответствует архитектурной ответственности Source Layer.
|
||
|
||
---
|
||
|
||
# Что намеренно не изменялось
|
||
|
||
Build 060.8 специально не изменяет существующие компоненты Acquisition Pipeline.
|
||
|
||
Без изменений остаются:
|
||
|
||
```text
|
||
app/src/market_data/acquisition/adapters/dzengi/parser.py
|
||
|
||
app/src/market_data/acquisition/adapters/dzengi/mapper.py
|
||
|
||
app/src/market_data/acquisition/adapters/dzengi/rest_trade_adapter.py
|
||
|
||
app/src/market_data/acquisition/validation/schema.py
|
||
|
||
app/src/market_data/acquisition/validation/values.py
|
||
|
||
app/src/market_data/acquisition/models/trade.py
|
||
|
||
app/src/market_data/acquisition/runtime/
|
||
|
||
app/src/market_data/acquisition/feeds/
|
||
|
||
app/src/market_data/acquisition/service.py
|
||
|
||
app/src/market_data/acquisition/registry.py
|
||
```
|
||
|
||
Также Build намеренно не включает:
|
||
|
||
- Schema Validation;
|
||
- Parser;
|
||
- Value Validation;
|
||
- Mapper;
|
||
- REST polling;
|
||
- Trades Feed;
|
||
- Runtime Integration;
|
||
- объединение REST и WebSocket сделок;
|
||
- дедупликацию;
|
||
- хранение истории;
|
||
- аналитическую обработку.
|
||
|
||
Все перечисленные задачи реализуются отдельными Build согласно утверждённой дорожной карте.
|
||
|
||
Таким образом Build 060.8 остаётся полностью локальным и не выходит за пределы собственной архитектурной ответственности.
|
||
|
||
---
|
||
|
||
# Проверка компиляции
|
||
|
||
После завершения реализации была выполнена проверка компиляции проекта.
|
||
|
||
Команда выполнялась из каталога:
|
||
|
||
```text
|
||
~/vsprojects/dzentra_bot/app
|
||
```
|
||
|
||
При активированном виртуальном окружении:
|
||
|
||
```bash
|
||
source .venv/bin/activate
|
||
```
|
||
|
||
Выполнена команда:
|
||
|
||
```bash
|
||
python -m compileall src
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
успешно
|
||
```
|
||
|
||
Все модули проекта успешно скомпилированы.
|
||
|
||
Ошибок синтаксиса не обнаружено.
|
||
|
||
Build не нарушил корректность структуры проекта.
|
||
|
||
---
|
||
|
||
# Проверка локальных unit-тестов
|
||
|
||
После завершения реализации нового REST Source были выполнены специализированные unit-тесты.
|
||
|
||
Команда:
|
||
|
||
```bash
|
||
python -m pytest \
|
||
tests/unit/market_data/acquisition/adapters/dzengi/test_trade_rest.py \
|
||
-v
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
13 passed in 0.04s
|
||
```
|
||
|
||
Проверки подтвердили:
|
||
|
||
- корректную работу REST Source;
|
||
- корректное формирование HTTP-запроса;
|
||
- корректную обработку параметров;
|
||
- корректную работу Dependency Injection;
|
||
- корректное создание клиента;
|
||
- корректное преобразование транспортных ошибок.
|
||
|
||
Ни одна существующая проверка проекта не была нарушена.
|
||
|
||
---
|
||
|
||
# Проверка подсистемы REST Adapter
|
||
|
||
После завершения Build был выполнен полный набор тестов всех адаптеров Dzengi.
|
||
|
||
Команда:
|
||
|
||
```bash
|
||
python -m pytest \
|
||
tests/unit/market_data/acquisition/adapters/dzengi/
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
243 passed in 0.10s
|
||
```
|
||
|
||
Регрессий не обнаружено.
|
||
|
||
Все существующие REST- и WebSocket-компоненты продолжают работать без изменений.
|
||
|
||
---
|
||
|
||
# Проверка подсистемы Acquisition
|
||
|
||
После завершения Build была выполнена проверка всей подсистемы получения рыночных данных.
|
||
|
||
Команда:
|
||
|
||
```bash
|
||
python -m pytest \
|
||
tests/unit/market_data/acquisition/
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
788 passed in 0.27s
|
||
```
|
||
|
||
Проверка подтвердила,
|
||
|
||
что новый REST Source полностью совместим с существующей архитектурой и не вызвал регрессий в других компонентах Acquisition Layer.
|
||
|
||
# Проверка форматирования
|
||
|
||
После завершения работы выполнена команда:
|
||
|
||
```bash
|
||
git diff --check
|
||
```
|
||
|
||
Вывод отсутствует.
|
||
|
||
Это подтверждает отсутствие:
|
||
|
||
- trailing whitespace;
|
||
- лишних пробелов;
|
||
- нарушений форматирования;
|
||
- ошибок оформления diff.
|
||
|
||
Build соответствует принятым требованиям оформления исходного кода.
|
||
|
||
---
|
||
|
||
# Контроль размещения новой функциональности
|
||
|
||
После завершения Build вся новая функциональность сосредоточена только в предназначенных для неё компонентах.
|
||
|
||
REST Source расположен в существующем модуле:
|
||
|
||
```text
|
||
src/market_data/acquisition/adapters/dzengi/rest.py
|
||
```
|
||
|
||
Новый тип транспортного исключения расположен исключительно в:
|
||
|
||
```text
|
||
src/market_data/acquisition/exceptions.py
|
||
```
|
||
|
||
Unit-тесты расположены исключительно в:
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/adapters/dzengi/test_trade_rest.py
|
||
```
|
||
|
||
Другие подсистемы проекта Build не затрагивает.
|
||
|
||
Архитектурная изоляция полностью сохранена.
|
||
|
||
---
|
||
|
||
# Состояние Git
|
||
|
||
После завершения Build выполнена команда:
|
||
|
||
```bash
|
||
git status
|
||
```
|
||
|
||
Для Build 060.8 зафиксированы изменения:
|
||
|
||
```text
|
||
modified:
|
||
|
||
app/src/market_data/acquisition/adapters/dzengi/rest.py
|
||
|
||
app/src/market_data/acquisition/exceptions.py
|
||
|
||
added:
|
||
|
||
app/tests/unit/market_data/acquisition/adapters/dzengi/test_trade_rest.py
|
||
|
||
untracked:
|
||
|
||
docs/migrations/build_060_8.md
|
||
```
|
||
|
||
Ветка разработки:
|
||
|
||
```text
|
||
main
|
||
```
|
||
|
||
опережает `origin/main`.
|
||
|
||
Данное состояние соответствует текущему процессу разработки и не связано с архитектурой Build.
|
||
|
||
---
|
||
|
||
# Фактический diff
|
||
|
||
В рамках Build реализованы следующие изменения.
|
||
|
||
В модуле REST Source добавлен новый компонент:
|
||
|
||
```python
|
||
DzengiTradesDocumentSource
|
||
```
|
||
|
||
Добавлен новый REST endpoint:
|
||
|
||
```text
|
||
/api/v1/aggTrades
|
||
```
|
||
|
||
Добавлен новый публичный метод:
|
||
|
||
```python
|
||
fetch_trades_document()
|
||
```
|
||
|
||
В модуле исключений добавлен новый тип:
|
||
|
||
```python
|
||
TradeTransportError
|
||
```
|
||
|
||
Также добавлен отдельный набор unit-тестов:
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/adapters/dzengi/test_trade_rest.py
|
||
```
|
||
|
||
Все изменения являются полностью additive.
|
||
|
||
Никакое существующее поведение системы не изменялось.
|
||
|
||
---
|
||
|
||
# Архитектурный результат
|
||
|
||
После завершения Build 060.8 REST Trade Pipeline впервые получает полноценный Source Layer.
|
||
|
||
Полная архитектура теперь выглядит следующим образом.
|
||
|
||
```text
|
||
Exchange
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
ExchangeRestClient
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
DzengiTradesDocumentSource
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Raw REST Document
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Schema Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
ValidatedRestAggTradesDocument
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Trade Adapter
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Parser
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Value Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Mapper
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
tuple[Trade]
|
||
```
|
||
|
||
Каждый слой обладает собственной зоной ответственности.
|
||
|
||
Ни один слой не выполняет задачи соседнего.
|
||
|
||
Source отвечает исключительно за получение документа.
|
||
|
||
Schema Validation отвечает исключительно за проверку структуры.
|
||
|
||
Adapter отвечает исключительно за композицию существующих этапов обработки.
|
||
|
||
Таким образом окончательно разделены три независимые архитектурные области:
|
||
|
||
- получение данных;
|
||
- обработка данных;
|
||
- построение канонической модели.
|
||
|
||
Именно такое разделение является одной из базовых архитектурных целей подсистемы Market Data Acquisition.
|
||
|
||
---
|
||
|
||
# Влияние на последующие Build
|
||
|
||
Build 060.8 завершает формирование инфраструктуры получения исторических сделок через REST API.
|
||
|
||
После его окончания все последующие компоненты могут использовать единый специализированный Source вместо прямой работы с ExchangeRestClient.
|
||
|
||
Это означает,
|
||
|
||
что любые изменения:
|
||
|
||
- REST endpoint;
|
||
- параметров HTTP-запроса;
|
||
- механизма авторизации;
|
||
- реализации HTTP-клиента;
|
||
- политики обработки транспортных ошибок;
|
||
|
||
будут локализованы исключительно внутри Source Layer.
|
||
|
||
Все остальные компоненты Acquisition останутся неизменными.
|
||
|
||
Таким образом Build создаёт стабильную архитектурную границу между транспортным уровнем и логикой обработки рыночных данных.
|
||
|
||
---
|
||
|
||
# Критерии завершения
|
||
|
||
Build 060.8 считается полностью завершённым, поскольку:
|
||
|
||
- проведён повторный архитектурный аудит существующего REST Layer;
|
||
- реализован специализированный REST Trades Source;
|
||
- введён новый публичный API получения документа;
|
||
- реализована поддержка Dependency Injection;
|
||
- реализовано ленивое создание ExchangeRestClient;
|
||
- реализовано формирование параметров REST-запроса;
|
||
- реализовано преобразование транспортных ошибок в TradeTransportError;
|
||
- сохранено разделение ответственности между Source Layer и Pipeline обработки;
|
||
- Source не выполняет Schema Validation;
|
||
- Source не выполняет Parser;
|
||
- Source не выполняет Value Validation;
|
||
- Source не выполняет Mapper;
|
||
- Source не создаёт Canonical Trade;
|
||
- compile-проверка успешно пройдена;
|
||
- локальные unit-тесты успешно пройдены;
|
||
- тесты всех адаптеров успешно пройдены;
|
||
- тесты всей подсистемы Acquisition успешно пройдены;
|
||
- проверка форматирования успешно пройдена;
|
||
- scope Build не расширен.
|
||
|
||
---
|
||
|
||
# Архитектурные инварианты
|
||
|
||
После завершения Build 060.8 следующие свойства REST Trade Source считаются архитектурным контрактом проекта.
|
||
|
||
Изменение любого из перечисленных инвариантов требует отдельного архитектурного решения.
|
||
|
||
---
|
||
|
||
## Инвариант 1
|
||
|
||
REST Source отвечает исключительно за получение документа.
|
||
|
||
Никакая обработка содержимого документа внутри Source не допускается.
|
||
|
||
---
|
||
|
||
## Инвариант 2
|
||
|
||
REST Source всегда возвращает необработанный транспортный документ.
|
||
|
||
Schema Validation остаётся отдельным архитектурным слоем.
|
||
|
||
---
|
||
|
||
## Инвариант 3
|
||
|
||
REST Source не зависит от Parser, Validation и Mapper.
|
||
|
||
Любая обработка транспортной модели выполняется исключительно после получения документа.
|
||
|
||
---
|
||
|
||
## Инвариант 4
|
||
|
||
Все транспортные ошибки получения исторических сделок преобразуются в:
|
||
|
||
```text
|
||
TradeTransportError
|
||
```
|
||
|
||
Использование общего типа транспортных исключений не допускается.
|
||
|
||
---
|
||
|
||
## Инвариант 5
|
||
|
||
REST Source реализуется классом.
|
||
|
||
Причина —
|
||
|
||
компонент инкапсулирует зависимость ExchangeRestClient и обладает собственным жизненным циклом.
|
||
|
||
---
|
||
|
||
## Инвариант 6
|
||
|
||
ExchangeRestClient создаётся лениво.
|
||
|
||
Если клиент передан через Dependency Injection,
|
||
|
||
создание собственного экземпляра не выполняется.
|
||
|
||
---
|
||
|
||
## Инвариант 7
|
||
|
||
REST Source является единственной официальной точкой получения исторических агрегированных сделок.
|
||
|
||
Все последующие компоненты должны использовать именно его.
|
||
|
||
Прямое обращение к ExchangeRestClient за пределами Source Layer не допускается.
|
||
|
||
---
|
||
|
||
## Инвариант 8
|
||
|
||
Source Layer не зависит от предметной модели.
|
||
|
||
Он ничего не знает о существовании:
|
||
|
||
```text
|
||
Trade
|
||
```
|
||
|
||
или других канонических объектов системы.
|
||
|
||
Его контракт ограничивается исключительно транспортным документом.
|
||
|
||
---
|
||
|
||
# Итог
|
||
|
||
**Build 060.8 завершён успешно.**
|
||
|
||
Текущее состояние REST Trade Pipeline:
|
||
|
||
```text
|
||
Transport Source — реализован
|
||
|
||
Transport Exception — реализована
|
||
|
||
Schema Validation — реализована
|
||
|
||
REST Trade Adapter — реализован
|
||
|
||
Parser — реализован
|
||
|
||
Value Validation — реализована
|
||
|
||
Mapper — реализован
|
||
|
||
Canonical Trade — используется
|
||
|
||
Compile check — успешно
|
||
|
||
Target tests — 13 passed
|
||
|
||
Dzengi adapters — 243 passed
|
||
|
||
Acquisition subsystem — 788 passed
|
||
|
||
Whitespace check — успешно
|
||
|
||
Production Integration — намеренно не выполнялась
|
||
```
|
||
|
||
После завершения Build 060.8 подсистема получения исторических сделок получила полноценный специализированный Source Layer, полностью соответствующий архитектуре остальных компонентов Market Data Acquisition.
|
||
|
||
Получение REST-документа теперь полностью изолировано от его последующей обработки, что завершает формирование транспортного уровня REST Trade Pipeline.
|
||
|
||
---
|
||
|
||
# Следующий этап
|
||
|
||
Следующим этапом развития серии Build 060 становится:
|
||
|
||
```text
|
||
Build 060.9 — WebSocket Trade Transport Model
|
||
```
|
||
|
||
На данном этапе будет реализована транспортная модель сообщений WebSocket, описывающая формат агрегированных сделок, поступающих от биржи в режиме реального времени.
|
||
|
||
Как и в случае с REST Pipeline, данный Build ограничивается исключительно транспортным уровнем и не включает:
|
||
|
||
- Schema Validation;
|
||
- Parser;
|
||
- Value Validation;
|
||
- Mapper;
|
||
- Adapter;
|
||
- маршрутизацию WebSocket-сообщений;
|
||
- механизм подписки;
|
||
- Trades Feed;
|
||
- Runtime Integration;
|
||
- объединение REST и WebSocket данных.
|
||
|
||
Все перечисленные задачи относятся к последующим Build и будут реализованы согласно утверждённой дорожной карте проекта.
|
||
|
||
После завершения Build 060.9 будет сформирована транспортная основа WebSocket Trade Pipeline, полностью симметричная ранее реализованной REST-модели.
|
||
|
||
Полная последовательность формирования WebSocket Pipeline будет выглядеть следующим образом.
|
||
|
||
```text
|
||
WebSocket Trade Transport Model
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
WebSocket Trade Schema Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
WebSocket Trade Parser
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
WebSocket Trade Value Validation
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
WebSocket Trade Mapper
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
WebSocket Trade Adapter
|
||
```
|
||
|
||
После завершения этих этапов станет возможным переход к построению общей инфраструктуры обработки сделок.
|
||
|
||
Следующие Build последовательно реализуют:
|
||
|
||
```text
|
||
Unified WebSocket Routing
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Trade Subscription Layer
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Trades Feed Core
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Trade Ordering & Deduplication
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
REST Backfill & Gap Recovery
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Trades Feed Registry
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Acquisition Protocol Integration
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Acquisition Service Integration
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Runtime Integration
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Reconnect & Recovery
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Integration & Regression
|
||
|
||
│
|
||
|
||
▼
|
||
|
||
Final Documentation
|
||
```
|
||
|
||
Таким образом последовательность реализации остаётся полностью согласованной с утверждённой архитектурной дорожной картой проекта.
|
||
|
||
Сначала полностью формируется REST Pipeline, затем — полностью симметричный WebSocket Pipeline, после чего оба источника объединяются в единый **Trades Feed (Time & Sales)** без необходимости пересмотра или переработки ранее реализованных компонентов. |