# Build 047 — Переключение `ExchangeService.get_klines()` на канонический Candles Feed **Статус:** Completed **Дата:** 2026-07-15 **Подсистема:** Market Data Acquisition / OHLCV Feed **Тип изменения:** Миграция legacy-потребителя на канонический Acquisition pipeline --- ## 1. Цель Build Цель Build 047 — переключить существующий публичный метод: ```text ExchangeService.get_klines() ``` с прямого получения и самостоятельной обработки ответа Dzengi `/api/v1/klines` на канонический pipeline подсистемы: ```text src/market_data/acquisition/ ``` При этом необходимо сохранить существующий публичный контракт `ExchangeService.get_klines()` и обратную совместимость с текущими потребителями legacy-слоя. --- ## 2. Исходное состояние До Build 047 метод: ```text ExchangeService.get_klines() ``` самостоятельно: 1. выполнял прямой REST-запрос к `/api/v1/klines`; 2. извлекал элементы ответа; 3. разбирал отдельные свечи; 4. создавал legacy-модели `Kline`; 5. формировал `KlineBatch`. В `ExchangeService` существовали собственные legacy-функции обработки свечей: ```text _parse_klines_payload() _extract_klines_items() _parse_kline_item() ``` Таким образом, после появления канонического Candles Feed в системе существовали два независимых пути получения и обработки OHLCV-данных. Это создавало дублирование ответственности между: ```text src/integrations/exchange/ ``` и: ```text src/market_data/acquisition/ ``` --- ## 3. Границы Build В Build 047 выполнено: 1. переключение внутренней реализации `ExchangeService.get_klines()` на канонический Candles Feed; 2. сохранение публичного контракта `ExchangeService.get_klines()`; 3. сохранение legacy-моделей `Kline` и `KlineBatch`; 4. добавление внутреннего адаптационного преобразования `Candle -> Kline`; 5. удаление прямого REST-запроса `/api/v1/klines` из `ExchangeService`; 6. удаление legacy-функций: - `_parse_klines_payload()`; - `_extract_klines_items()`; - `_parse_kline_item()`; 7. добавление специализированных unit-тестов для `ExchangeService.get_klines()` и его интеграции с каноническим Acquisition pipeline. В Build 047 не выполнялось: - изменение публичного контракта `ExchangeService.get_klines()`; - изменение legacy-потребителей в `trading/market_analysis`; - удаление моделей `Kline` и `KlineBatch`; - прямое переключение `trading/market_analysis` на `CandlesAcquisitionService`; - изменение архитектуры других Market Data feeds; - изменение runtime-компонентов Acquisition. --- ## 4. Изменённые production-файлы ### 4.1. `src/integrations/exchange/service.py` Метод: ```text ExchangeService.get_klines() ``` сохранён как публичный compatibility-контракт. Внутренний путь получения данных изменён. До Build 047: ```text ExchangeService.get_klines() ↓ прямой REST-запрос /api/v1/klines ↓ _parse_klines_payload() ↓ _extract_klines_items() ↓ _parse_kline_item() ↓ Kline ↓ KlineBatch ``` После Build 047: ```text ExchangeService.get_klines() ↓ _load_candles_via_acquisition() ↓ DzengiCandlesDocumentSource ↓ DzengiCandlesDocumentHandler ↓ CandlesFeed ↓ CandlesFeedRegistry ↓ CandlesAcquisitionService ↓ Candle ↓ _kline_from_candle() ↓ Kline ↓ KlineBatch ``` --- ## 5. Канонический путь получения свечей После Build 047 единственный production-путь прямого обращения к endpoint: ```text /api/v1/klines ``` расположен в: ```text src/market_data/acquisition/adapters/dzengi/rest.py ``` Это соответствует архитектурному разделению ответственности: ```text Exchange-specific transport ↓ Market Data Acquisition ↓ Canonical Candle ↓ Legacy compatibility boundary ↓ Kline / KlineBatch ``` `ExchangeService` больше не владеет транспортной логикой получения OHLCV-данных от Dzengi. --- ## 6. Добавленные внутренние методы ### 6.1. `_load_candles_via_acquisition()` Добавлен внутренний compatibility-helper: ```text ExchangeService._load_candles_via_acquisition() ``` Его ответственность: 1. создать `DzengiCandlesDocumentSource`; 2. создать `DzengiCandlesDocumentHandler`; 3. создать `CandlesFeed`; 4. зарегистрировать feed в `CandlesFeedRegistry`; 5. создать `CandlesAcquisitionService`; 6. вызвать канонический `load_candles()`; 7. вернуть immutable-набор моделей `Candle`. Метод является внутренней границей между legacy `ExchangeService` и канонической подсистемой Market Data Acquisition. ### 6.2. `_kline_from_candle()` Добавлен внутренний адаптер: ```text ExchangeService._kline_from_candle() ``` Преобразование выполняется по следующему правилу: ```text Candle.symbol -> Kline.symbol Candle.interval -> Kline.interval Candle.open_time -> Kline.open_time Candle.open_price -> Kline.open_price Candle.high_price -> Kline.high_price Candle.low_price -> Kline.low_price Candle.close_price -> Kline.close_price Candle.volume -> Kline.volume ``` Это позволяет сохранить существующий legacy-контракт без переноса legacy-моделей внутрь `market_data/acquisition`. --- ## 7. Удалённая legacy-логика Из: ```text src/integrations/exchange/service.py ``` удалены: ```text _parse_klines_payload() _extract_klines_items() _parse_kline_item() ``` Также удалён прямой вызов: ```text /api/v1/klines ``` из `ExchangeService`. После Build 047 parsing, schema validation, value validation и mapping OHLCV-данных выполняются только канонической подсистемой: ```text src/market_data/acquisition/ ``` --- ## 8. Сохранённая обратная совместимость Публичный контракт: ```text ExchangeService.get_klines(...) ``` сохранён. Существующие production-потребители не изменялись: ```text src/trading/market_analysis/service.py src/trading/market_analysis/htf.py ``` Они продолжают работать через: ```text ExchangeService.get_klines() ``` и получать: ```text KlineBatch ``` Таким образом, Build 047 меняет внутренний источник данных, но не требует одновременного переписывания legacy-потребителей. --- ## 9. Добавленные тесты Добавлен специализированный файл: ```text tests/unit/integrations/exchange/test_service_klines.py ``` Тесты покрывают: - использование `default_symbol`; - нормализацию `limit`; - нормализацию `interval`; - отклонение неподдерживаемого interval; - передачу `price_type`; - обработку выключенной биржи; - обработку неизвестного символа; - передачу параметров в Acquisition pipeline; - преобразование `Candle` в `Kline`; - формирование `KlineBatch`; - обработку пустого набора свечей; - обработку исключений канонического Acquisition pipeline; - построение полного composition pipeline; - сохранение публичного legacy-контракта. Результат targeted-проверки: ```text 26 passed in 0.17s ``` --- ## 10. Полная регрессионная проверка Выполнено: ```bash python -m pytest -q ``` Результат: ```text 750 passed in 2.83s ``` Регрессий не обнаружено. --- ## 11. Контроль прямого использования `/api/v1/klines` Команда: ```bash grep -RIn \ --exclude-dir="__pycache__" \ --exclude="*.pyc" \ '"/api/v1/klines"' \ src ``` Результат: ```text src/market_data/acquisition/adapters/dzengi/rest.py:17:_KLINES_PATH = "/api/v1/klines" ``` Вывод: Прямое знание endpoint `/api/v1/klines` находится только в каноническом Dzengi REST-адаптере. --- ## 12. Контроль удаления legacy-парсеров Команда: ```bash grep -RIn \ --exclude-dir="__pycache__" \ --exclude="*.pyc" \ "_parse_klines_payload\|_extract_klines_items\|_parse_kline_item" \ src tests ``` Результат: ```text пусто ``` Вывод: Legacy-функции обработки kline payload полностью удалены. --- ## 13. Контроль оставшихся потребителей `ExchangeService.get_klines()` Команда: ```bash grep -RIn \ --exclude-dir="__pycache__" \ --exclude="*.pyc" \ "\.get_klines(" \ src tests ``` Production-потребители: ```text src/trading/market_analysis/service.py src/trading/market_analysis/htf.py ``` Дополнительно присутствуют специализированные вызовы в: ```text tests/unit/integrations/exchange/test_service_klines.py ``` Вывод: Публичный compatibility-контракт `ExchangeService.get_klines()` продолжает обслуживать существующие legacy-потребители. --- ## 14. Контроль интеграции с Acquisition pipeline Команда: ```bash grep -RIn \ --exclude-dir="__pycache__" \ --exclude="*.pyc" \ "CandlesAcquisitionService\|CandlesFeedRegistry\|DzengiCandlesDocumentSource" \ src/integrations/exchange/service.py ``` Результат подтверждает использование: ```text DzengiCandlesDocumentSource CandlesFeedRegistry CandlesAcquisitionService ``` внутри compatibility boundary `ExchangeService`. --- ## 15. Контроль качества diff Выполнено: ```bash git diff --check ``` Результат: ```text пусто ``` Whitespace-ошибок не обнаружено. --- ## 16. Архитектурный результат После Build 047 путь OHLCV-данных имеет следующую структуру: ```text Dzengi REST API ↓ DzengiCandlesDocumentSource ↓ schema validation ↓ parser ↓ value validation ↓ mapper ↓ Candle ↓ CandlesFeed ↓ CandlesFeedRegistry ↓ CandlesAcquisitionService ↓ ExchangeService compatibility boundary ↓ Kline / KlineBatch ↓ legacy trading consumers ``` Главный архитектурный результат: ```text ExchangeService больше не получает и не разбирает сырой ответ /api/v1/klines самостоятельно. ``` Владение получением и канонической обработкой OHLCV-данных теперь принадлежит: ```text src/market_data/acquisition/ ``` --- ## 17. Состояние после Build 047 На момент завершения Build: ```text Targeted tests: 26 passed Full test suite: 750 passed git diff --check: clean ``` Прямой endpoint: ```text /api/v1/klines ``` остался только в: ```text src/market_data/acquisition/adapters/dzengi/rest.py ``` Legacy parsing helpers: ```text _parse_klines_payload _extract_klines_items _parse_kline_item ``` полностью отсутствуют. Публичный контракт: ```text ExchangeService.get_klines() ``` сохранён. --- ## 18. Итог Build **Build 047 завершён успешно.** Канонический Candles Feed интегрирован в существующий `ExchangeService.get_klines()` без изменения его публичного контракта. Дублирующая legacy-логика получения и разбора `/api/v1/klines` удалена. Существующие потребители продолжают работать без изменений. Полная регрессионная проверка подтверждает отсутствие нарушений существующего поведения: ```text 750 passed ```