build 046: integrate canonical candles feed into acquisition service
This commit is contained in:
515
docs/migrations/build_046.md
Normal file
515
docs/migrations/build_046.md
Normal file
@@ -0,0 +1,515 @@
|
||||
# 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-реализации и без нарушения работы существующего торгового бота.
|
||||
Reference in New Issue
Block a user