build 047: switch ExchangeService klines to canonical candles feed
This commit is contained in:
573
docs/migrations/build_047.md
Normal file
573
docs/migrations/build_047.md
Normal file
@@ -0,0 +1,573 @@
|
||||
# Build 047 — Переключение `ExchangeService.get_klines()` на канонический Candles Feed
|
||||
|
||||
**Статус:** Completed
|
||||
**Дата:** 2026-07-15
|
||||
**Подсистема:** Market Data Acquisition / OHLCV Feed
|
||||
**Тип изменения:** Миграция legacy-потребителя на канонический Acquisition pipeline
|
||||
|
||||
---
|
||||
|
||||
## 1. Цель Build
|
||||
|
||||
Цель Build 047 — переключить существующий публичный метод:
|
||||
|
||||
```text
|
||||
ExchangeService.get_klines()
|
||||
```
|
||||
|
||||
с прямого получения и самостоятельной обработки ответа Dzengi `/api/v1/klines` на канонический pipeline подсистемы:
|
||||
|
||||
```text
|
||||
src/market_data/acquisition/
|
||||
```
|
||||
|
||||
При этом необходимо сохранить существующий публичный контракт `ExchangeService.get_klines()` и обратную совместимость с текущими потребителями legacy-слоя.
|
||||
|
||||
---
|
||||
|
||||
## 2. Исходное состояние
|
||||
|
||||
До Build 047 метод:
|
||||
|
||||
```text
|
||||
ExchangeService.get_klines()
|
||||
```
|
||||
|
||||
самостоятельно:
|
||||
|
||||
1. выполнял прямой REST-запрос к `/api/v1/klines`;
|
||||
2. извлекал элементы ответа;
|
||||
3. разбирал отдельные свечи;
|
||||
4. создавал legacy-модели `Kline`;
|
||||
5. формировал `KlineBatch`.
|
||||
|
||||
В `ExchangeService` существовали собственные legacy-функции обработки свечей:
|
||||
|
||||
```text
|
||||
_parse_klines_payload()
|
||||
_extract_klines_items()
|
||||
_parse_kline_item()
|
||||
```
|
||||
|
||||
Таким образом, после появления канонического Candles Feed в системе существовали два независимых пути получения и обработки OHLCV-данных.
|
||||
|
||||
Это создавало дублирование ответственности между:
|
||||
|
||||
```text
|
||||
src/integrations/exchange/
|
||||
```
|
||||
|
||||
и:
|
||||
|
||||
```text
|
||||
src/market_data/acquisition/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Границы Build
|
||||
|
||||
В Build 047 выполнено:
|
||||
|
||||
1. переключение внутренней реализации `ExchangeService.get_klines()` на канонический Candles Feed;
|
||||
2. сохранение публичного контракта `ExchangeService.get_klines()`;
|
||||
3. сохранение legacy-моделей `Kline` и `KlineBatch`;
|
||||
4. добавление внутреннего адаптационного преобразования `Candle -> Kline`;
|
||||
5. удаление прямого REST-запроса `/api/v1/klines` из `ExchangeService`;
|
||||
6. удаление legacy-функций:
|
||||
- `_parse_klines_payload()`;
|
||||
- `_extract_klines_items()`;
|
||||
- `_parse_kline_item()`;
|
||||
7. добавление специализированных unit-тестов для `ExchangeService.get_klines()` и его интеграции с каноническим Acquisition pipeline.
|
||||
|
||||
В Build 047 не выполнялось:
|
||||
|
||||
- изменение публичного контракта `ExchangeService.get_klines()`;
|
||||
- изменение legacy-потребителей в `trading/market_analysis`;
|
||||
- удаление моделей `Kline` и `KlineBatch`;
|
||||
- прямое переключение `trading/market_analysis` на `CandlesAcquisitionService`;
|
||||
- изменение архитектуры других Market Data feeds;
|
||||
- изменение runtime-компонентов Acquisition.
|
||||
|
||||
---
|
||||
|
||||
## 4. Изменённые production-файлы
|
||||
|
||||
### 4.1. `src/integrations/exchange/service.py`
|
||||
|
||||
Метод:
|
||||
|
||||
```text
|
||||
ExchangeService.get_klines()
|
||||
```
|
||||
|
||||
сохранён как публичный compatibility-контракт.
|
||||
|
||||
Внутренний путь получения данных изменён.
|
||||
|
||||
До Build 047:
|
||||
|
||||
```text
|
||||
ExchangeService.get_klines()
|
||||
↓
|
||||
прямой REST-запрос /api/v1/klines
|
||||
↓
|
||||
_parse_klines_payload()
|
||||
↓
|
||||
_extract_klines_items()
|
||||
↓
|
||||
_parse_kline_item()
|
||||
↓
|
||||
Kline
|
||||
↓
|
||||
KlineBatch
|
||||
```
|
||||
|
||||
После Build 047:
|
||||
|
||||
```text
|
||||
ExchangeService.get_klines()
|
||||
↓
|
||||
_load_candles_via_acquisition()
|
||||
↓
|
||||
DzengiCandlesDocumentSource
|
||||
↓
|
||||
DzengiCandlesDocumentHandler
|
||||
↓
|
||||
CandlesFeed
|
||||
↓
|
||||
CandlesFeedRegistry
|
||||
↓
|
||||
CandlesAcquisitionService
|
||||
↓
|
||||
Candle
|
||||
↓
|
||||
_kline_from_candle()
|
||||
↓
|
||||
Kline
|
||||
↓
|
||||
KlineBatch
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Канонический путь получения свечей
|
||||
|
||||
После Build 047 единственный production-путь прямого обращения к endpoint:
|
||||
|
||||
```text
|
||||
/api/v1/klines
|
||||
```
|
||||
|
||||
расположен в:
|
||||
|
||||
```text
|
||||
src/market_data/acquisition/adapters/dzengi/rest.py
|
||||
```
|
||||
|
||||
Это соответствует архитектурному разделению ответственности:
|
||||
|
||||
```text
|
||||
Exchange-specific transport
|
||||
↓
|
||||
Market Data Acquisition
|
||||
↓
|
||||
Canonical Candle
|
||||
↓
|
||||
Legacy compatibility boundary
|
||||
↓
|
||||
Kline / KlineBatch
|
||||
```
|
||||
|
||||
`ExchangeService` больше не владеет транспортной логикой получения OHLCV-данных от Dzengi.
|
||||
|
||||
---
|
||||
|
||||
## 6. Добавленные внутренние методы
|
||||
|
||||
### 6.1. `_load_candles_via_acquisition()`
|
||||
|
||||
Добавлен внутренний compatibility-helper:
|
||||
|
||||
```text
|
||||
ExchangeService._load_candles_via_acquisition()
|
||||
```
|
||||
|
||||
Его ответственность:
|
||||
|
||||
1. создать `DzengiCandlesDocumentSource`;
|
||||
2. создать `DzengiCandlesDocumentHandler`;
|
||||
3. создать `CandlesFeed`;
|
||||
4. зарегистрировать feed в `CandlesFeedRegistry`;
|
||||
5. создать `CandlesAcquisitionService`;
|
||||
6. вызвать канонический `load_candles()`;
|
||||
7. вернуть immutable-набор моделей `Candle`.
|
||||
|
||||
Метод является внутренней границей между legacy `ExchangeService` и канонической подсистемой Market Data Acquisition.
|
||||
|
||||
### 6.2. `_kline_from_candle()`
|
||||
|
||||
Добавлен внутренний адаптер:
|
||||
|
||||
```text
|
||||
ExchangeService._kline_from_candle()
|
||||
```
|
||||
|
||||
Преобразование выполняется по следующему правилу:
|
||||
|
||||
```text
|
||||
Candle.symbol -> Kline.symbol
|
||||
Candle.interval -> Kline.interval
|
||||
Candle.open_time -> Kline.open_time
|
||||
Candle.open_price -> Kline.open_price
|
||||
Candle.high_price -> Kline.high_price
|
||||
Candle.low_price -> Kline.low_price
|
||||
Candle.close_price -> Kline.close_price
|
||||
Candle.volume -> Kline.volume
|
||||
```
|
||||
|
||||
Это позволяет сохранить существующий legacy-контракт без переноса legacy-моделей внутрь `market_data/acquisition`.
|
||||
|
||||
---
|
||||
|
||||
## 7. Удалённая legacy-логика
|
||||
|
||||
Из:
|
||||
|
||||
```text
|
||||
src/integrations/exchange/service.py
|
||||
```
|
||||
|
||||
удалены:
|
||||
|
||||
```text
|
||||
_parse_klines_payload()
|
||||
_extract_klines_items()
|
||||
_parse_kline_item()
|
||||
```
|
||||
|
||||
Также удалён прямой вызов:
|
||||
|
||||
```text
|
||||
/api/v1/klines
|
||||
```
|
||||
|
||||
из `ExchangeService`.
|
||||
|
||||
После Build 047 parsing, schema validation, value validation и mapping OHLCV-данных выполняются только канонической подсистемой:
|
||||
|
||||
```text
|
||||
src/market_data/acquisition/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Сохранённая обратная совместимость
|
||||
|
||||
Публичный контракт:
|
||||
|
||||
```text
|
||||
ExchangeService.get_klines(...)
|
||||
```
|
||||
|
||||
сохранён.
|
||||
|
||||
Существующие production-потребители не изменялись:
|
||||
|
||||
```text
|
||||
src/trading/market_analysis/service.py
|
||||
src/trading/market_analysis/htf.py
|
||||
```
|
||||
|
||||
Они продолжают работать через:
|
||||
|
||||
```text
|
||||
ExchangeService.get_klines()
|
||||
```
|
||||
|
||||
и получать:
|
||||
|
||||
```text
|
||||
KlineBatch
|
||||
```
|
||||
|
||||
Таким образом, Build 047 меняет внутренний источник данных, но не требует одновременного переписывания legacy-потребителей.
|
||||
|
||||
---
|
||||
|
||||
## 9. Добавленные тесты
|
||||
|
||||
Добавлен специализированный файл:
|
||||
|
||||
```text
|
||||
tests/unit/integrations/exchange/test_service_klines.py
|
||||
```
|
||||
|
||||
Тесты покрывают:
|
||||
|
||||
- использование `default_symbol`;
|
||||
- нормализацию `limit`;
|
||||
- нормализацию `interval`;
|
||||
- отклонение неподдерживаемого interval;
|
||||
- передачу `price_type`;
|
||||
- обработку выключенной биржи;
|
||||
- обработку неизвестного символа;
|
||||
- передачу параметров в Acquisition pipeline;
|
||||
- преобразование `Candle` в `Kline`;
|
||||
- формирование `KlineBatch`;
|
||||
- обработку пустого набора свечей;
|
||||
- обработку исключений канонического Acquisition pipeline;
|
||||
- построение полного composition pipeline;
|
||||
- сохранение публичного legacy-контракта.
|
||||
|
||||
Результат targeted-проверки:
|
||||
|
||||
```text
|
||||
26 passed in 0.17s
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Полная регрессионная проверка
|
||||
|
||||
Выполнено:
|
||||
|
||||
```bash
|
||||
python -m pytest -q
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
750 passed in 2.83s
|
||||
```
|
||||
|
||||
Регрессий не обнаружено.
|
||||
|
||||
---
|
||||
|
||||
## 11. Контроль прямого использования `/api/v1/klines`
|
||||
|
||||
Команда:
|
||||
|
||||
```bash
|
||||
grep -RIn \
|
||||
--exclude-dir="__pycache__" \
|
||||
--exclude="*.pyc" \
|
||||
'"/api/v1/klines"' \
|
||||
src
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
src/market_data/acquisition/adapters/dzengi/rest.py:17:_KLINES_PATH = "/api/v1/klines"
|
||||
```
|
||||
|
||||
Вывод:
|
||||
|
||||
Прямое знание endpoint `/api/v1/klines` находится только в каноническом Dzengi REST-адаптере.
|
||||
|
||||
---
|
||||
|
||||
## 12. Контроль удаления legacy-парсеров
|
||||
|
||||
Команда:
|
||||
|
||||
```bash
|
||||
grep -RIn \
|
||||
--exclude-dir="__pycache__" \
|
||||
--exclude="*.pyc" \
|
||||
"_parse_klines_payload\|_extract_klines_items\|_parse_kline_item" \
|
||||
src tests
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
пусто
|
||||
```
|
||||
|
||||
Вывод:
|
||||
|
||||
Legacy-функции обработки kline payload полностью удалены.
|
||||
|
||||
---
|
||||
|
||||
## 13. Контроль оставшихся потребителей `ExchangeService.get_klines()`
|
||||
|
||||
Команда:
|
||||
|
||||
```bash
|
||||
grep -RIn \
|
||||
--exclude-dir="__pycache__" \
|
||||
--exclude="*.pyc" \
|
||||
"\.get_klines(" \
|
||||
src tests
|
||||
```
|
||||
|
||||
Production-потребители:
|
||||
|
||||
```text
|
||||
src/trading/market_analysis/service.py
|
||||
src/trading/market_analysis/htf.py
|
||||
```
|
||||
|
||||
Дополнительно присутствуют специализированные вызовы в:
|
||||
|
||||
```text
|
||||
tests/unit/integrations/exchange/test_service_klines.py
|
||||
```
|
||||
|
||||
Вывод:
|
||||
|
||||
Публичный compatibility-контракт `ExchangeService.get_klines()` продолжает обслуживать существующие legacy-потребители.
|
||||
|
||||
---
|
||||
|
||||
## 14. Контроль интеграции с Acquisition pipeline
|
||||
|
||||
Команда:
|
||||
|
||||
```bash
|
||||
grep -RIn \
|
||||
--exclude-dir="__pycache__" \
|
||||
--exclude="*.pyc" \
|
||||
"CandlesAcquisitionService\|CandlesFeedRegistry\|DzengiCandlesDocumentSource" \
|
||||
src/integrations/exchange/service.py
|
||||
```
|
||||
|
||||
Результат подтверждает использование:
|
||||
|
||||
```text
|
||||
DzengiCandlesDocumentSource
|
||||
CandlesFeedRegistry
|
||||
CandlesAcquisitionService
|
||||
```
|
||||
|
||||
внутри compatibility boundary `ExchangeService`.
|
||||
|
||||
---
|
||||
|
||||
## 15. Контроль качества diff
|
||||
|
||||
Выполнено:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
пусто
|
||||
```
|
||||
|
||||
Whitespace-ошибок не обнаружено.
|
||||
|
||||
---
|
||||
|
||||
## 16. Архитектурный результат
|
||||
|
||||
После Build 047 путь OHLCV-данных имеет следующую структуру:
|
||||
|
||||
```text
|
||||
Dzengi REST API
|
||||
↓
|
||||
DzengiCandlesDocumentSource
|
||||
↓
|
||||
schema validation
|
||||
↓
|
||||
parser
|
||||
↓
|
||||
value validation
|
||||
↓
|
||||
mapper
|
||||
↓
|
||||
Candle
|
||||
↓
|
||||
CandlesFeed
|
||||
↓
|
||||
CandlesFeedRegistry
|
||||
↓
|
||||
CandlesAcquisitionService
|
||||
↓
|
||||
ExchangeService compatibility boundary
|
||||
↓
|
||||
Kline / KlineBatch
|
||||
↓
|
||||
legacy trading consumers
|
||||
```
|
||||
|
||||
Главный архитектурный результат:
|
||||
|
||||
```text
|
||||
ExchangeService больше не получает и не разбирает
|
||||
сырой ответ /api/v1/klines самостоятельно.
|
||||
```
|
||||
|
||||
Владение получением и канонической обработкой OHLCV-данных теперь принадлежит:
|
||||
|
||||
```text
|
||||
src/market_data/acquisition/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 17. Состояние после Build 047
|
||||
|
||||
На момент завершения Build:
|
||||
|
||||
```text
|
||||
Targeted tests: 26 passed
|
||||
Full test suite: 750 passed
|
||||
git diff --check: clean
|
||||
```
|
||||
|
||||
Прямой endpoint:
|
||||
|
||||
```text
|
||||
/api/v1/klines
|
||||
```
|
||||
|
||||
остался только в:
|
||||
|
||||
```text
|
||||
src/market_data/acquisition/adapters/dzengi/rest.py
|
||||
```
|
||||
|
||||
Legacy parsing helpers:
|
||||
|
||||
```text
|
||||
_parse_klines_payload
|
||||
_extract_klines_items
|
||||
_parse_kline_item
|
||||
```
|
||||
|
||||
полностью отсутствуют.
|
||||
|
||||
Публичный контракт:
|
||||
|
||||
```text
|
||||
ExchangeService.get_klines()
|
||||
```
|
||||
|
||||
сохранён.
|
||||
|
||||
---
|
||||
|
||||
## 18. Итог Build
|
||||
|
||||
**Build 047 завершён успешно.**
|
||||
|
||||
Канонический Candles Feed интегрирован в существующий `ExchangeService.get_klines()` без изменения его публичного контракта.
|
||||
|
||||
Дублирующая legacy-логика получения и разбора `/api/v1/klines` удалена.
|
||||
|
||||
Существующие потребители продолжают работать без изменений.
|
||||
|
||||
Полная регрессионная проверка подтверждает отсутствие нарушений существующего поведения:
|
||||
|
||||
```text
|
||||
750 passed
|
||||
```
|
||||
Reference in New Issue
Block a user