# 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 с сохранением существующего поведения системы.