17 KiB
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
После переименования конфликт модулей устранён.
Покрытие специализированных тестов
Новый тестовый файл проверяет:
- вызов
ExchangeService.get_candles()с точными аргументами; - использование canonical symbol первой полученной свечи;
- использование исходного запрошенного symbol при пустом наборе;
- безопасный
UNKNOWNпри ошибке получения свечей; - отклонение нечислового конечного значения
NaN; - преобразование
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;
Decimalclose 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-контракта.