573 lines
14 KiB
Markdown
573 lines
14 KiB
Markdown
# 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
|
||
``` |