# Build 051 — Переключение HTF-анализа на канонические Candle ## Статус **Завершён** --- ## Цель Build Переключить оба HTF-пути подсистемы `Market Analysis` с legacy API: ```text ExchangeService.get_klines() ↓ KlineBatch ↓ batch.candles ``` на канонический API: ```text ExchangeService.get_candles() ↓ tuple[Candle, ...] ``` без изменения существующей HTF-семантики, расчётных алгоритмов, порогов, payload-полей и диагностических причин. --- ## Исходное состояние До Build 051 в файле: ```text src/trading/market_analysis/htf.py ``` существовали два legacy-пути получения свечей: ```text htf_volatility_context() htf_trend_context() ``` Оба использовали: ```text ExchangeService.get_klines() ``` и затем извлекали: ```text batch.candles ``` После Build 050 основной `MarketAnalysisService` уже был переключён на: ```text ExchangeService.get_candles() ``` Поэтому `htf.py` оставался последним активным production-потребителем `get_klines()` внутри `src/trading/market_analysis`. --- ## Объём изменений В Build 051 изменены: ```text src/trading/market_analysis/htf.py tests/unit/trading/market_analysis/test_htf_candles.py docs/migrations/build_051.md ``` Build 051 не изменяет: ```text src/trading/market_analysis/service.py src/integrations/exchange/service.py src/integrations/exchange/models.py src/market_data/acquisition/ ``` Build также не удаляет: ```text ExchangeService.get_klines() Kline KlineBatch _kline_from_candle() ``` Их удаление возможно только после отдельной общей проверки всех production-потребителей. --- ## Переключение htf_volatility_context() До Build 051: ```text htf_volatility_context() ↓ ExchangeService.get_klines() ↓ KlineBatch ↓ batch.candles ``` После Build 051: ```text htf_volatility_context() ↓ ExchangeService.get_candles() ↓ tuple[Candle, ...] ``` Удалена зависимость от: ```text batch.candles ``` Все остальные расчёты сохранены. --- ## Переключение htf_trend_context() До Build 051: ```text htf_trend_context() ↓ ExchangeService.get_klines() ↓ KlineBatch ↓ batch.candles ``` После Build 051: ```text htf_trend_context() ↓ ExchangeService.get_candles() ↓ tuple[Candle, ...] ``` Удалена зависимость от legacy-контейнера `KlineBatch`. --- ## Каноническая модель Candle После Build 051 оба HTF-пути непосредственно получают: ```text tuple[Candle, ...] ``` Каноническая модель: ```text src.market_data.acquisition.models.candle.Candle ``` использует: ```text Decimal ``` для полей: ```text open_price high_price low_price close_price volume ``` Поэтому для вычислительных функций была сохранена явная числовая граница: ```text Decimal ↓ safe_float(...) ↓ isfinite(...) ↓ float ``` --- ## Числовая граница в htf_volatility_context() В `htf_volatility_context()` последняя цена закрытия преобразуется через: ```text safe_float() ``` и дополнительно проверяется через: ```text math.isfinite() ``` Невалидные значения: ```text NaN Infinity -Infinity ``` не допускаются к вычислению ATR percent. При невозможности получить корректную цену закрытия сохраняется существующая причина: ```text HTF_ATR_UNAVAILABLE ``` --- ## Числовая граница в htf_trend_context() В `htf_trend_context()` формируется: ```text closes: list[float] ``` Каждая каноническая цена: ```text Candle.close_price ``` проходит: ```text safe_float() isfinite() ``` Если хотя бы одно значение невалидно, HTF-анализ возвращает существующую причину: ```text HTF_INDICATORS_UNAVAILABLE ``` Невалидные значения не пропускаются выборочно, поскольку это нарушило бы соответствие: ```text candles[index] ↔ closes[index] ``` --- ## Сохранение диагностического контракта Несмотря на переход с `get_klines()` на `get_candles()`, существующая причина ошибки получения данных сохранена: ```text HTF_KLINES_ERROR ``` Она используется в обоих HTF-путях. Переименование в: ```text HTF_CANDLES_ERROR ``` не выполнялось, поскольку это не требуется для технической миграции и могло изменить внешний диагностический контракт. --- ## Сохранённая логика htf_volatility_context() Без изменений сохранены: ```text проверка одинакового base и HTF interval минимальное количество свечей ATR close price validation ATR percent ATR percent baseline volatility ratio classify_volatility() rounding формат payload HTF_SKIPPED_SAME_INTERVAL HTF_NOT_ENOUGH_CANDLES HTF_ATR_UNAVAILABLE HTF_OK ``` --- ## Сохранённая логика htf_trend_context() Без изменений сохранены: ```text проверка одинакового base и HTF interval минимальное количество свечей EMA fast EMA slow ATR ATR percent adaptive thresholds EMA slopes trend classification trend gap trend strength trend consistency trend efficiency EMA distance / ATR candle noise score price position score trend quality market phase market state alignment confirmation score формат HTF payload ``` --- ## Новый тестовый файл Добавлен: ```text tests/unit/trading/market_analysis/test_htf_candles.py ``` Файл создан отдельно, поскольку до Build 051 специализированного `test_htf.py` в проекте не существовало. --- ## Покрытие htf_volatility_context() Тесты проверяют: 1. пропуск запроса при одинаковом base и HTF interval; 2. точные аргументы `get_candles()`; 3. сохранение причины `HTF_KLINES_ERROR`; 4. сохранение причины `HTF_NOT_ENOUGH_CANDLES`; 5. отклонение не конечной close price; 6. сохранение причины `HTF_ATR_UNAVAILABLE`; 7. успешный HTF volatility payload; 8. поддержку canonical `Candle` с `Decimal`. --- ## Покрытие htf_trend_context() Тесты проверяют: 1. пропуск запроса при одинаковом base и HTF interval; 2. точные аргументы `get_candles()`; 3. сохранение причины `HTF_KLINES_ERROR`; 4. сохранение причины `HTF_NOT_ENOUGH_CANDLES`; 5. отклонение не конечной close price; 6. сохранение причины `HTF_INDICATORS_UNAVAILABLE`; 7. преобразование `Decimal` closes в `float`; 8. успешный HTF trend payload; 9. неизменность структуры результата. --- ## Targeted tests Выполнена команда: ```bash python -m pytest -q \ tests/unit/trading/market_analysis/test_htf_candles.py ``` Результат: ```text 11 passed in 0.12s ``` --- ## Regression-набор Market Analysis и стратегий Выполнена команда: ```bash python -m pytest -q \ tests/unit/trading/market_analysis \ tests/unit/trading/strategies/test_scalp_quote.py \ tests/unit/trading/strategies/test_trend_quote.py ``` Результат: ```text 38 passed in 0.14s ``` --- ## Полный regression suite Выполнена команда: ```bash python -m pytest -q ``` Результат: ```text 805 passed in 2.72s ``` Регрессий не обнаружено. --- ## Контроль отсутствия get_klines() в Market Analysis Выполнена команда: ```bash grep -RIn \ --exclude-dir="__pycache__" \ --exclude="*.pyc" \ "\.get_klines(" \ src/trading/market_analysis ``` Результат: ```text пусто ``` Это подтверждает, что в активном production-коде `Market Analysis` больше нет вызовов: ```text ExchangeService.get_klines() ``` --- ## Контроль использования get_candles() Выполнена команда: ```bash grep -RIn \ --exclude-dir="__pycache__" \ --exclude="*.pyc" \ "\.get_candles(" \ src/trading/market_analysis ``` Результат: ```text src/trading/market_analysis/service.py:244: candles = ExchangeService().get_candles( src/trading/market_analysis/htf.py:57: candles = ExchangeService().get_candles( src/trading/market_analysis/htf.py:157: candles = ExchangeService().get_candles( ``` После Build 051 в `Market Analysis` существует ровно три production-вызова канонического API. --- ## Контроль legacy batch-зависимостей Выполнена команда: ```bash grep -RIn \ --exclude-dir="__pycache__" \ --exclude="*.pyc" \ "batch\.candles\|batch\.symbol\|KlineBatch\|Kline" \ src/trading/market_analysis ``` Результат: ```text src/trading/market_analysis/indicators_legacy.py:5:from src.integrations.exchange.models import Kline src/trading/market_analysis/indicators_legacy.py:21:def atr(candles: list[Kline], period: int = 14) -> float | None: ``` Следовательно: - активные production-компоненты `Market Analysis` больше не зависят от `Kline`; - единственная оставшаяся зависимость находится в подтверждённом неиспользуемом legacy-файле; - `indicators_legacy.py` намеренно не изменяется в Build 051. --- ## Проверка форматирования Выполнена команда: ```bash git diff --check ``` Вывод отсутствует. Whitespace-ошибок не обнаружено. --- ## Архитектурный результат После Build 051 весь активный production-путь `Market Analysis` использует канонические свечи: ```text MarketAnalysisService ↓ ExchangeService.get_candles() ↓ tuple[Candle, ...] HTF volatility ↓ ExchangeService.get_candles() ↓ tuple[Candle, ...] HTF trend ↓ ExchangeService.get_candles() ↓ tuple[Candle, ...] ``` Legacy API: ```text ExchangeService.get_klines() ``` больше не используется активным `Market Analysis`. --- ## Что намеренно не выполнено Build 051 намеренно не включает: - удаление `ExchangeService.get_klines()`; - удаление `Kline`; - удаление `KlineBatch`; - удаление `_kline_from_candle()`; - удаление `indicators_legacy.py`; - изменение диагностической причины `HTF_KLINES_ERROR`; - изменение алгоритмов HTF; - изменение торговой логики; - изменение порогов; - изменение scoring; - изменение Market Data Acquisition; - изменение структуры каталогов; - удаление рабочего compatibility-кода. --- ## Критерии завершения Build 051 считается завершённым, поскольку: - оба HTF-пути используют `get_candles()`; - зависимости `batch.candles` удалены; - `Decimal` close prices безопасно преобразуются в `float`; - `NaN` и бесконечности отклоняются; - существующие диагностические причины сохранены; - HTF payload не изменён; - специализированные тесты проходят; - regression-набор проходит; - полный suite проходит; - `get_klines()` отсутствует во всём активном `Market Analysis`; - legacy `Kline` остаётся только в неиспользуемом `indicators_legacy.py`; - `git diff --check` чистый. --- ## Итог **Build 051 завершён успешно.** Текущее состояние: ```text MarketAnalysisService → get_candles() HTF volatility → get_candles() HTF trend → get_candles() ``` Результаты: ```text Targeted HTF tests: 11 passed Market Analysis + strategies: 38 passed Full test suite: 805 passed git diff --check: clean ``` Следующий безопасный этап — общая проверка всех оставшихся production-потребителей: ```text ExchangeService.get_klines() Kline KlineBatch _kline_from_candle() ``` Только после этой проверки можно определять отдельный Build по удалению compatibility bridge.