Files
dzentra_bot/docs/migrations/build_050.md

17 KiB
Raw Permalink Blame History

Build 050 — Переключение MarketAnalysisService на канонические Candle

Статус

Завершён


Цель Build

Переключить основной orchestration-компонент анализа рынка:

MarketAnalysisService

с legacy API получения свечей:

ExchangeService.get_klines()
    ↓
KlineBatch
    ↓
list[Kline]

на канонический API:

ExchangeService.get_candles()
    ↓
tuple[Candle, ...]

без изменения торговой логики, алгоритмов анализа рынка, порогов, scoring, HTF-логики и существующих compatibility-контрактов.


Исходное состояние

До Build 050 основной orchestration-компонент:

src/trading/market_analysis/service.py

получал свечи следующим образом:

ExchangeService.get_klines()
    ↓
KlineBatch
    ↓
batch.candles
    ↓
list[Kline]

При этом после Build 048 следующие вычислительные компоненты уже были переключены на каноническую модель Candle:

src/trading/market_analysis/indicators/volatility.py
src/trading/market_analysis/quality.py
src/trading/market_analysis/structure.py

Таким образом возникло переходное несоответствие типов:

MarketAnalysisService
    ↓
list[Kline]

market_structure()
candle_noise_score()
ATR calculations
    ↓
Sequence[Candle]

Build 050 устраняет это несоответствие для основного MarketAnalysisService.


Объём изменений

В Build 050 изменены:

src/trading/market_analysis/service.py
tests/unit/trading/market_analysis/test_market_analysis_service_candles.py
docs/migrations/build_050.md

Build 050 намеренно не изменяет:

src/trading/market_analysis/htf.py
src/integrations/exchange/service.py
src/integrations/exchange/models.py
src/market_data/acquisition/

Также не изменяются:

ExchangeService.get_klines()
Kline
KlineBatch
_kline_from_candle()

Переключение MarketAnalysisService на get_candles()

До Build 050 основной путь получения свечей выглядел так:

MarketAnalysisService.analyze()
    ↓
ExchangeService.get_klines()
    ↓
KlineBatch
    ↓
batch.candles

После Build 050:

MarketAnalysisService.analyze()
    ↓
ExchangeService.get_candles()
    ↓
tuple[Candle, ...]

Таким образом основной orchestration-компонент больше не зависит от:

Kline
KlineBatch
batch.candles
batch.symbol

Каноническая модель данных

После Build 050 MarketAnalysisService непосредственно получает:

tuple[Candle, ...]

где каждая свеча представлена канонической моделью:

src.market_data.acquisition.models.candle.Candle

Основные числовые поля модели:

open_price: Decimal
high_price: Decimal
low_price: Decimal
close_price: Decimal
volume: Decimal

Таким образом основной Market Analysis orchestration теперь работает на той же канонической модели свечи, что и вычислительные компоненты, переключённые в Build 048.


Определение analysis_symbol

Ранее symbol результата получался из:

batch.symbol

После удаления зависимости от KlineBatch введена локальная переменная:

analysis_symbol = candles[0].symbol if candles else symbol

Логика:

  • если получена хотя бы одна каноническая свеча, используется Candle.symbol;
  • если набор свечей пуст, сохраняется исходный запрошенный symbol.

Все прежние обращения:

batch.symbol

в MarketAnalysisService заменены на:

analysis_symbol

Это устраняет зависимость основного orchestration от legacy-контейнера KlineBatch.


Числовая граница Decimal → float

Каноническая модель Candle хранит цены как:

Decimal

При этом существующие индикаторы и часть вычислительного pipeline работают с:

float

Поэтому в MarketAnalysisService введена явная числовая граница преобразования цен закрытия:

Candle.close_price
    ↓
safe_float()
    ↓
float
    ↓
list[float]

После Build 050:

closes: list[float] = []

for candle in candles:
    close_price_value = safe_float(candle.close_price)

    ...

    closes.append(close_price_value)

Это сохраняет существующий числовой контракт индикаторов и одновременно позволяет основному orchestration работать непосредственно с каноническими Candle.


Проверка невалидных цен закрытия

При преобразовании Candle.close_price выполняется проверка:

  • результат safe_float() не должен быть None;
  • результат должен быть конечным числом;
  • NaN не допускается;
  • положительная или отрицательная бесконечность не допускается.

Для проверки конечности используется:

math.isfinite()

Если обнаружена некорректная цена закрытия, анализ безопасно завершается результатом UNKNOWN с причиной:

Получены некорректные цены закрытия свечей.

При этом сохраняется полное соответствие:

candles[index] ↔ closes[index]

Невалидные значения не пропускаются выборочно, поскольку это нарушило бы временное соответствие последовательностей свечей и цен.


Сохранение недостаточного количества свечей

Проверка минимального количества свечей сохранена.

Если:

len(candles) < 60

анализ завершается безопасным результатом UNKNOWN с причиной:

Недостаточно свечей для анализа рынка.

При пустом наборе свечей используется исходный запрошенный symbol.

При непустом наборе используется canonical symbol первой свечи.


Сохранение обработки ошибок ExchangeService

Вызов:

ExchangeService.get_candles()

остаётся внутри существующей обработки исключений.

Если получение свечей завершается ошибкой, MarketAnalysisService возвращает безопасный результат UNKNOWN с причиной, содержащей сообщение исходной ошибки.

Build 050 не изменяет общую стратегию обработки ошибок анализа рынка.


Сохранение вычислительной логики

Build 050 не изменяет:

  • EMA;
  • ATR;
  • RSI;
  • momentum;
  • market phase;
  • market structure;
  • candle noise score;
  • price position score;
  • scoring;
  • confidence;
  • payload;
  • причины решений;
  • пороги;
  • HTF calculations;
  • индексы текущей и закрытой свечи;
  • торговые решения.

Изменяется только источник и тип входных свечей основного orchestration-компонента:

Было:
KlineBatch → list[Kline]

Стало:
tuple[Candle, ...]

HTF намеренно не изменён

В Build 050 файл:

src/trading/market_analysis/htf.py

не изменяется.

В нём остаются два legacy-вызова:

ExchangeService().get_klines(...)

Это намеренное переходное состояние.

HTF должен быть переключён на канонический Candle отдельным Build после анализа его фактических контрактов и зависимостей.


Новый тестовый файл

Добавлен:

tests/unit/trading/market_analysis/test_market_analysis_service_candles.py

Изначально тестовый файл имел имя:

test_service_candles.py

Однако такое имя уже использовалось другим тестовым модулем:

tests/unit/integrations/exchange/test_service_candles.py

Из-за одинакового basename pytest обнаружил import mismatch.

Новый Market Analysis тест был переименован в:

test_market_analysis_service_candles.py

После переименования конфликт модулей устранён.


Покрытие специализированных тестов

Новый тестовый файл проверяет:

  1. вызов ExchangeService.get_candles() с точными аргументами;
  2. использование canonical symbol первой полученной свечи;
  3. использование исходного запрошенного symbol при пустом наборе;
  4. безопасный UNKNOWN при ошибке получения свечей;
  5. отклонение нечислового конечного значения NaN;
  6. преобразование Decimal-цен закрытия в float до передачи индикаторам.

Targeted tests

Выполнена команда:

python -m pytest -q \
  tests/unit/trading/market_analysis/test_market_analysis_service_candles.py

Результат:

5 passed in 0.12s

Regression-набор Market Analysis и стратегий

До финальной полной проверки был выполнен набор:

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

Результат:

27 passed in 0.14s

Полный regression suite

После устранения конфликта имён тестовых модулей выполнена команда:

python -m pytest -q

Результат:

794 passed in 2.97s

Регрессий не обнаружено.


Архитектурная проверка legacy get_klines()

Выполнена команда:

grep -RIn \
  --exclude-dir="__pycache__" \
  --exclude="*.pyc" \
  "\.get_klines(" \
  src/trading/market_analysis

Результат:

src/trading/market_analysis/htf.py:55:        batch = ExchangeService().get_klines(
src/trading/market_analysis/htf.py:153:        batch = ExchangeService().get_klines(

Таким образом:

  • основной MarketAnalysisService больше не использует get_klines();
  • остаются ровно два legacy-вызова;
  • оба находятся в htf.py;
  • их миграция отложена на отдельный Build.

Архитектурная проверка canonical get_candles()

Выполнена команда:

grep -RIn \
  --exclude-dir="__pycache__" \
  --exclude="*.pyc" \
  "\.get_candles(" \
  src/trading/market_analysis \
  tests/unit/trading/market_analysis

Production-вызов обнаружен в:

src/trading/market_analysis/service.py

Это подтверждает переключение основного orchestration-компонента на канонический API.


Проверка отсутствия legacy-моделей в MarketAnalysisService

Выполнена команда:

grep -nE \
  "Kline|KlineBatch|batch\.candles|batch\.symbol" \
  src/trading/market_analysis/service.py

Вывод отсутствует.

Это подтверждает отсутствие в основном orchestration-файле зависимостей от:

Kline
KlineBatch
batch.candles
batch.symbol

Проверка форматирования

Выполнена команда:

git diff --check

Вывод отсутствует.

Whitespace-ошибок не обнаружено.


Фактический diff Build 050

Перед подготовкой документации состояние изменений:

app/src/trading/market_analysis/service.py | 41 +++++++++++++++++++++++++++++++----------
1 file changed, 31 insertions(+), 10 deletions(-)

Дополнительно добавлен новый тестовый файл:

tests/unit/trading/market_analysis/test_market_analysis_service_candles.py

Документация Build добавляется как:

docs/migrations/build_050.md

Архитектурный результат

После Build 050 основной Market Analysis orchestration имеет следующий путь:

MarketAnalysisService.analyze()
    ↓
ExchangeService.get_candles()
    ↓
tuple[Candle, ...]
    ↓
canonical Candle consumers
    ↓
explicit Decimal → float boundary for closes
    ↓
Market Analysis calculations

Переходный HTF-путь пока остаётся:

htf.py
    ↓
ExchangeService.get_klines()
    ↓
KlineBatch

Такое разделение позволяет продолжить миграцию поэтапно без удаления рабочего compatibility-кода.


Что намеренно не выполнено

Build 050 намеренно не включает:

  • переключение htf.py на get_candles();
  • удаление ExchangeService.get_klines();
  • удаление Kline;
  • удаление KlineBatch;
  • удаление _kline_from_candle();
  • изменение Market Data Acquisition;
  • изменение алгоритмов анализа;
  • изменение торговой логики;
  • изменение scoring;
  • изменение порогов;
  • изменение HTF-логики;
  • изменение структуры каталогов;
  • удаление рабочего legacy-кода.

Критерии завершения

Build 050 считается завершённым, поскольку:

  • MarketAnalysisService переключён на ExchangeService.get_candles();
  • основной orchestration получает tuple[Candle, ...];
  • зависимость от KlineBatch в service.py устранена;
  • обращения batch.candles устранены;
  • обращения batch.symbol устранены;
  • canonical symbol берётся из первой свечи;
  • для пустого набора сохраняется исходный symbol;
  • Decimal close prices явно преобразуются в float;
  • невалидные и не конечные close prices безопасно отклоняются;
  • новый контракт покрыт специализированными тестами;
  • targeted tests проходят;
  • regression-набор Market Analysis и стратегий проходит;
  • полный suite проходит;
  • legacy-вызовы остаются только в htf.py;
  • git diff --check чистый.

Итог

Build 050 завершён успешно.

Текущее состояние:

MarketAnalysisService → get_candles() → tuple[Candle, ...]
HTF                  → get_klines()  → KlineBatch

Результаты проверок:

Targeted tests: 5 passed
Market Analysis + strategies: 27 passed
Full test suite: 794 passed
git diff --check: clean

Следующий безопасный этап — отдельная миграция двух оставшихся HTF-вызовов с:

ExchangeService.get_klines()

на:

ExchangeService.get_candles()

без одновременного удаления legacy compatibility-контракта.