feat: add market data architecture and complete migration through build 039
This commit is contained in:
890
docs/migrations/build_014.md
Normal file
890
docs/migrations/build_014.md
Normal file
@@ -0,0 +1,890 @@
|
||||
# Dzentra — Instrument Reference Data Migration — Build 014
|
||||
|
||||
> Статус: Завершён
|
||||
|
||||
## Название
|
||||
|
||||
**Compatibility mapper Instrument → ExchangeSymbol**
|
||||
|
||||
---
|
||||
|
||||
## Цель
|
||||
|
||||
Создать временный compatibility-слой, преобразующий новую внутреннюю модель:
|
||||
|
||||
```text
|
||||
Instrument
|
||||
```
|
||||
|
||||
в существующую legacy-модель:
|
||||
|
||||
```text
|
||||
ExchangeSymbol
|
||||
```
|
||||
|
||||
Целевая цепочка:
|
||||
|
||||
```text
|
||||
new acquisition pipeline
|
||||
↓
|
||||
tuple[Instrument, ...]
|
||||
↓
|
||||
compatibility mapper
|
||||
↓
|
||||
list[ExchangeSymbol]
|
||||
```
|
||||
|
||||
Compatibility mapper необходим для последующего переключения:
|
||||
|
||||
```text
|
||||
ExchangeService.get_exchange_symbols()
|
||||
```
|
||||
|
||||
на новую Instrument Reference Data pipeline без изменения существующего публичного контракта:
|
||||
|
||||
```python
|
||||
def get_exchange_symbols(self) -> list[ExchangeSymbol]:
|
||||
...
|
||||
```
|
||||
|
||||
Build 014 создаёт только compatibility-границу.
|
||||
|
||||
Переключение `ExchangeService.get_exchange_symbols()` в рамках этого Build не выполняется.
|
||||
|
||||
---
|
||||
|
||||
## Причина создания compatibility-слоя
|
||||
|
||||
К началу Build 014 завершены:
|
||||
|
||||
```text
|
||||
Build 001–012
|
||||
↓
|
||||
создана новая независимая Instrument Reference Data acquisition pipeline
|
||||
|
||||
Build 013
|
||||
↓
|
||||
доказана эквивалентность legacy- и новой реализации
|
||||
на одном и том же реальном exchangeInfo sample
|
||||
```
|
||||
|
||||
Новая pipeline возвращает:
|
||||
|
||||
```python
|
||||
tuple[Instrument, ...]
|
||||
```
|
||||
|
||||
Существующий legacy-контракт возвращает:
|
||||
|
||||
```python
|
||||
list[ExchangeSymbol]
|
||||
```
|
||||
|
||||
Существующие production-потребители продолжают ожидать:
|
||||
|
||||
```text
|
||||
ExchangeSymbol
|
||||
```
|
||||
|
||||
Поэтому прямое переключение невозможно без временного преобразования:
|
||||
|
||||
```text
|
||||
Instrument
|
||||
↓
|
||||
ExchangeSymbol
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Реализованные файлы
|
||||
|
||||
Создан production-файл:
|
||||
|
||||
```text
|
||||
app/src/market_data/acquisition/compatibility.py
|
||||
```
|
||||
|
||||
Создан unit-тест:
|
||||
|
||||
```text
|
||||
app/tests/unit/market_data/acquisition/test_compatibility.py
|
||||
```
|
||||
|
||||
Другие файлы в рамках Build 014 не изменялись.
|
||||
|
||||
---
|
||||
|
||||
## Размещение compatibility mapper
|
||||
|
||||
Compatibility mapper размещён в:
|
||||
|
||||
```text
|
||||
app/src/market_data/acquisition/compatibility.py
|
||||
```
|
||||
|
||||
Он намеренно не размещён в:
|
||||
|
||||
```text
|
||||
app/src/market_data/acquisition/adapters/dzengi/
|
||||
```
|
||||
|
||||
поскольку преобразование:
|
||||
|
||||
```text
|
||||
Instrument → ExchangeSymbol
|
||||
```
|
||||
|
||||
не зависит от формата Dzengi.
|
||||
|
||||
Он также не размещён в:
|
||||
|
||||
```text
|
||||
app/src/integrations/exchange/
|
||||
```
|
||||
|
||||
поскольку новый миграционный код не должен расширять legacy-подсистему.
|
||||
|
||||
Архитектурная граница имеет следующий вид:
|
||||
|
||||
```text
|
||||
new acquisition model
|
||||
↓
|
||||
compatibility.py
|
||||
↓
|
||||
legacy integration model
|
||||
```
|
||||
|
||||
Compatibility mapper является временным слоем и должен быть удалён после полного перевода production-потребителей:
|
||||
|
||||
```text
|
||||
Build 024 — Удаление compatibility-слоя
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Реализованные функции
|
||||
|
||||
Созданы две функции:
|
||||
|
||||
```python
|
||||
def map_instrument_to_exchange_symbol(
|
||||
instrument: Instrument,
|
||||
) -> ExchangeSymbol:
|
||||
...
|
||||
```
|
||||
|
||||
и:
|
||||
|
||||
```python
|
||||
def map_instruments_to_exchange_symbols(
|
||||
instruments: tuple[Instrument, ...],
|
||||
) -> list[ExchangeSymbol]:
|
||||
...
|
||||
```
|
||||
|
||||
Первая функция преобразует один объект:
|
||||
|
||||
```text
|
||||
Instrument
|
||||
↓
|
||||
ExchangeSymbol
|
||||
```
|
||||
|
||||
Вторая преобразует полный immutable-набор:
|
||||
|
||||
```text
|
||||
tuple[Instrument, ...]
|
||||
↓
|
||||
list[ExchangeSymbol]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Архитектурные зависимости
|
||||
|
||||
Compatibility mapper сознательно зависит от обеих моделей:
|
||||
|
||||
```python
|
||||
from src.integrations.exchange.models import ExchangeSymbol
|
||||
from src.market_data.acquisition.models.instrument import Instrument
|
||||
```
|
||||
|
||||
Это допустимая временная зависимость:
|
||||
|
||||
```text
|
||||
новая acquisition-подсистема
|
||||
↓
|
||||
compatibility boundary
|
||||
↓
|
||||
legacy contract
|
||||
```
|
||||
|
||||
Никакие другие компоненты новой acquisition pipeline не изменялись для добавления зависимости от `ExchangeSymbol`.
|
||||
|
||||
Re-export через:
|
||||
|
||||
```text
|
||||
app/src/market_data/acquisition/__init__.py
|
||||
```
|
||||
|
||||
не добавлялся.
|
||||
|
||||
---
|
||||
|
||||
## Соответствие полей
|
||||
|
||||
Compatibility mapper переносит все 11 полей, общих для `Instrument` и `ExchangeSymbol`:
|
||||
|
||||
```text
|
||||
Instrument.symbol
|
||||
→ ExchangeSymbol.symbol
|
||||
|
||||
Instrument.name
|
||||
→ ExchangeSymbol.name
|
||||
|
||||
Instrument.status
|
||||
→ ExchangeSymbol.status
|
||||
|
||||
Instrument.base_asset
|
||||
→ ExchangeSymbol.base_asset
|
||||
|
||||
Instrument.quote_asset
|
||||
→ ExchangeSymbol.quote_asset
|
||||
|
||||
Instrument.market_modes
|
||||
→ ExchangeSymbol.market_modes
|
||||
|
||||
Instrument.market_type
|
||||
→ ExchangeSymbol.market_type
|
||||
|
||||
Instrument.tick_size
|
||||
→ ExchangeSymbol.tick_size
|
||||
|
||||
Instrument.step_size
|
||||
→ ExchangeSymbol.step_size
|
||||
|
||||
Instrument.min_qty
|
||||
→ ExchangeSymbol.min_qty
|
||||
|
||||
Instrument.min_notional
|
||||
→ ExchangeSymbol.min_notional
|
||||
```
|
||||
|
||||
Никакая повторная предметная интерпретация данных в compatibility mapper не выполняется.
|
||||
|
||||
---
|
||||
|
||||
## Поля новой модели, не представленные в legacy-модели
|
||||
|
||||
Модель `Instrument` содержит дополнительные поля:
|
||||
|
||||
```text
|
||||
asset_type
|
||||
order_types
|
||||
base_asset_precision
|
||||
quote_asset_precision
|
||||
tick_value
|
||||
max_qty
|
||||
country
|
||||
sector
|
||||
industry
|
||||
trading_hours
|
||||
```
|
||||
|
||||
Эти поля отсутствуют в legacy-модели:
|
||||
|
||||
```text
|
||||
ExchangeSymbol
|
||||
```
|
||||
|
||||
Поэтому они намеренно не переносятся через compatibility boundary.
|
||||
|
||||
Это ожидаемая потеря расширенной информации:
|
||||
|
||||
```text
|
||||
полная новая модель Instrument
|
||||
↓
|
||||
ограниченный legacy-контракт ExchangeSymbol
|
||||
```
|
||||
|
||||
Compatibility mapper не:
|
||||
|
||||
```text
|
||||
расширяет ExchangeSymbol;
|
||||
создаёт дополнительные атрибуты;
|
||||
переносит данные в несоответствующие поля;
|
||||
изменяет модель Instrument.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Преобразование Decimal → float
|
||||
|
||||
Новая модель `Instrument` использует:
|
||||
|
||||
```python
|
||||
Decimal | None
|
||||
```
|
||||
|
||||
Legacy-модель `ExchangeSymbol` использует:
|
||||
|
||||
```python
|
||||
float | None
|
||||
```
|
||||
|
||||
Преобразованию подлежат:
|
||||
|
||||
```text
|
||||
tick_size
|
||||
step_size
|
||||
min_qty
|
||||
min_notional
|
||||
```
|
||||
|
||||
Правило преобразования:
|
||||
|
||||
```text
|
||||
None
|
||||
↓
|
||||
None
|
||||
```
|
||||
|
||||
и:
|
||||
|
||||
```text
|
||||
Decimal("0.0001")
|
||||
↓
|
||||
0.0001
|
||||
```
|
||||
|
||||
Для этого реализован внутренний helper:
|
||||
|
||||
```python
|
||||
def _decimal_to_float(
|
||||
value: Decimal | None,
|
||||
) -> float | None:
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
return float(value)
|
||||
```
|
||||
|
||||
Переход к `float` выполняется только на compatibility-границе.
|
||||
|
||||
Внутри новой Instrument Reference Data pipeline точное десятичное представление через `Decimal` сохраняется.
|
||||
|
||||
---
|
||||
|
||||
## Преобразование market_modes
|
||||
|
||||
Новая модель использует:
|
||||
|
||||
```python
|
||||
tuple[str, ...]
|
||||
```
|
||||
|
||||
Legacy-модель использует:
|
||||
|
||||
```python
|
||||
list[str]
|
||||
```
|
||||
|
||||
Compatibility mapper выполняет:
|
||||
|
||||
```python
|
||||
list(instrument.market_modes)
|
||||
```
|
||||
|
||||
Например:
|
||||
|
||||
```text
|
||||
Instrument:
|
||||
("REGULAR", "CLOSE_ONLY")
|
||||
|
||||
↓
|
||||
|
||||
ExchangeSymbol:
|
||||
["REGULAR", "CLOSE_ONLY"]
|
||||
```
|
||||
|
||||
Порядок значений сохраняется.
|
||||
|
||||
Для каждого результата создаётся новый независимый список.
|
||||
|
||||
Изменение:
|
||||
|
||||
```python
|
||||
exchange_symbol.market_modes.append("ADDED_IN_LEGACY")
|
||||
```
|
||||
|
||||
не изменяет:
|
||||
|
||||
```python
|
||||
instrument.market_modes
|
||||
```
|
||||
|
||||
и не влияет на другие объекты `ExchangeSymbol`, созданные из того же `Instrument`.
|
||||
|
||||
---
|
||||
|
||||
## Преобразование полного набора инструментов
|
||||
|
||||
Функция:
|
||||
|
||||
```python
|
||||
map_instruments_to_exchange_symbols()
|
||||
```
|
||||
|
||||
принимает:
|
||||
|
||||
```python
|
||||
tuple[Instrument, ...]
|
||||
```
|
||||
|
||||
и возвращает:
|
||||
|
||||
```python
|
||||
list[ExchangeSymbol]
|
||||
```
|
||||
|
||||
Порядок инструментов сохраняется.
|
||||
|
||||
Например:
|
||||
|
||||
```text
|
||||
(
|
||||
BTC/USD_LEVERAGE,
|
||||
ETH/USD_LEVERAGE,
|
||||
XRP/USD_LEVERAGE,
|
||||
)
|
||||
```
|
||||
|
||||
преобразуется в:
|
||||
|
||||
```text
|
||||
[
|
||||
BTC/USD_LEVERAGE,
|
||||
ETH/USD_LEVERAGE,
|
||||
XRP/USD_LEVERAGE,
|
||||
]
|
||||
```
|
||||
|
||||
Для пустого входного набора:
|
||||
|
||||
```python
|
||||
()
|
||||
```
|
||||
|
||||
возвращается:
|
||||
|
||||
```python
|
||||
[]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Обработка ошибок
|
||||
|
||||
Новый тип исключения в рамках Build 014 не создавался.
|
||||
|
||||
Compatibility mapper получает уже построенную и проверенную модель:
|
||||
|
||||
```text
|
||||
Instrument
|
||||
```
|
||||
|
||||
и выполняет только:
|
||||
|
||||
```text
|
||||
чтение полей;
|
||||
Decimal → float;
|
||||
tuple → list;
|
||||
создание ExchangeSymbol.
|
||||
```
|
||||
|
||||
Существующая ошибка:
|
||||
|
||||
```text
|
||||
InstrumentReferenceMappingError
|
||||
```
|
||||
|
||||
не переиспользуется, поскольку она относится к другому направлению преобразования:
|
||||
|
||||
```text
|
||||
Dzengi raw model
|
||||
↓
|
||||
Instrument
|
||||
```
|
||||
|
||||
Создание отдельной категории ошибки без конкретного реального сценария не требуется.
|
||||
|
||||
---
|
||||
|
||||
## Unit-тесты
|
||||
|
||||
Создан файл:
|
||||
|
||||
```text
|
||||
app/tests/unit/market_data/acquisition/test_compatibility.py
|
||||
```
|
||||
|
||||
Реализовано 12 тестов.
|
||||
|
||||
Проверяются:
|
||||
|
||||
1. преобразование одного `Instrument` в `ExchangeSymbol`;
|
||||
2. перенос всех 11 общих legacy-полей;
|
||||
3. преобразование `Decimal → float`;
|
||||
4. сохранение `None` для отсутствующих числовых значений;
|
||||
5. преобразование `tuple[str, ...] → list[str]` для `market_modes`;
|
||||
6. сохранение порядка `market_modes`;
|
||||
7. создание независимого списка `market_modes`;
|
||||
8. преобразование нескольких инструментов;
|
||||
9. сохранение порядка инструментов;
|
||||
10. пустой `tuple` преобразуется в пустой `list`;
|
||||
11. исходный `Instrument` не изменяется;
|
||||
12. результат compatibility mapper эквивалентен исходным `Instrument` по comparator Build 013.
|
||||
|
||||
---
|
||||
|
||||
## Round-trip проверка через comparator Build 013
|
||||
|
||||
Для дополнительной проверки используется уже созданный в Build 013 comparator:
|
||||
|
||||
```text
|
||||
tuple[Instrument, ...]
|
||||
↓
|
||||
map_instruments_to_exchange_symbols()
|
||||
↓
|
||||
list[ExchangeSymbol]
|
||||
↓
|
||||
compare_instrument_reference_data()
|
||||
↓
|
||||
InstrumentReferenceEquivalenceReport
|
||||
```
|
||||
|
||||
Основная проверка:
|
||||
|
||||
```python
|
||||
legacy_symbols = map_instruments_to_exchange_symbols(
|
||||
instruments
|
||||
)
|
||||
|
||||
report = compare_instrument_reference_data(
|
||||
legacy_symbols,
|
||||
instruments,
|
||||
)
|
||||
|
||||
assert report.is_equivalent, report.format()
|
||||
```
|
||||
|
||||
Это подтверждает, что compatibility mapper воспроизводит все 11 общих полей legacy-контракта.
|
||||
|
||||
Comparator остаётся только в тестовом контуре.
|
||||
|
||||
Production-код от comparator не зависит.
|
||||
|
||||
---
|
||||
|
||||
## Результат unit-тестов
|
||||
|
||||
Выполнена команда:
|
||||
|
||||
```bash
|
||||
python -m pytest \
|
||||
tests/unit/market_data/acquisition/test_compatibility.py \
|
||||
-q
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
12 passed in 0.02s
|
||||
```
|
||||
|
||||
Все тесты compatibility mapper успешно пройдены.
|
||||
|
||||
---
|
||||
|
||||
## Проверка синтаксиса
|
||||
|
||||
Выполнена команда:
|
||||
|
||||
```bash
|
||||
python -m py_compile \
|
||||
src/market_data/acquisition/compatibility.py \
|
||||
tests/unit/market_data/acquisition/test_compatibility.py
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
успешно
|
||||
```
|
||||
|
||||
Ошибок синтаксиса не обнаружено.
|
||||
|
||||
---
|
||||
|
||||
## Полный регрессионный прогон
|
||||
|
||||
Выполнена команда:
|
||||
|
||||
```bash
|
||||
python -m pytest -q
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
```text
|
||||
204 passed in 0.19s
|
||||
```
|
||||
|
||||
До Build 014 полный набор содержал:
|
||||
|
||||
```text
|
||||
192 passed
|
||||
```
|
||||
|
||||
В Build 014 добавлено:
|
||||
|
||||
```text
|
||||
12 новых тестов
|
||||
```
|
||||
|
||||
Итого:
|
||||
|
||||
```text
|
||||
192 + 12 = 204
|
||||
```
|
||||
|
||||
Все предыдущие тесты продолжают проходить.
|
||||
|
||||
Регрессий существующего проекта не обнаружено.
|
||||
|
||||
---
|
||||
|
||||
## Проверка отсутствия преждевременного production-использования
|
||||
|
||||
Выполнена команда:
|
||||
|
||||
```bash
|
||||
grep -RIn \
|
||||
--exclude-dir="__pycache__" \
|
||||
--exclude="*.pyc" \
|
||||
-E "map_instrument_to_exchange_symbol|map_instruments_to_exchange_symbols" \
|
||||
src tests
|
||||
```
|
||||
|
||||
Результат подтвердил, что функции compatibility mapper используются только в:
|
||||
|
||||
```text
|
||||
src/market_data/acquisition/compatibility.py
|
||||
tests/unit/market_data/acquisition/test_compatibility.py
|
||||
```
|
||||
|
||||
Других production-потребителей нет.
|
||||
|
||||
В частности:
|
||||
|
||||
```text
|
||||
ExchangeService.get_exchange_symbols()
|
||||
```
|
||||
|
||||
ещё не использует compatibility mapper.
|
||||
|
||||
Это подтверждает отсутствие преждевременного переключения production runtime.
|
||||
|
||||
---
|
||||
|
||||
## Изменения production-кода
|
||||
|
||||
В рамках Build 014 создан только один новый production-файл:
|
||||
|
||||
```text
|
||||
app/src/market_data/acquisition/compatibility.py
|
||||
```
|
||||
|
||||
Не изменялись:
|
||||
|
||||
```text
|
||||
app/src/integrations/exchange/service.py
|
||||
app/src/integrations/exchange/models.py
|
||||
|
||||
app/src/market_data/acquisition/service.py
|
||||
app/src/market_data/acquisition/registry.py
|
||||
app/src/market_data/acquisition/protocol.py
|
||||
app/src/market_data/acquisition/exceptions.py
|
||||
app/src/market_data/acquisition/models/instrument.py
|
||||
|
||||
app/src/market_data/acquisition/adapters/dzengi/
|
||||
app/src/market_data/acquisition/handlers/instrument_handler.py
|
||||
app/src/market_data/acquisition/feeds/instrument_feed.py
|
||||
|
||||
app/src/telegram/
|
||||
app/src/trading/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Обратная совместимость
|
||||
|
||||
Полностью сохранены:
|
||||
|
||||
```text
|
||||
ExchangeService.get_exchange_symbols()
|
||||
ExchangeService.validate_symbol()
|
||||
ExchangeService.get_symbol_runtime_status()
|
||||
```
|
||||
|
||||
Также не изменены:
|
||||
|
||||
```text
|
||||
сигнатуры существующих production-методов;
|
||||
legacy imports;
|
||||
формат runtime-ошибок;
|
||||
Telegram UI;
|
||||
автоторговля;
|
||||
существующее runtime-поведение;
|
||||
exchange symbols cache.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Что Build 014 намеренно не делает
|
||||
|
||||
Build 014 не выполняет:
|
||||
|
||||
```text
|
||||
переключение ExchangeService.get_exchange_symbols();
|
||||
подключение InstrumentAcquisitionService к ExchangeService;
|
||||
создание production composition;
|
||||
изменение exchange symbols cache;
|
||||
изменение validate_symbol();
|
||||
изменение get_symbol_runtime_status();
|
||||
изменение normalize_symbol();
|
||||
изменение symbol_candidates();
|
||||
изменение legacy parser;
|
||||
удаление legacy parser;
|
||||
изменение ExchangeSymbol;
|
||||
изменение Instrument;
|
||||
изменение Telegram UI;
|
||||
изменение AutoTrade.
|
||||
```
|
||||
|
||||
Переключение:
|
||||
|
||||
```text
|
||||
ExchangeService.get_exchange_symbols()
|
||||
```
|
||||
|
||||
относится строго к следующему этапу:
|
||||
|
||||
```text
|
||||
Build 015 — Переключение get_exchange_symbols()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Классификация изменений
|
||||
|
||||
| Изменение | Классификация |
|
||||
|---|---|
|
||||
| Compatibility mapper `Instrument → ExchangeSymbol` | Обязательное архитектурное изменение |
|
||||
| `Decimal → float` на legacy-границе | Обратная совместимость |
|
||||
| `tuple → list` для `market_modes` | Обратная совместимость |
|
||||
| Batch mapper | Обязательное архитектурное изменение |
|
||||
| Unit-тесты | Улучшение надёжности |
|
||||
| Round-trip проверка через Build 013 comparator | Миграционная проверка |
|
||||
| Новый exception | Не создавался |
|
||||
| Изменение legacy runtime | Отсутствует |
|
||||
| Изменение поведения | Отсутствует |
|
||||
|
||||
---
|
||||
|
||||
## Итоговые проверки
|
||||
|
||||
| Проверка | Результат |
|
||||
|---|---|
|
||||
| Unit-тесты Compatibility Mapper | `12 passed in 0.02s` |
|
||||
| `py_compile` | Успешно |
|
||||
| Полный `pytest` | `204 passed in 0.19s` |
|
||||
| Round-trip эквивалентность | Подтверждена |
|
||||
| Независимость `market_modes` | Подтверждена |
|
||||
| Сохранение порядка инструментов | Подтверждено |
|
||||
| Отсутствие преждевременного production-использования | Подтверждено |
|
||||
|
||||
---
|
||||
|
||||
## Условие завершения Build 014
|
||||
|
||||
Все условия выполнены:
|
||||
|
||||
```text
|
||||
[✓] Создан compatibility mapper Instrument → ExchangeSymbol.
|
||||
|
||||
[✓] Переносятся все 11 общих legacy-полей.
|
||||
|
||||
[✓] Decimal корректно преобразуется в float только на legacy-границе.
|
||||
|
||||
[✓] None сохраняется.
|
||||
|
||||
[✓] market_modes преобразуется из tuple в независимый list.
|
||||
|
||||
[✓] Порядок market_modes сохраняется.
|
||||
|
||||
[✓] Batch mapper сохраняет порядок инструментов.
|
||||
|
||||
[✓] Пустой tuple преобразуется в пустой list.
|
||||
|
||||
[✓] Исходные Instrument не изменяются.
|
||||
|
||||
[✓] Round-trip проверка через comparator Build 013 проходит.
|
||||
|
||||
[✓] 12 unit-тестов проходят.
|
||||
|
||||
[✓] Синтаксическая проверка проходит.
|
||||
|
||||
[✓] Полный набор из 204 тестов проходит.
|
||||
|
||||
[✓] ExchangeService ещё не использует compatibility mapper.
|
||||
|
||||
[✓] Production runtime не переключён преждевременно.
|
||||
|
||||
[✓] Обратная совместимость полностью сохранена.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Результат
|
||||
|
||||
```text
|
||||
BUILD 014 — COMPLETE
|
||||
```
|
||||
|
||||
Создана временная compatibility-граница:
|
||||
|
||||
```text
|
||||
Instrument
|
||||
↓
|
||||
ExchangeSymbol
|
||||
```
|
||||
|
||||
Она позволяет на следующем этапе переключить:
|
||||
|
||||
```text
|
||||
ExchangeService.get_exchange_symbols()
|
||||
```
|
||||
|
||||
на новую Instrument Reference Data acquisition pipeline без изменения существующего публичного контракта:
|
||||
|
||||
```python
|
||||
def get_exchange_symbols(self) -> list[ExchangeSymbol]:
|
||||
...
|
||||
```
|
||||
|
||||
Следующий этап утверждённого Migration Plan:
|
||||
|
||||
```text
|
||||
Build 015 — Переключение get_exchange_symbols()
|
||||
```
|
||||
Reference in New Issue
Block a user