Files
dzentra_bot/docs/migrations/build_050.md

578 lines
17 KiB
Markdown
Raw Permalink 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 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-контракта.