# Build 048 — Переключение вычислительных потребителей Market Analysis на каноническую модель Candle **Статус:** Completed **Дата:** 2026-07-15 **Подсистема:** Market Data Acquisition / OHLCV Feed / Market Analysis --- ## 1. Назначение Build Build 048 переводит активные вычислительные компоненты подсистемы `Market Analysis`, непосредственно работающие с последовательностями рыночных свечей, с legacy-модели: ```text src.integrations.exchange.models.Kline ``` на каноническую модель рыночной свечи: ```text src.market_data.acquisition.models.candle.Candle ``` Build является очередным этапом поэтапной миграции получения и использования OHLCV-данных в канонический слой: ```text Market Data Acquisition ``` Основная цель Build — устранить прямую зависимость активных вычислительных функций анализа рынка от legacy-модели `Kline`, сохранив существующую торговую семантику и поведение системы. --- ## 2. Контекст миграции До Build 048 канонический OHLCV pipeline уже был создан и интегрирован поэтапно. ### Build 044 Создан фундамент канонического Candles Feed: ```text REST transport ↓ schema validation ↓ parser ↓ value validation ↓ mapper ↓ Candle ↓ handler ↓ CandlesFeed ``` Каноническая модель: ```text src/market_data/acquisition/models/candle.py ``` ### Build 045 Создан и протестирован: ```text CandlesFeedRegistry ``` ### Build 046 Создан и интегрирован: ```text CandlesAcquisitionService ``` ### Build 047 Legacy-метод: ```text ExchangeService.get_klines() ``` переключён на канонический Candles Feed. После Build 047 транспортный запрос: ```text /api/v1/klines ``` выполняется только внутри: ```text src/market_data/acquisition/adapters/dzengi/rest.py ``` При этом `ExchangeService.get_klines()` временно сохраняет обратную совместимость и преобразует канонические `Candle` обратно в legacy-модели `Kline`. Build 048 начинает устранять эту обратную legacy-зависимость непосредственно внутри вычислительных потребителей `Market Analysis`. --- ## 3. Границы Build В Build 048 изменены только активные вычислительные компоненты `Market Analysis`, которые непосредственно принимают последовательности свечей и используют поля OHLCV. Изменены: ```text src/trading/market_analysis/indicators/volatility.py src/trading/market_analysis/quality.py src/trading/market_analysis/structure.py ``` Добавлены специализированные unit-тесты: ```text tests/unit/trading/market_analysis/indicators/test_volatility_candle.py tests/unit/trading/market_analysis/test_quality_candle.py tests/unit/trading/market_analysis/test_structure_candle.py ``` Build 048 не изменяет: ```text src/trading/market_analysis/service.py src/trading/market_analysis/htf.py src/trading/market_analysis/indicators_legacy.py src/integrations/exchange/service.py src/integrations/exchange/models.py ``` Build также не удаляет: ```text Kline KlineBatch ExchangeService.get_klines() ``` Их дальнейшая судьба определяется отдельными последующими этапами миграции. --- ## 4. Основное архитектурное изменение До Build 048 активные вычислительные функции использовали legacy-модель: ```python from src.integrations.exchange.models import Kline ``` После Build 048 они используют каноническую модель: ```python from src.market_data.acquisition.models.candle import Candle ``` Изменение выполнено в: ```text src/trading/market_analysis/indicators/volatility.py src/trading/market_analysis/quality.py src/trading/market_analysis/structure.py ``` Таким образом, активные вычислительные функции: ```text atr() atr_percent_baseline() candle_noise_score() market_structure() ``` больше не зависят от: ```text src.integrations.exchange.models.Kline ``` и работают непосредственно с: ```text src.market_data.acquisition.models.candle.Candle ``` --- ## 5. Адаптация к канонической модели Candle Каноническая модель `Candle` использует точные числовые значения: ```text Decimal ``` для полей: ```text open_price high_price low_price close_price volume ``` Legacy-модель `Kline` использовала: ```text float ``` Поэтому Build 048 выполняет явную адаптацию вычислительного слоя к `Decimal`. Для безопасного преобразования используется существующий helper: ```text src.core.numbers.safe_float ``` Числовые значения преобразуются непосредственно на границе вычислений. Принцип: ```text Candle ↓ Decimal OHLCV ↓ safe_float(...) ↓ float ↓ существующая вычислительная логика Market Analysis ``` Это позволяет: - сохранить точность канонической модели данных; - не переносить `float` в слой Market Data Acquisition; - сохранить существующие вычислительные алгоритмы; - минимизировать область изменений; - не изменять торговую семантику в рамках миграционного Build. --- ## 6. Изменения volatility.py Файл: ```text src/trading/market_analysis/indicators/volatility.py ``` переведён с: ```text Sequence[Kline] ``` на: ```text Sequence[Candle] ``` Адаптированы функции: ```text atr() atr_percent_baseline() ``` Поля канонической модели: ```text previous.close_price current.high_price current.low_price ``` преобразуются через: ```text safe_float(...) ``` Дополнительно выполняется проверка: ```text math.isfinite(...) ``` для исключения нечисловых конечных значений: ```text NaN Infinity -Infinity ``` Это необходимо, поскольку преобразование: ```text Decimal("NaN") ``` в `float` само по себе не возвращает `None`, а создаёт: ```text float("nan") ``` Без проверки `isfinite()` такое значение могло проникнуть в вычисление ATR. --- ## 7. Изменения quality.py Файл: ```text src/trading/market_analysis/quality.py ``` переведён с: ```text Sequence[Kline] ``` на: ```text Sequence[Candle] ``` Адаптирована функция: ```text candle_noise_score() ``` Поля: ```text high_price low_price open_price close_price ``` преобразуются через: ```text safe_float(...) ``` и проверяются через: ```text isfinite(...) ``` Невалидные свечи не участвуют в расчёте. Если после фильтрации отсутствуют допустимые свечи, функция возвращает: ```text None ``` Существующая функция: ```text price_position_score() ``` сохранена. Build 048 не изменяет её назначение и вычислительную семантику. --- ## 8. Изменения structure.py Файл: ```text src/trading/market_analysis/structure.py ``` переведён с: ```text Sequence[Kline] ``` на: ```text Sequence[Candle] ``` Адаптирована функция: ```text market_structure() ``` Для построения последовательностей максимумов и минимумов используются значения: ```text candle.high_price candle.low_price ``` с безопасным преобразованием к вычислительному типу. Существующая логика определения структуры рынка сохранена: ```text HH_HL LH_LL MIXED UNKNOWN ``` Build 048 не изменяет: - правила определения swing high; - правила определения swing low; - правила сравнения последних swing-точек; - adaptive structure parameters; - существующие причины результата; - существующую классификацию `MarketStructure`. --- ## 9. Защита от невалидных числовых значений В ходе специализированного тестирования была выявлена необходимость явной проверки конечности числовых значений. Для вычислительных функций используется: ```python from math import isfinite ``` После: ```text safe_float(...) ``` значения дополнительно проверяются на: ```text NaN Infinity -Infinity ``` Это предотвращает распространение невалидных числовых значений внутри: ```text ATR ATR baseline candle noise score ``` В частности, предотвращается ситуация: ```text Decimal("NaN") ↓ float("nan") ↓ результат вычисления nan ``` Вместо этого невалидное значение исключается из вычисления согласно локальной логике соответствующей функции. --- ## 10. Специализированные unit-тесты Добавлены три специализированных тестовых файла. ### 10.1. Volatility Файл: ```text tests/unit/trading/market_analysis/indicators/test_volatility_candle.py ``` Проверяет: - работу `atr()` с каноническими `Candle`; - поддержку `Decimal`; - недостаточное количество свечей; - невалидный период; - обработку не конечных числовых значений; - работу `atr_percent_baseline()`; - невалидную цену закрытия. ### 10.2. Quality Файл: ```text tests/unit/trading/market_analysis/test_quality_candle.py ``` Проверяет: - работу `candle_noise_score()` с `Candle`; - поддержку `Decimal`; - пустую последовательность; - свечу с нулевым диапазоном; - не конечные числовые значения; - применение указанного tail window. ### 10.3. Structure Файл: ```text tests/unit/trading/market_analysis/test_structure_candle.py ``` Проверяет: - структуру `HH_HL`; - структуру `LH_LL`; - структуру `MIXED`; - недостаточное количество свечей; - непосредственную работу с `Decimal`-значениями `Candle`. --- ## 11. Результаты специализированных тестов Команда: ```bash python -m pytest -q \ tests/unit/trading/market_analysis/indicators/test_volatility_candle.py \ tests/unit/trading/market_analysis/test_quality_candle.py \ tests/unit/trading/market_analysis/test_structure_candle.py ``` Результат: ```text 16 passed in 0.02s ``` --- ## 12. Regression-проверка стратегий Дополнительно выполнена проверка стратегий: ```bash python -m pytest -q \ tests/unit/trading/strategies/test_scalp_quote.py \ tests/unit/trading/strategies/test_trend_quote.py ``` Результат: ```text 6 passed in 0.14s ``` Эта проверка подтверждает сохранение импортов и существующей интеграции `Market Analysis` с торговыми стратегиями. --- ## 13. Полный regression suite Выполнена команда: ```bash python -m pytest -q ``` Результат: ```text 766 passed in 2.53s ``` Полный unit test suite проекта проходит успешно. --- ## 14. Контроль legacy-зависимости Kline Выполнена команда: ```bash grep -RIn \ --exclude-dir="__pycache__" \ --exclude="*.pyc" \ "integrations.exchange.models import Kline" \ src/trading/market_analysis ``` Результат: ```text src/trading/market_analysis/indicators_legacy.py:5:from src.integrations.exchange.models import Kline ``` Следовательно, после Build 048 активные вычислительные компоненты: ```text volatility.py quality.py structure.py ``` больше не импортируют legacy-модель `Kline`. Оставшийся импорт находится только в: ```text indicators_legacy.py ``` который не изменяется в рамках Build 048. --- ## 15. Контроль использования канонической модели Candle Выполнена команда: ```bash grep -RIn \ --exclude-dir="__pycache__" \ --exclude="*.pyc" \ "models.candle import Candle" \ src/trading/market_analysis ``` Результат: ```text src/trading/market_analysis/indicators/volatility.py:10:from src.market_data.acquisition.models.candle import Candle src/trading/market_analysis/structure.py:9:from src.market_data.acquisition.models.candle import Candle src/trading/market_analysis/quality.py:9:from src.market_data.acquisition.models.candle import Candle ``` Это подтверждает прямую зависимость трёх активных вычислительных потребителей от канонической модели `Candle`. --- ## 16. Контроль оставшихся вызовов ExchangeService.get_klines() Выполнена команда: ```bash grep -RIn \ --exclude-dir="__pycache__" \ --exclude="*.pyc" \ "\.get_klines(" \ src/trading/market_analysis ``` Результат: ```text src/trading/market_analysis/service.py:243: batch = ExchangeService().get_klines( src/trading/market_analysis/htf.py:55: batch = ExchangeService().get_klines( src/trading/market_analysis/htf.py:153: batch = ExchangeService().get_klines( ``` Эти три вызова являются ожидаемыми и не удаляются в Build 048. Они показывают следующую границу дальнейшей миграции: ```text Market Analysis orchestration ↓ прямое получение Candle ↓ устранение зависимости от ExchangeService.get_klines() ``` Такое переключение должно выполняться отдельным Build после анализа контрактов: ```text service.py htf.py ``` --- ## 17. Проверка форматирования diff Выполнена команда: ```bash git diff --check ``` Вывод отсутствует. Следовательно: ```text trailing whitespace отсутствует ошибки whitespace отсутствуют ``` --- ## 18. Архитектурный результат После Build 048 активный вычислительный путь выглядит следующим образом: ```text Dzengi REST API ↓ DzengiCandlesDocumentSource ↓ validate_candles_schema() ↓ parse_candles() ↓ validate_candles_values() ↓ map_candles() ↓ Candle ↓ CandlesDocumentHandler ↓ CandlesFeed ↓ CandlesFeedRegistry ↓ CandlesAcquisitionService ↓ ExchangeService.get_klines() ↓ legacy compatibility conversion ↓ Market Analysis orchestration ↓ active computational consumers typed for Candle ``` При этом три активных вычислительных компонента уже используют канонический контракт: ```text volatility.py → Candle quality.py → Candle structure.py → Candle ``` Однако orchestration-слой `Market Analysis` пока продолжает получать данные через: ```text ExchangeService.get_klines() ``` Поэтому полное устранение legacy-преобразования `Candle → Kline` ещё не завершено. --- ## 19. Что сознательно не сделано В Build 048 сознательно не выполнялось: - удаление `Kline`; - удаление `KlineBatch`; - удаление `ExchangeService.get_klines()`; - изменение `MarketAnalysisService`; - изменение HTF orchestration; - изменение `indicators_legacy.py`; - изменение торговых стратегий; - изменение scoring; - изменение signal logic; - изменение торговых порогов; - изменение определения market structure; - изменение правил ATR; - изменение существующей архитектуры каталогов; - удаление рабочего legacy-кода. Это соответствует принципу поэтапной миграции: ```text сначала новый канонический путь ↓ затем переключение потребителей ↓ затем подтверждение отсутствия legacy-потребителей ↓ только после этого отдельное удаление legacy-кода ``` --- ## 20. Соблюдение правил миграции Build 048 соответствует утверждённым правилам проекта Dzentra: 1. Рабочий код не удаляется без отдельного согласования. 2. Изменения ограничены согласованными границами Build. 3. Канонический слой Market Data не зависит от `src.trading`. 4. Миграция выполняется поэтапно. 5. Обратная совместимость сохраняется там, где она ещё необходима. 6. Legacy-код не удаляется до подтверждённого переключения всех его потребителей. 7. Существующая торговая семантика не изменяется в рамках инфраструктурной миграции. 8. Каждый Build подтверждается специализированными тестами, полным regression suite и контрольными grep. --- ## 21. Итог Build 048 Build 048 завершён успешно. В результате: ```text volatility.py quality.py structure.py ``` переведены с: ```text Kline ``` на: ```text Candle ``` Канонические `Decimal`-значения адаптированы к существующему вычислительному слою через: ```text safe_float(...) ``` с дополнительной защитой от: ```text NaN Infinity -Infinity ``` Подтверждено: ```text 16 специализированных тестов passed 6 regression-тестов стратегий passed 766 тестов полного suite passed git diff --check — чисто ``` Build 048 завершает переключение выбранных активных вычислительных потребителей `Market Analysis` на каноническую модель `Candle`. Следующая архитектурная граница миграции — прямое переключение orchestration-компонентов: ```text src/trading/market_analysis/service.py src/trading/market_analysis/htf.py ``` с: ```text ExchangeService.get_klines() ``` на получение канонических: ```text Candle ``` через слой: ```text Market Data Acquisition ``` Такое переключение должно выполняться отдельным Build с сохранением существующего поведения системы.