Files
dzentra_bot/docs/migrations/build_060_8.md

1902 lines
53 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)** без необходимости пересмотра или переработки ранее реализованных компонентов.