13 KiB
Build 046 — Интеграция канонического Candles Feed в сервисный слой Market Data Acquisition
Статус
Завершён
Цель Build
Интегрировать канонический Candles Feed в существующий сервисный слой подсистемы:
src/market_data/acquisition/
без переключения legacy-потребителей ExchangeService.get_klines() и без изменения существующего поведения работающего торгового бота.
Build продолжает поэтапную миграцию получения OHLCV-данных из legacy-подсистемы:
src/integrations/exchange/
в каноническую подсистему:
src/market_data/acquisition/
Исходное состояние
До начала Build 046 канонический OHLCV-контур уже включал:
Candle
↓
DzengiCandlesDocumentSource
↓
schema validation
↓
parser
↓
value validation
↓
mapper
↓
DzengiCandlesDocumentHandler
↓
CandlesFeed
↓
CandlesFeedRegistry
Однако сервисный слой Market Data Acquisition ещё не предоставлял application-level сервис для получения канонического набора свечей.
В файле:
src/market_data/acquisition/service.py
существовали отдельные сервисы для:
InstrumentAcquisitionService
QuoteAcquisitionService
При этом CandlesAcquisitionService отсутствовал.
Принятое архитектурное решение
В рамках Build 046 не создавался новый объединяющий класс MarketDataAcquisitionService.
Существующая архитектура сервисного слоя использует отдельный application-level сервис для каждого типа рыночных данных:
InstrumentAcquisitionService
QuoteAcquisitionService
Поэтому для OHLCV-данных добавлен отдельный:
CandlesAcquisitionService
Это сохраняет существующий архитектурный паттерн и не требует изменения уже работающих контрактов Instrument Reference Data и Quotes Feed.
Границы Build
В рамках Build 046 изменены только:
src/market_data/acquisition/service.py
tests/unit/market_data/acquisition/test_service.py
docs/migrations/build_046.md
Не изменялись:
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
В файл:
src/market_data/acquisition/service.py
добавлен класс:
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 добавлены импорты:
from src.market_data.acquisition.models.candle import Candle
и:
from src.market_data.acquisition.registry import CandlesFeedRegistry
3. Зафиксирована ответственность CandlesAcquisitionService
CandlesAcquisitionService выполняет только две операции:
- получает зарегистрированный
Candles Feedпо имени источника; - делегирует ему загрузку свечей.
Каноническая цепочка:
source_name
↓
CandlesFeedRegistry.get()
↓
CandlesFeedProtocol
↓
load_candles()
↓
tuple[Candle, ...]
Сервис не выполняет:
нормализацию source_name
нормализацию symbol
валидацию interval
ограничение limit
нормализацию price_type
schema validation
parsing
value validation
mapping
REST-запросы
retry
кэширование
сортировку свечей
копирование результата
оборачивание исключений
Это сохраняет строгие границы ответственности между слоями.
Контракт CandlesAcquisitionService
Метод:
load_candles(
source_name: str,
symbol: str,
*,
interval: str,
limit: int,
price_type: str,
) -> tuple[Candle, ...]
гарантирует следующую последовательность:
1. Получить Feed:
registry.get(source_name)
2. Передать Feed исходные параметры:
symbol
interval
limit
price_type
3. Вернуть результат Feed без преобразований.
Сервис не изменяет входные параметры.
Сервис не изменяет порядок свечей.
Сервис сохраняет identity возвращённого immutable tuple.
Сервис не выполняет автоматический retry при ошибке.
Исключения нижележащих слоёв пробрасываются без оборачивания.
Добавленные тесты
В:
tests/unit/market_data/acquisition/test_service.py
добавлено тестовое покрытие CandlesAcquisitionService.
Проверяются следующие свойства:
- сервис возвращает результат зарегистрированного Feed;
- сохраняется identity возвращённого
tuple; source_nameпередаётся в registry без изменений;- registry вызывается ровно один раз;
symbolпередаётся в Feed без изменений;intervalпередаётся без изменений;limitпередаётся без изменений;price_typeпередаётся без изменений;- Feed вызывается ровно один раз;
- пустой
tupleвозвращается без изменения; - порядок свечей сохраняется;
CandleFeedRegistryErrorпробрасывается без оборачивания;- после ошибки registry Feed не вызывается;
- ошибки Feed пробрасываются без оборачивания;
- после ошибки Feed автоматический retry не выполняется.
Для проверки ошибок Feed используются:
CandleTransportError
CandleValueError
CandleMappingError
Результаты проверок
Компиляция
Выполнена команда:
python -m compileall \
src/market_data/acquisition/service.py \
tests/unit/market_data/acquisition/test_service.py
Результат:
Compiling 'src/market_data/acquisition/service.py'...
Compiling 'tests/unit/market_data/acquisition/test_service.py'...
Ошибок компиляции нет.
Targeted tests
Выполнена команда:
python -m pytest -q \
tests/unit/market_data/acquisition/test_service.py
Результат:
40 passed in 0.03s
Полный test suite
Выполнена команда:
python -m pytest -q
Результат:
724 passed in 2.43s
Регрессий не обнаружено.
Проверка Git diff
Выполнена команда:
git diff --check
Результат:
пустой вывод
Whitespace errors отсутствуют.
Архитектурный результат
После Build 046 канонический OHLCV-контур имеет следующую структуру:
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-метод:
ExchangeService.get_klines()
не изменялся и не удалялся.
Legacy-потребители:
src/trading/market_analysis/service.py
src/trading/market_analysis/htf.py
продолжают использовать существующий API.
Это соответствует основному правилу миграции Dzentra:
Сначала строится и проверяется новый канонический путь, затем потребители переключаются на него поэтапно, и только после подтверждения полной совместимости удаляется legacy-код.
Что намеренно не выполнялось
Build 046 не включает:
переключение 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.
Текущий завершённый путь:
Dzengi REST API
↓
DzengiCandlesDocumentSource
↓
Schema Validation
↓
Parser
↓
Value Validation
↓
Mapper
↓
DzengiCandlesDocumentHandler
↓
CandlesFeed
↓
CandlesFeedRegistry
↓
CandlesAcquisitionService
↓
tuple[Candle, ...]
Следующий Build должен продолжить миграцию OHLCV-контура без преждевременного удаления legacy-реализации и без нарушения работы существующего торгового бота.