Files
dzentra_bot/docs/migrations/build_045.md

692 lines
17 KiB
Markdown
Raw 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 045 — регистрация канонического Candles Feed
## Статус
**Завершён**
---
## Цель Build
Продолжить поэтапную миграцию **OHLCV Feed** в утверждённую архитектуру `Market Data Acquisition` после завершения фундаментального слоя Candles Feed в Build 044.
Build 045 добавляет реестр канонических источников свечей и завершает следующий минимальный архитектурный шаг:
```text
Candles Feed
CandlesFeedRegistry
```
При этом:
- существующий runtime не переключается;
- `ExchangeService.get_klines()` не изменяется;
- legacy-путь получения свечей продолжает работать;
- новая архитектура остаётся изолированной от существующего торгового runtime;
- обратная совместимость полностью сохраняется.
---
## Исходное состояние
До Build 045 проект уже содержал фундамент канонического Candles Feed, созданный в Build 044:
```text
REST /api/v1/klines
DzengiCandlesDocumentSource
validate_candles_schema()
parse_candles()
validate_candles_values()
map_candles()
Candle
DzengiCandlesDocumentHandler
CandlesFeed
```
Также существовали:
- каноническая модель `Candle`;
- транспортная raw-модель Dzengi для свечей;
- REST document source;
- schema validation;
- parser;
- value validation;
- mapper;
- document handler;
- `CandlesFeed`;
- протоколы Candles Feed;
- специализированные исключения;
- unit-тесты фундаментального слоя.
Однако отсутствовал реестр, позволяющий регистрировать и выбирать конкретный `CandlesFeed` по имени источника.
---
## Объём изменений
Build 045 изменяет только следующие файлы:
```text
src/market_data/acquisition/exceptions.py
src/market_data/acquisition/registry.py
tests/unit/market_data/acquisition/test_registry.py
docs/migrations/build_045.md
```
Build не изменяет:
```text
src/market_data/acquisition/service.py
src/integrations/exchange/service.py
src/integrations/exchange/models.py
src/trading/
src/telegram/
```
---
## 1. Исключение CandleFeedRegistryError
В файл:
```text
src/market_data/acquisition/exceptions.py
```
добавлено специализированное исключение:
```python
class CandleFeedRegistryError(MarketDataAcquisitionError):
pass
```
Назначение исключения — изолировать ошибки регистрации и получения Candles Feed от других ошибок подсистемы `Market Data Acquisition`.
Иерархия:
```text
MarketDataAcquisitionError
CandleFeedRegistryError
```
---
## 2. Реестр CandlesFeedRegistry
В файл:
```text
src/market_data/acquisition/registry.py
```
добавлен класс:
```text
CandlesFeedRegistry
```
Реестр отвечает исключительно за регистрацию и получение реализаций:
```text
CandlesFeedProtocol
```
Реестр не выполняет:
- REST-запросы;
- schema validation;
- parsing;
- value validation;
- mapping;
- хранение свечей;
- управление runtime;
- переключение legacy-потребителей.
---
## 3. Контракт реестра
`CandlesFeedRegistry` предоставляет только операции регистрации и получения Candles Feed.
Публичный контракт:
```text
register()
get()
```
Внутренняя нормализация имени источника выполняется методом:
```text
_normalize_source_name()
```
Концептуально:
```text
source name
CandlesFeedRegistry
CandlesFeedProtocol
```
Пример:
```text
"dzengi"
CandlesFeedRegistry
CandlesFeed
```
Реестр принимает только объекты, соответствующие каноническому контракту:
```text
CandlesFeedProtocol
```
---
## 4. Нормализация имени источника
Имена источников нормализуются перед использованием.
Поддерживается удаление внешних пробелов:
```text
"dzengi"
" dzengi "
```
После нормализации оба значения соответствуют одному ключу:
```text
dzengi
```
Пустые имена источников не допускаются.
Регистр символов сохраняется. Имена источников являются case-sensitive:
```text
"dzengi"
```
и:
```text
"DZENGI"
```
считаются разными именами источников.
---
## 5. Защита от некорректной регистрации
`CandlesFeedRegistry` отклоняет:
- пустое имя источника;
- имя, состоящее только из пробелов;
- объект, не соответствующий `CandlesFeedProtocol`;
- повторную регистрацию уже существующего нормализованного имени источника.
Ошибки представлены через:
```text
CandleFeedRegistryError
```
---
## 6. Защита от повторной регистрации
Повторная регистрация одного и того же нормализованного имени источника запрещена.
Пример:
```text
register("dzengi", feed_a)
dzengi → feed_a
```
Повторная регистрация:
```text
register("dzengi", feed_b)
CandleFeedRegistryError
```
Исходный Feed при ошибке повторной регистрации не заменяется.
Для замены зарегистрированного Feed требуется отдельное явное изменение архитектурного контракта. В Build 045 такая возможность не добавлялась.
---
## 7. Получение зарегистрированного Feed
Зарегистрированный Candles Feed может быть получен по имени источника.
Концептуально:
```text
registry.get("dzengi")
CandlesFeedProtocol
```
При успешном получении реестр возвращает тот же объект Feed, который был ранее зарегистрирован:
```text
feed = CandlesFeed(...)
registry.register("dzengi", feed)
registry.get("dzengi") is feed
True
```
Попытка получить неизвестный источник приводит к:
```text
CandleFeedRegistryError
```
---
## 8. Ограниченный публичный контракт
Публичный контракт `CandlesFeedRegistry` в Build 045 ограничен двумя операциями:
```text
register()
get()
```
В Build 045 намеренно не добавлены:
```text
contains()
remove()
replace()
clear()
```
Реестр не предоставляет управление жизненным циклом зарегистрированных Feed и не выполняет их запуск или остановку.
Такое ограничение соответствует принципу минимального безопасного Build: добавляется только функциональность, необходимая для последующей интеграции Candles Feed в сервисный слой.
---
## 9. Изоляция от Acquisition Service
В рамках Build 045 намеренно не добавлялись:
```text
CandlesAcquisitionService
```
или метод:
```text
load_candles()
```
в существующий:
```text
src/market_data/acquisition/service.py
```
Архитектурная проверка:
```bash
grep -RIn \
--exclude-dir="__pycache__" \
--exclude="*.pyc" \
"CandlesAcquisitionService\|load_candles" \
src/market_data/acquisition/service.py
```
Результат:
```text
пусто
```
Это подтверждает, что Build 045 ограничен добавлением реестра и не выполняет преждевременную интеграцию сервисного слоя.
---
## 10. Изоляция от legacy runtime
Build 045 не изменяет существующий runtime-путь получения свечей:
```text
Trading / Market Analysis
ExchangeService.get_klines()
legacy parsing
KlineBatch
```
После Build 045 этот путь продолжает работать без изменений.
Новый канонический путь пока существует параллельно:
```text
DzengiCandlesDocumentSource
DzengiCandlesDocumentHandler
CandlesFeed
CandlesFeedRegistry
```
Таким образом, в системе временно существуют два пути:
```text
Legacy runtime path
+
Canonical Candles Feed path
```
Это ожидаемое переходное состояние безопасной поэтапной миграции.
---
## 11. Unit-тесты
Расширен файл:
```text
tests/unit/market_data/acquisition/test_registry.py
```
Добавлено покрытие для:
- создания `CandlesFeedRegistry`;
- регистрации Candles Feed;
- получения зарегистрированного Feed;
- нормализации внешних пробелов имени источника;
- case-sensitive поведения имён источников;
- отклонения пустого имени;
- отклонения имени, состоящего только из пробелов;
- отклонения объекта, не соответствующего `CandlesFeedProtocol`;
- защиты от повторной регистрации;
- проверки, что повторная регистрация не заменяет исходный Feed;
- получения неизвестного источника;
- сохранения identity зарегистрированного Feed;
- отсутствия вызова `load_candles()` при регистрации Feed;
- отсутствия вызова `load_candles()` при получении Feed;
- соответствия зарегистрированного объекта `CandlesFeedProtocol`;
- корректной работы специализированного `CandleFeedRegistryError`.
---
## 12. Результаты targeted-тестов
Выполнена команда:
```bash
python -m pytest -q \
tests/unit/market_data/acquisition/test_registry.py
```
Результат:
```text
61 passed in 0.04s
```
---
## 13. Результаты полного набора тестов
Выполнена команда:
```bash
python -m pytest -q
```
Результат:
```text
707 passed in 2.87s
```
Все unit-тесты проекта проходят успешно.
---
## 14. Архитектурная проверка реестра
Выполнена команда:
```bash
grep -RIn \
--exclude-dir="__pycache__" \
--exclude="*.pyc" \
"CandlesFeedRegistry\|CandleFeedRegistryError" \
src tests
```
Результат подтверждает, что новые сущности находятся только в ожидаемых областях:
```text
src/market_data/acquisition/registry.py
src/market_data/acquisition/exceptions.py
tests/unit/market_data/acquisition/test_registry.py
```
Неожиданных зависимостей не обнаружено.
---
## 15. Проверка отсутствия преждевременной сервисной интеграции
Выполнена команда:
```bash
grep -RIn \
--exclude-dir="__pycache__" \
--exclude="*.pyc" \
"CandlesAcquisitionService\|load_candles" \
src/market_data/acquisition/service.py
```
Результат:
```text
пусто
```
Это подтверждает, что Build 045 не расширяет `MarketDataAcquisitionService` и не переключает runtime.
---
## 16. Проверка синтаксиса
Выполнена команда:
```bash
python -m compileall \
src/market_data/acquisition/exceptions.py \
src/market_data/acquisition/registry.py \
tests/unit/market_data/acquisition/test_registry.py
```
Результат:
```text
успешно
```
Ошибок синтаксиса не обнаружено.
---
## 17. Проверка форматирования diff
Первоначальная проверка:
```bash
git diff --check
```
обнаружила одну лишнюю пустую строку в конце:
```text
app/tests/unit/market_data/acquisition/test_registry.py:691: new blank line at EOF.
```
После удаления лишней пустой строки необходимо повторно выполнить:
```bash
git diff --check
```
Финальный ожидаемый результат:
```text
пустой вывод
```
До создания commit эта проверка должна быть подтверждена.
---
## 18. Итоговая архитектура после Build 045
После завершения Build 045 канонический Candles Feed имеет следующую структуру:
```text
Dzengi REST /api/v1/klines
DzengiCandlesDocumentSource
validate_candles_schema()
parse_candles()
validate_candles_values()
map_candles()
Candle
DzengiCandlesDocumentHandler
CandlesFeed
CandlesFeedRegistry
```
При этом существующий production/runtime-путь остаётся неизменным:
```text
Trading / Market Analysis
ExchangeService.get_klines()
legacy parsing
KlineBatch
```
---
## 19. Что намеренно не входит в Build 045
Build 045 намеренно не выполняет:
- интеграцию Candles Feed в `MarketDataAcquisitionService`;
- изменение `ExchangeService.get_klines()`;
- изменение `Kline`;
- изменение `KlineBatch`;
- переключение `trading/market_analysis`;
- переключение HTF-анализа;
- удаление legacy parser свечей;
- добавление постоянного Candle Store;
- изменение runtime;
- изменение Telegram UI;
- изменение торговой логики.
Эти ограничения являются частью стратегии безопасной миграции.
---
## 20. Следующий Build
Следующий минимальный безопасный этап:
```text
Build 046 — интеграция канонического Candles Feed в MarketDataAcquisitionService
```
Предполагаемая цель:
```text
CandlesFeedRegistry
MarketDataAcquisitionService
load_candles(...)
tuple[Candle, ...]
```
Build 046 должен:
- добавить поддержку `CandlesFeedRegistry` в сервисный слой `Market Data Acquisition`;
- добавить публичный метод загрузки свечей через канонический сервис;
- сохранить существующие Instrument Reference Data и Quotes Feed без изменений;
- не переключать `ExchangeService.get_klines()`;
- не удалять legacy `Kline` и `KlineBatch`;
- не изменять торговую логику;
- сохранить полную обратную совместимость.
---
## Итог
Build 045 завершает регистрацию канонического Candles Feed.
Достигнуто состояние:
```text
OHLCV Feed foundation
✓ Candle model
✓ REST document source
✓ schema validation
✓ parser
✓ value validation
✓ mapper
✓ document handler
✓ Candles Feed
✓ CandlesFeedRegistry
☐ Acquisition Service integration
☐ ExchangeService compatibility bridge
☐ Consumer migration
☐ Legacy Kline removal
```
Build 045 является небольшим изолированным архитектурным шагом и не изменяет поведение существующего рабочего торгового бота.