14 KiB
Build 025 — Удаление legacy ExchangeSymbol compatibility layer
Статус: ✅ Завершён
Дата: 2026-07-12
Подсистема: Instrument Reference / Exchange Integration
Этап: Завершение миграции на каноническую модель Instrument
Цель Build
Полностью удалить временный compatibility layer, использовавшийся во время поэтапного перехода от legacy-модели:
ExchangeSymbol
к канонической модели:
Instrument
После завершения Build все production-потребители работают через единый канонический контур:
Instrument Acquisition
↓
Instrument Store
↓
get_instruments()
↓
validate_symbol()
↓
Instrument
Предпосылки
На предыдущих этапах были выполнены:
- создание канонической модели
Instrument; - построение Instrument Acquisition pipeline;
- создание
InstrumentStoreProtocol; - реализация
InMemoryInstrumentStore; - перенос кэша справочника инструментов в Storage;
- добавление
ExchangeService.get_instruments(); - перевод
validate_symbol()на канонический справочник; - перевод Telegram UI и runtime-потребителей на
Instrument; - удаление неиспользуемого legacy Market Handler.
После этого legacy-контур сохранялся только как временная проекция:
Instrument
↓
compatibility.py
↓
ExchangeSymbol
↓
get_exchange_symbols()
Реальных production-потребителей этого контура больше не осталось.
Предварительный аудит
Выполнен поиск production-зависимостей:
grep -RIn \
--exclude-dir="__pycache__" \
--exclude="*.pyc" \
-E "ExchangeSymbol|get_exchange_symbols|map_instrument_to_exchange_symbol|map_instruments_to_exchange_symbols|_exchange_symbols_projection_cache" \
src
Установлено, что все найденные элементы находились только внутри самого legacy-контура:
src/market_data/acquisition/compatibility.py
src/integrations/exchange/models.py
src/integrations/exchange/service.py
Канонические production-потребители уже использовали:
get_instruments()
validate_symbol()
Instrument
Аудит legacy parser helpers
Проверены методы:
_extract_exchange_symbols_raw()
_parse_exchange_symbol()
_parse_exchange_symbol_status()
_parse_market_modes()
_extract_filter_value()
Выполнено:
grep -RIn \
--exclude-dir="__pycache__" \
--exclude="*.pyc" \
-E "_extract_exchange_symbols_raw|_parse_exchange_symbol\(|_parse_exchange_symbol_status|_parse_market_modes|_extract_filter_value" \
src tests
Подтверждено, что эти методы использовались только устаревшим equivalence-тестом и больше не требовались production-коду.
Подготовка тестового контура
Перед удалением production compatibility layer были обновлены канонические тесты.
Обновлён файл
tests/unit/integrations/exchange/test_service_instruments.py
Из него удалены тесты legacy-проекции:
test_get_exchange_symbols_uses_get_instruments
test_get_exchange_symbols_maps_canonical_instruments
test_get_exchange_symbols_projection_cache_preserves_identity
Сохранены все тесты канонического поведения:
- отключённая биржа;
- Instrument Store hit;
- Instrument Store miss;
- загрузка через acquisition;
- сохранение результата в Store;
- повторное использование Store;
- поддержка пустого immutable-набора;
- общий Store между экземплярами
ExchangeService; - обработка acquisition errors;
- логирование ошибок;
- отсутствие заполнения Store при ошибке.
Обновлён файл
tests/unit/integrations/exchange/test_service_validate_symbol.py
Legacy-проверка через monkeypatch метода:
get_exchange_symbols()
заменена проверкой прямого использования:
get_instruments()
Дополнительно добавлен отрицательный архитектурный тест:
def test_exchange_service_has_no_legacy_get_exchange_symbols() -> None:
assert not hasattr(
ExchangeService,
"get_exchange_symbols",
)
Обновлён файл
tests/unit/telegram/ui/test_currency_ui.py
Legacy-проверка отсутствия вызова get_exchange_symbols() заменена прямой проверкой использования канонического:
get_instruments()
Проверка подготовительного пакета
Выполнена компиляция:
python -m py_compile \
tests/unit/integrations/exchange/test_service_instruments.py \
tests/unit/integrations/exchange/test_service_validate_symbol.py \
tests/unit/telegram/ui/test_currency_ui.py
Результат:
Ошибок нет.
Выполнены целевые тесты:
python -m pytest \
tests/unit/integrations/exchange/test_service_instruments.py \
tests/unit/integrations/exchange/test_service_validate_symbol.py \
tests/unit/telegram/ui/test_currency_ui.py \
-q
Результат:
49 passed
Удалённые migration-only файлы
Полностью удалены:
src/market_data/acquisition/compatibility.py
tests/unit/market_data/acquisition/test_compatibility.py
tests/unit/market_data/acquisition/test_equivalence_comparator.py
tests/unit/integrations/exchange/test_service_exchange_symbols.py
tests/integration/market_data/acquisition/test_instrument_reference_equivalence.py
tests/support/instrument_reference_equivalence.py
Эти файлы обслуживали только временный compatibility/equivalence-контур и завершили свою миграционную задачу.
Изменения в models.py
Из файла:
src/integrations/exchange/models.py
полностью удалена legacy-модель:
@dataclass(slots=True)
class ExchangeSymbol:
...
Модель:
SymbolValidationResult
сохраняет канонический контракт:
symbol_info: Instrument | None
Импорт Instrument выполняется через:
TYPE_CHECKING
что исключает runtime-cycle и сохраняет корректную типизацию.
Изменения в service.py
Из файла:
src/integrations/exchange/service.py
удалены следующие элементы.
Legacy imports
Удалены:
ExchangeSymbol
и:
from src.market_data.acquisition.compatibility import (
map_instruments_to_exchange_symbols,
)
Legacy projection cache
Удалено поле:
_exchange_symbols_projection_cache
Теперь ExchangeService хранит только канонический Store:
_instrument_store: InstrumentStoreProtocol = InMemoryInstrumentStore()
Legacy API
Полностью удалён метод:
get_exchange_symbols()
Единственным публичным API справочника инструментов остаётся:
get_instruments()
Legacy parser helpers
Удалены методы:
_extract_exchange_symbols_raw()
_parse_exchange_symbol()
_parse_exchange_symbol_status()
_parse_market_modes()
_extract_filter_value()
Их функции полностью заменены новой pipeline:
Dzengi REST document
↓
Dzengi parser
↓
validation
↓
mapper
↓
Instrument
Сохранённый helper
Метод:
_safe_str()
сохранён, так как продолжает использоваться обработкой trading fee payload.
Финальный production-контур
После удаления compatibility layer работа со справочником инструментов выполняется так:
DzengiInstrumentDocumentSource
↓
DzengiInstrumentDocumentHandler
↓
InstrumentFeed
↓
InstrumentFeedRegistry
↓
InstrumentAcquisitionService
↓
tuple[Instrument, ...]
↓
Instrument Store
↓
ExchangeService.get_instruments()
Проверка пользовательского символа выполняется через:
validate_symbol()
↓
SymbolValidationResult
↓
symbol_info: Instrument | None
Архитектурные отрицательные тесты
Добавлены проверки физического отсутствия legacy API.
Отсутствие legacy-метода
def test_exchange_service_has_no_legacy_get_exchange_symbols() -> None:
assert not hasattr(
ExchangeService,
"get_exchange_symbols",
)
Отсутствие legacy projection cache
def test_exchange_service_has_no_legacy_projection_cache() -> None:
assert not hasattr(
ExchangeService,
"_exchange_symbols_projection_cache",
)
Финальный аудит
Выполнено:
grep -RIn \
--exclude-dir="__pycache__" \
--exclude="*.pyc" \
-E "ExchangeSymbol|get_exchange_symbols|map_instrument_to_exchange_symbol|map_instruments_to_exchange_symbols|_exchange_symbols_projection_cache|market_data\.acquisition\.compatibility" \
src tests
Остались только ожидаемые упоминания в отрицательных архитектурных тестах:
test_exchange_service_has_no_legacy_get_exchange_symbols
test_exchange_service_has_no_legacy_projection_cache
Других production- или test-зависимостей не обнаружено.
Проверка компиляции
Выполнено:
python -m py_compile \
src/integrations/exchange/models.py \
src/integrations/exchange/service.py \
tests/unit/integrations/exchange/test_service_instruments.py \
tests/unit/integrations/exchange/test_service_validate_symbol.py \
tests/unit/telegram/ui/test_currency_ui.py
Результат:
Ошибок нет.
Целевой Regression Suite
Выполнено:
python -m pytest \
tests/unit/integrations/exchange/test_service_instruments.py \
tests/unit/integrations/exchange/test_service_validate_symbol.py \
tests/unit/integrations/exchange/test_service_symbol_runtime_status.py \
tests/unit/integrations/exchange/test_market_stream.py \
tests/unit/integrations/exchange/test_market_data_runner.py \
tests/unit/telegram/ui/test_currency_ui.py \
-q
Результат:
94 passed
Полный Regression Suite
Выполнено:
python -m pytest -q
Результат:
423 passed
Снижение общего числа тестов относительно предыдущего Build является ожидаемым, поскольку были удалены временные compatibility- и equivalence-тесты вместе с соответствующим legacy-кодом.
Архитектурный итог
До Build 025:
Instrument
├── canonical consumers
└── compatibility mapper
↓
ExchangeSymbol
↓
legacy projection cache
После Build 025:
Instrument
↓
Instrument Store
↓
canonical consumers
В проекте больше не существует:
ExchangeSymbol
compatibility.py
get_exchange_symbols()
_exchange_symbols_projection_cache
Instrument → ExchangeSymbol mapping
legacy exchangeInfo parser helpers
migration equivalence framework
Итог Build
Build 025 полностью завершён.
Подтверждено:
- все production-потребители переведены на
Instrument; - удалена legacy-модель
ExchangeSymbol; - удалён временный compatibility mapper;
- удалён legacy-метод
get_exchange_symbols(); - удалён projection cache;
- удалены неиспользуемые parser helpers;
- удалены migration-only и equivalence-тесты;
- сохранены и усилены канонические тесты Instrument Store;
- добавлены архитектурные тесты отсутствия legacy API;
- компиляция проходит без ошибок;
- целевой Regression Suite успешно пройден — 94 passed;
- полный Regression Suite успешно пройден — 423 passed.
Статус Build: ✅ Завершён.