Files
dzentra_bot/docs/migrations/build_046.md

13 KiB
Raw Blame History

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 выполняет только две операции:

  1. получает зарегистрированный Candles Feed по имени источника;
  2. делегирует ему загрузку свечей.

Каноническая цепочка:

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.

Проверяются следующие свойства:

  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 используются:

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-реализации и без нарушения работы существующего торгового бота.