Files
dzentra_bot/docs/migrations/build_046.md

515 lines
13 KiB
Markdown
Raw 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 046 — Интеграция канонического Candles Feed в сервисный слой Market Data Acquisition
## Статус
**Завершён**
---
## Цель Build
Интегрировать канонический `Candles Feed` в существующий сервисный слой подсистемы:
```text
src/market_data/acquisition/
```
без переключения legacy-потребителей `ExchangeService.get_klines()` и без изменения существующего поведения работающего торгового бота.
Build продолжает поэтапную миграцию получения OHLCV-данных из legacy-подсистемы:
```text
src/integrations/exchange/
```
в каноническую подсистему:
```text
src/market_data/acquisition/
```
---
## Исходное состояние
До начала Build 046 канонический OHLCV-контур уже включал:
```text
Candle
DzengiCandlesDocumentSource
schema validation
parser
value validation
mapper
DzengiCandlesDocumentHandler
CandlesFeed
CandlesFeedRegistry
```
Однако сервисный слой `Market Data Acquisition` ещё не предоставлял application-level сервис для получения канонического набора свечей.
В файле:
```text
src/market_data/acquisition/service.py
```
существовали отдельные сервисы для:
```text
InstrumentAcquisitionService
QuoteAcquisitionService
```
При этом `CandlesAcquisitionService` отсутствовал.
---
## Принятое архитектурное решение
В рамках Build 046 не создавался новый объединяющий класс `MarketDataAcquisitionService`.
Существующая архитектура сервисного слоя использует отдельный application-level сервис для каждого типа рыночных данных:
```text
InstrumentAcquisitionService
QuoteAcquisitionService
```
Поэтому для OHLCV-данных добавлен отдельный:
```text
CandlesAcquisitionService
```
Это сохраняет существующий архитектурный паттерн и не требует изменения уже работающих контрактов Instrument Reference Data и Quotes Feed.
---
## Границы Build
В рамках Build 046 изменены только:
```text
src/market_data/acquisition/service.py
tests/unit/market_data/acquisition/test_service.py
docs/migrations/build_046.md
```
Не изменялись:
```text
src/market_data/acquisition/registry.py
src/market_data/acquisition/protocol.py
src/market_data/acquisition/feeds/candles_feed.py
src/market_data/acquisition/handlers/candles_handler.py
src/market_data/acquisition/adapters/dzengi/
src/integrations/exchange/service.py
src/integrations/exchange/models.py
src/trading/market_analysis/
```
---
## Реализованные изменения
### 1. Добавлен `CandlesAcquisitionService`
В файл:
```text
src/market_data/acquisition/service.py
```
добавлен класс:
```python
class CandlesAcquisitionService:
def __init__(
self,
*,
registry: CandlesFeedRegistry,
) -> None:
self._registry = registry
def load_candles(
self,
source_name: str,
symbol: str,
*,
interval: str,
limit: int,
price_type: str,
) -> tuple[Candle, ...]:
feed = self._registry.get(source_name)
return feed.load_candles(
symbol,
interval=interval,
limit=limit,
price_type=price_type,
)
```
---
### 2. Добавлены зависимости сервисного слоя
В `service.py` добавлены импорты:
```python
from src.market_data.acquisition.models.candle import Candle
```
и:
```python
from src.market_data.acquisition.registry import CandlesFeedRegistry
```
---
### 3. Зафиксирована ответственность `CandlesAcquisitionService`
`CandlesAcquisitionService` выполняет только две операции:
1. получает зарегистрированный `Candles Feed` по имени источника;
2. делегирует ему загрузку свечей.
Каноническая цепочка:
```text
source_name
CandlesFeedRegistry.get()
CandlesFeedProtocol
load_candles()
tuple[Candle, ...]
```
Сервис не выполняет:
```text
нормализацию source_name
нормализацию symbol
валидацию interval
ограничение limit
нормализацию price_type
schema validation
parsing
value validation
mapping
REST-запросы
retry
кэширование
сортировку свечей
копирование результата
оборачивание исключений
```
Это сохраняет строгие границы ответственности между слоями.
---
## Контракт `CandlesAcquisitionService`
Метод:
```python
load_candles(
source_name: str,
symbol: str,
*,
interval: str,
limit: int,
price_type: str,
) -> tuple[Candle, ...]
```
гарантирует следующую последовательность:
```text
1. Получить Feed:
registry.get(source_name)
2. Передать Feed исходные параметры:
symbol
interval
limit
price_type
3. Вернуть результат Feed без преобразований.
```
Сервис не изменяет входные параметры.
Сервис не изменяет порядок свечей.
Сервис сохраняет identity возвращённого immutable `tuple`.
Сервис не выполняет автоматический retry при ошибке.
Исключения нижележащих слоёв пробрасываются без оборачивания.
---
## Добавленные тесты
В:
```text
tests/unit/market_data/acquisition/test_service.py
```
добавлено тестовое покрытие `CandlesAcquisitionService`.
Проверяются следующие свойства:
1. сервис возвращает результат зарегистрированного Feed;
2. сохраняется identity возвращённого `tuple`;
3. `source_name` передаётся в registry без изменений;
4. registry вызывается ровно один раз;
5. `symbol` передаётся в Feed без изменений;
6. `interval` передаётся без изменений;
7. `limit` передаётся без изменений;
8. `price_type` передаётся без изменений;
9. Feed вызывается ровно один раз;
10. пустой `tuple` возвращается без изменения;
11. порядок свечей сохраняется;
12. `CandleFeedRegistryError` пробрасывается без оборачивания;
13. после ошибки registry Feed не вызывается;
14. ошибки Feed пробрасываются без оборачивания;
15. после ошибки Feed автоматический retry не выполняется.
Для проверки ошибок Feed используются:
```text
CandleTransportError
CandleValueError
CandleMappingError
```
---
## Результаты проверок
### Компиляция
Выполнена команда:
```bash
python -m compileall \
src/market_data/acquisition/service.py \
tests/unit/market_data/acquisition/test_service.py
```
Результат:
```text
Compiling 'src/market_data/acquisition/service.py'...
Compiling 'tests/unit/market_data/acquisition/test_service.py'...
```
Ошибок компиляции нет.
---
### Targeted tests
Выполнена команда:
```bash
python -m pytest -q \
tests/unit/market_data/acquisition/test_service.py
```
Результат:
```text
40 passed in 0.03s
```
---
### Полный test suite
Выполнена команда:
```bash
python -m pytest -q
```
Результат:
```text
724 passed in 2.43s
```
Регрессий не обнаружено.
---
### Проверка Git diff
Выполнена команда:
```bash
git diff --check
```
Результат:
```text
пустой вывод
```
Whitespace errors отсутствуют.
---
## Архитектурный результат
После Build 046 канонический OHLCV-контур имеет следующую структуру:
```text
External Dzengi API
DzengiCandlesDocumentSource
raw transport document
validate_candles_schema()
parse_candles()
validate_candles_values()
map_candles()
DzengiCandlesDocumentHandler
CandlesFeed
CandlesFeedRegistry
CandlesAcquisitionService
tuple[Candle, ...]
```
Таким образом, канонический Candles Feed теперь имеет полный application-level путь от внешнего REST API до сервисного слоя `Market Data Acquisition`.
---
## Состояние legacy-контура
В рамках Build 046 legacy-метод:
```text
ExchangeService.get_klines()
```
не изменялся и не удалялся.
Legacy-потребители:
```text
src/trading/market_analysis/service.py
src/trading/market_analysis/htf.py
```
продолжают использовать существующий API.
Это соответствует основному правилу миграции Dzentra:
> Сначала строится и проверяется новый канонический путь, затем потребители переключаются на него поэтапно, и только после подтверждения полной совместимости удаляется legacy-код.
---
## Что намеренно не выполнялось
Build 046 не включает:
```text
переключение ExchangeService.get_klines()
переключение trading/market_analysis
удаление legacy KlineBatch
удаление legacy Kline
удаление legacy parser
удаление legacy endpoint /api/v1/klines
регистрацию production Candles Feed на composition root
изменение runtime
изменение scheduler
кэширование свечей
хранение свечей
проверку последовательности свечей
```
Эти задачи должны выполняться отдельными Build с собственными границами и проверками.
---
## Критерии завершения
Build 046 считается завершённым, поскольку:
- `CandlesAcquisitionService` реализован;
- сервис использует `CandlesFeedRegistry`;
- сервис возвращает канонический `tuple[Candle, ...]`;
- входные параметры передаются без изменений;
- результат не копируется и не сортируется;
- ошибки не оборачиваются;
- retry отсутствует;
- существующие Instrument и Quote сервисы не нарушены;
- targeted tests успешно проходят;
- полный test suite успешно проходит;
- `git diff --check` не обнаруживает ошибок;
- legacy-потребители не переключались;
- работающий торговый бот сохраняет обратную совместимость.
---
## Итог
**Build 046 завершён успешно.**
Канонический `Candles Feed` интегрирован в сервисный слой `Market Data Acquisition`.
Текущий завершённый путь:
```text
Dzengi REST API
DzengiCandlesDocumentSource
Schema Validation
Parser
Value Validation
Mapper
DzengiCandlesDocumentHandler
CandlesFeed
CandlesFeedRegistry
CandlesAcquisitionService
tuple[Candle, ...]
```
Следующий Build должен продолжить миграцию OHLCV-контура без преждевременного удаления legacy-реализации и без нарушения работы существующего торгового бота.