Files
dzentra_bot/docs/migrations/build_049.md

570 lines
14 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 049 — Предоставление канонических Candle через публичный API ExchangeService
## Статус
**Завершён**
---
## Цель Build
Предоставить существующим потребителям публичный канонический API получения рыночных свечей:
```text
ExchangeService.get_candles()
tuple[Candle, ...]
```
без прямого доступа слоя Trading к деталям сборки `Market Data Acquisition` и без удаления действующего compatibility-контракта:
```text
ExchangeService.get_klines()
KlineBatch
```
Build 049 подготавливает orchestration-компоненты `Market Analysis` к последующему поэтапному переключению с legacy-моделей `Kline` и `KlineBatch` на каноническую модель `Candle`.
---
## Исходное состояние
До Build 049 `ExchangeService` уже использовал канонический Candles pipeline:
```text
DzengiCandlesDocumentSource
DzengiCandlesDocumentHandler
CandlesFeed
CandlesFeedRegistry
CandlesAcquisitionService
tuple[Candle, ...]
```
Однако публично сервис предоставлял только legacy-метод:
```text
ExchangeService.get_klines()
```
который:
1. выполнял проверку параметров;
2. вызывал канонический Acquisition pipeline;
3. преобразовывал `Candle` в `Kline`;
4. возвращал `KlineBatch`.
Из-за отсутствия публичного метода `get_candles()` непосредственное переключение `Market Analysis` на канонические модели потребовало бы либо использования внутреннего helper, либо дублирования composition chain в слое Trading.
---
## Объём изменений
В Build 049 изменены:
```text
src/integrations/exchange/service.py
tests/unit/integrations/exchange/test_service_candles.py
docs/migrations/build_049.md
```
Существующий файл:
```text
tests/unit/integrations/exchange/test_service_klines.py
```
не изменялся. Он использован как regression-контракт сохранения поведения legacy API.
Build 049 не изменяет:
```text
src/trading/market_analysis/service.py
src/trading/market_analysis/htf.py
src/market_data/acquisition/
src/integrations/exchange/models.py
```
Build не удаляет:
```text
ExchangeService.get_klines()
Kline
KlineBatch
_kline_from_candle()
```
---
## Новый публичный метод get_candles()
В `ExchangeService` добавлен метод:
```python
def get_candles(
self,
symbol: str | None = None,
*,
interval: str = "1m",
limit: int = 200,
price_type: str = "bid",
) -> tuple[Candle, ...]:
...
```
Метод возвращает immutable-набор канонических моделей:
```text
tuple[Candle, ...]
```
без преобразования в legacy-модели, без копирования и без сортировки результата.
---
## Ответственность get_candles()
`get_candles()` выполняет:
1. выбор `default_symbol`, если symbol не передан;
2. нормализацию `limit`;
3. проверку поддерживаемого interval;
4. нормализацию `price_type`;
5. проверку mock mode;
6. валидацию symbol;
7. вызов `_load_candles_via_acquisition()`;
8. логирование ошибок Acquisition pipeline;
9. преобразование ошибки в `ExchangeError`.
Нормализация limit:
```text
limit <= 0 → 200
limit > 200 → 200
иначе → исходное значение
```
Поддерживаемые intervals:
```text
1m
5m
15m
1h
```
Поддерживаемые значения `price_type`:
```text
bid
ask
```
Неизвестное значение нормализуется в:
```text
bid
```
---
## Канонический путь получения данных
После Build 049 публичный канонический путь выглядит так:
```text
ExchangeService.get_candles()
_load_candles_via_acquisition()
DzengiCandlesDocumentSource
DzengiCandlesDocumentHandler
CandlesFeed
CandlesFeedRegistry
CandlesAcquisitionService
tuple[Candle, ...]
```
Слой Trading получает стабильную публичную точку доступа к каноническим свечам и не знает о:
```text
DzengiCandlesDocumentSource
DzengiCandlesDocumentHandler
CandlesFeedRegistry
CandlesAcquisitionService composition
```
---
## Изменение get_klines()
`ExchangeService.get_klines()` сохранён как legacy compatibility-фасад.
После Build 049 его цепочка:
```text
ExchangeService.get_klines()
ExchangeService.get_candles()
tuple[Candle, ...]
_kline_from_candle()
list[Kline]
KlineBatch
```
`get_klines()` больше не вызывает `_load_candles_via_acquisition()` напрямую.
Таким образом, логика получения, проверки и обработки Acquisition-ошибок сосредоточена в одном публичном каноническом методе:
```text
get_candles()
```
---
## Сохранение legacy-контракта
Для `get_klines()` сохранены:
- публичная сигнатура;
- возвращаемый тип `KlineBatch`;
- нормализация limit;
- нормализация `price_type`;
- compatibility mapping `Candle → Kline`;
- источник вида `rest_klines:<price_type>`;
- прежнее сообщение mock mode:
```text
Klines are not available in mock exchange mode.
```
Для нового канонического API используется отдельное сообщение:
```text
Candles are not available in mock exchange mode.
```
Это сохраняет прежний контракт legacy-метода и одновременно вводит семантически корректный контракт нового API.
---
## Исключение повторной валидации symbol
При пустом наборе канонических свечей `get_klines()` не выполняет повторный вызов:
```text
validate_symbol()
```
Для заполнения symbol в пустом `KlineBatch` используется локальная нормализация:
```text
normalize_symbol(symbol_to_use)
```
Это сохраняет:
- один validation flow;
- один вызов `validate_symbol()`;
- корректный symbol пустого `KlineBatch`;
- отсутствие повторной загрузки Instrument Reference Data.
---
## Новый тестовый файл
Добавлен:
```text
tests/unit/integrations/exchange/test_service_candles.py
```
Тесты проверяют:
- использование `default_symbol`;
- нормализацию неположительного limit;
- ограничение limit значением 200;
- допустимые intervals;
- отклонение неподдерживаемого interval;
- нормализацию `price_type`;
- ошибку mock mode;
- ошибку invalid symbol;
- точные параметры вызова Acquisition helper;
- сохранение identity возвращённого tuple;
- сохранение порядка свечей;
- сохранение identity пустого tuple;
- логирование Acquisition-ошибок;
- преобразование ошибки в `ExchangeError`;
- сохранение исходного исключения как `__cause__`.
---
## Результаты targeted-тестов get_candles()
Выполнена команда:
```bash
python -m pytest -q tests/unit/integrations/exchange/test_service_candles.py
```
Результат:
```text
23 passed in 0.13s
```
---
## Совместная проверка canonical и legacy API
Выполнена команда:
```bash
python -m pytest -q tests/unit/integrations/exchange/test_service_candles.py tests/unit/integrations/exchange/test_service_klines.py
```
Результат:
```text
49 passed in 0.13s
```
Это подтверждает одновременно:
```text
get_candles() → новый канонический контракт
get_klines() → сохранённый legacy compatibility-контракт
```
---
## Полный regression suite
Выполнена команда:
```bash
python -m pytest -q
```
Результат:
```text
789 passed in 2.80s
```
Регрессий не обнаружено.
---
## Архитектурная проверка get_candles()
Выполнена команда:
```bash
grep -RIn --exclude-dir="__pycache__" --exclude="*.pyc" "def get_candles\|\.get_candles(" src tests
```
Результат подтверждает:
- определение `get_candles()` находится в `ExchangeService`;
- `get_klines()` вызывает `get_candles()`;
- специализированные вызовы находятся в `test_service_candles.py`;
- production-компоненты `Market Analysis` пока не переключены.
---
## Контроль legacy-потребителей
Выполнена команда:
```bash
grep -RIn --exclude-dir="__pycache__" --exclude="*.pyc" "\.get_klines(" src/trading/market_analysis tests/unit/integrations/exchange
```
Production-вызовы сохранены в:
```text
src/trading/market_analysis/service.py
src/trading/market_analysis/htf.py
```
Всего остаётся три production-вызова:
```text
MarketAnalysisService — один вызов
HTF orchestration — два вызова
```
Это ожидаемое переходное состояние.
---
## Контроль вызова Acquisition helper
Выполнена команда:
```bash
grep -RIn --exclude-dir="__pycache__" --exclude="*.pyc" "_load_candles_via_acquisition(" src/integrations/exchange/service.py tests/unit/integrations/exchange
```
В production обнаружены:
```text
один вызов внутри get_candles()
одно определение _load_candles_via_acquisition()
```
`get_klines()` больше не обращается к Acquisition helper напрямую.
---
## Контроль composition и compatibility bridge
Выполнена команда:
```bash
grep -nE "def get_candles|def get_klines|self\.get_candles|_load_candles_via_acquisition|_kline_from_candle" src/integrations/exchange/service.py
```
Результат подтверждает ожидаемую последовательность:
```text
get_candles()
_load_candles_via_acquisition()
get_klines()
self.get_candles()
_kline_from_candle()
```
---
## Проверка форматирования
Выполнена команда:
```bash
git diff --check
```
Вывод отсутствует.
Whitespace-ошибок не обнаружено.
---
## Архитектурный результат
После Build 049 доступны два явно разделённых публичных контракта.
Канонический API:
```text
ExchangeService.get_candles()
tuple[Candle, ...]
```
Legacy compatibility API:
```text
ExchangeService.get_klines()
get_candles()
Candle → Kline
KlineBatch
```
Получение данных и обработка ошибок больше не дублируются между двумя публичными методами.
---
## Что намеренно не выполнено
Build 049 намеренно не включает:
- переключение `MarketAnalysisService` на `get_candles()`;
- переключение HTF orchestration на `get_candles()`;
- удаление `get_klines()`;
- удаление `Kline`;
- удаление `KlineBatch`;
- удаление `_kline_from_candle()`;
- изменение торговой логики;
- изменение алгоритмов Market Analysis;
- изменение `Market Data Acquisition`;
- изменение структуры каталогов;
- удаление рабочего compatibility-кода.
---
## Критерии завершения
Build 049 считается завершённым, поскольку:
- добавлен публичный `ExchangeService.get_candles()`;
- метод возвращает канонический `tuple[Candle, ...]`;
- `get_klines()` использует `get_candles()` как единственный источник свечей;
- прямой вызов Acquisition helper из `get_klines()` устранён;
- legacy-контракт `KlineBatch` сохранён;
- повторная symbol validation устранена;
- новый canonical API покрыт специализированными тестами;
- legacy API проходит существующие regression-тесты;
- полный suite проходит;
- архитектурные grep соответствуют переходному состоянию;
- `git diff --check` чистый.
---
## Итог
**Build 049 завершён успешно.**
Публичный канонический API:
```text
ExchangeService.get_candles()
```
готов для поэтапного переключения orchestration-компонентов `Market Analysis`.
Текущее состояние:
```text
Targeted canonical + legacy tests: 49 passed
Full test suite: 789 passed
git diff --check: clean
```
Следующий безопасный этап — отдельное переключение непосредственных production-потребителей с:
```text
ExchangeService.get_klines()
```
на:
```text
ExchangeService.get_candles()
```
без одновременного удаления legacy compatibility-контракта.