18 KiB
Dzentra — Instrument Reference Data Migration
Build 004 — Dzengi exchangeInfo Parser
Статус: Завершён
Подсистема: Market Data Acquisition
Компонент: Dzengi Adapter / exchangeInfo Parser
Проект: Dzentra
Язык: Русский
Python: 3.12
1. Цель Build 004
Цель Build 004 — реализовать parser для ответа Dzengi exchangeInfo, который преобразует уже структурно проверенный документ из Build 003 в типизированные транспортные модели Dzengi, созданные в Build 002.
Целевая цепочка после завершения Build 004:
Raw JSON document
↓
validate_exchange_info_schema()
↓
ValidatedExchangeInfoDocument
↓
parse_exchange_info()
↓
DzengiExchangeInfoResponse
Build 004 не подключает новую реализацию к существующему ExchangeService и не изменяет поведение работающего бота.
2. Почему Build 004 выполняется именно сейчас
До начала Build 004 уже были завершены необходимые предыдущие этапы.
Build 001 — внутренняя модель Instrument
Создана внутренняя типизированная модель:
Instrument
Она представляет инструмент внутри новой архитектуры Dzentra и не зависит от формата конкретной биржи.
Build 002 — транспортные модели Dzengi
Созданы типизированные модели сырого ответа exchangeInfo:
DzengiExchangeInfoResponse
DzengiExchangeInfoPayload
DzengiExchangeInfoSymbol
DzengiRateLimit
DzengiInstrumentFilter
DzengiLotSizeFilter
DzengiMinNotionalFilter
DzengiUnknownFilter
Build 003 — структурная валидация exchangeInfo
Создан слой проверки структуры сырого JSON-документа:
validate_exchange_info_schema()
Его результат:
ValidatedExchangeInfoDocument
Таким образом, только после Build 001–003 стало безопасно реализовать parser, не смешивая:
- проверку структуры JSON;
- разбор транспортного ответа;
- предметное преобразование в
Instrument; - сетевой REST-доступ;
- кэширование;
- legacy-совместимость.
3. Реализованная архитектурная цепочка
На текущем этапе действует следующая архитектура:
Raw Dzengi JSON
↓
Schema Validation
↓
ValidatedExchangeInfoDocument
↓
Dzengi Parser
↓
DzengiExchangeInfoResponse
Полная целевая цепочка миграции пока ещё не завершена:
Dzengi REST API
↓
Dzengi REST Adapter
↓
Schema Validation
↓
Parser
↓
Dzengi Raw Models
↓
Mapper
↓
Instrument
↓
Instrument Handler
↓
Instrument Feed
↓
Market Data Acquisition Service
↓
Compatibility Layer
↓
ExchangeService
↓
Existing Consumers
Build 004 реализует только участок:
ValidatedExchangeInfoDocument
↓
Parser
↓
Dzengi Raw Models
4. Файлы Build 004
Реализован
app/src/market_data/acquisition/adapters/dzengi/parser.py
Использованы существующие файлы
app/src/market_data/acquisition/adapters/dzengi/models.py
app/src/market_data/acquisition/validation/schema.py
app/src/market_data/acquisition/exceptions.py
Добавлены тесты
app/tests/unit/market_data/acquisition/adapters/dzengi/test_parser.py
5. Публичный контракт parser
Основной публичный вход Build 004:
def parse_exchange_info(
document: ValidatedExchangeInfoDocument,
) -> DzengiExchangeInfoResponse:
...
Parser намеренно не принимает произвольный сырой dict.
Корректная последовательность вызовов:
validated = validate_exchange_info_schema(raw_document)
response = parse_exchange_info(validated)
Это обеспечивает явное разделение ответственности между Build 003 и Build 004.
6. Разделение ответственности
Build 003 — Schema Validation
Отвечает за структурную корректность документа:
- корневой объект должен быть JSON-объектом;
payload, если используется wrapped-формат, должен быть объектом;symbolsдолжен быть массивом;- каждый элемент
symbolsдолжен быть объектом; filtersдолжен быть массивом;- другие структурные ограничения проверяются до parser.
Build 003 не создаёт транспортные модели Dzengi.
Build 004 — Parser
Отвечает за преобразование структурно проверенного документа в:
DzengiExchangeInfoResponse
и вложенные типизированные транспортные модели.
Parser не отвечает за:
- HTTP-запросы;
- кэширование;
- предметную модель
Instrument; - нормализацию символа для бизнес-логики;
- runtime-статус инструмента;
- UI;
- legacy-совместимость.
7. Поддерживаемые форматы exchangeInfo
Архитектура поддерживает два формата ответа.
Unwrapped
{
"timezone": "UTC",
"serverTime": 1783537921471,
"rateLimits": [],
"exchangeFilters": [],
"symbols": []
}
После Build 003:
is_wrapped: False
status: None
correlation_id: None
После Build 004 документ преобразуется в:
DzengiExchangeInfoResponse
└── payload: DzengiExchangeInfoPayload
Wrapped
{
"status": "OK",
"correlationId": "2",
"payload": {
"timezone": "UTC",
"serverTime": 1783537921471,
"rateLimits": [],
"exchangeFilters": [],
"symbols": []
}
}
После Build 003:
is_wrapped: True
status: OK
correlation_id: 2
После Build 004 метаданные оболочки сохраняются в:
DzengiExchangeInfoResponse.status
DzengiExchangeInfoResponse.correlation_id
8. Разбор инструмента
Каждый элемент массива symbols преобразуется в:
DzengiExchangeInfoSymbol
Поддерживаются следующие поля:
symbol
name
status
asset_type
base_asset
base_asset_precision
quote_asset
quote_asset_id
quote_precision
order_types
filters
market_modes
market_type
country
sector
industry
trading_hours
tick_size
tick_value
trading_fee
exchange_fee
long_rate
short_rate
swap_charge_interval
min_sl_gap
max_sl_gap
min_tp_gap
max_tp_gap
На этом этапе поля сохраняют транспортную семантику Dzengi и ещё не преобразуются в предметную модель Instrument.
9. Разбор filters
Parser преобразует известные типы фильтров в отдельные типизированные модели.
LOT_SIZE
Преобразуется в:
DzengiLotSizeFilter
Поля:
filter_type
min_qty
max_qty
step_size
Пример:
DzengiLotSizeFilter(
filter_type='LOT_SIZE',
min_qty='0.0001',
max_qty='1000',
step_size='0.0001',
)
MIN_NOTIONAL
Преобразуется в:
DzengiMinNotionalFilter
Поле:
min_notional
Неизвестные фильтры
Неизвестный filterType не отбрасывается автоматически.
Он преобразуется в:
DzengiUnknownFilter
Это позволяет сохранить неизвестные скалярные поля транспортного ответа без добавления неподтверждённой предметной семантики.
10. Числовые значения
Build 004 сохраняет важное разделение между транспортным и предметным слоями.
Например, значения фильтра:
{
"minQty": "0.0001",
"maxQty": "1000",
"stepSize": "0.0001"
}
в транспортной модели остаются:
min_qty='0.0001'
max_qty='1000'
step_size='0.0001'
Parser не выполняет преждевременное преобразование этих значений в float.
Преобразование в точный предметный числовой тип должно выполняться на следующем архитектурном этапе при построении Instrument.
Это позволяет избежать потери точности и сохраняет исходную семантику ответа Dzengi.
11. Обработка ошибок
Для ошибок parser используется отдельное исключение:
InstrumentReferenceParseError
Оно объявлено в:
app/src/market_data/acquisition/exceptions.py
Иерархия:
MarketDataAcquisitionError
└── InstrumentReferenceParseError
InstrumentReferenceParseError используется только:
- в
parser.py; - в unit-тестах parser.
На момент завершения Build 004 это исключение не используется:
ExchangeService;- Telegram UI;
- runtime-кодом;
- автоторговлей;
- существующими production-потребителями.
12. Классификация изменений
| Изменение | Классификация | Влияние на поведение |
|---|---|---|
Реализация exchangeInfo parser |
Обязательное архитектурное изменение | Нет |
| Типизированное преобразование symbols | Обязательное архитектурное изменение | Нет |
| Типизированный разбор известных filters | Обязательное архитектурное изменение | Нет |
| Сохранение неизвестных filters | Улучшение надёжности | Нет |
Отдельное InstrumentReferenceParseError |
Улучшение надёжности | Нет |
Сохранение числовых строк без преобразования в float |
Улучшение надёжности | Нет |
| Поддержка wrapped и unwrapped форматов | Улучшение надёжности | Нет |
Изменений поведения работающего бота в Build 004 нет.
13. Обратная совместимость
Build 004 не изменяет существующие публичные интерфейсы.
Без изменений продолжают работать:
ExchangeService.get_exchange_symbols()
ExchangeService.validate_symbol()
ExchangeService.get_symbol_runtime_status()
ExchangeService.get_symbol_market_status()
Также без изменений остаются:
ExchangeSymbol
SymbolValidationResult
normalize_symbol()
symbol_candidates()
Новая реализация parser пока не подключена к существующему ExchangeService.
Следовательно:
- Telegram UI не изменён;
- автоторговля не изменена;
- runtime-проверки символа не изменены;
- существующий кэш инструментов не изменён;
- старые импорты продолжают работать;
- production-поведение бота сохранено.
14. Выполненные проверки
Проверка 1 — unit-тесты parser
Команда:
python -m pytest \
tests/unit/market_data/acquisition/adapters/dzengi/test_parser.py \
-q
Результат:
19 passed in 0.02s
Статус:
PASS
Проверка 2 — синтаксическая компиляция
Команда:
python -m py_compile \
src/market_data/acquisition/exceptions.py \
src/market_data/acquisition/adapters/dzengi/parser.py \
tests/unit/market_data/acquisition/adapters/dzengi/test_parser.py
Результат:
Команда завершена без ошибок.
Статус:
PASS
Проверка 3 — ручная цепочка schema → parser
Была проверена цепочка:
raw dict
↓
validate_exchange_info_schema()
↓
ValidatedExchangeInfoDocument
↓
parse_exchange_info()
↓
DzengiExchangeInfoResponse
Полученный результат:
Symbol: BTC/USD_LEVERAGE
Status: TRADING
Asset type: CRYPTOCURRENCY
Base asset: BTC
Quote asset: USD
Tick size: 0.05
Filters: (DzengiLotSizeFilter(filter_type='LOT_SIZE', min_qty='0.0001', max_qty='1000', step_size='0.0001'),)
Статус:
PASS
Проверка 4 — полный набор тестов проекта
Команда:
python -m pytest -q
Результат:
50 passed in 0.05s
Статус:
PASS
Проверка 5 — контроль зависимостей parser
Команда:
grep -RIn \
--exclude-dir="__pycache__" \
--exclude="*.pyc" \
-E "parse_exchange_info|_parse_exchange_info|DzengiExchangeInfoParseError" \
src tests
Подтверждено:
parse_exchange_info()определён в новомparser.py;- используется только unit-тестами нового parser;
- не используется существующим
ExchangeService; - не используется UI;
- не используется runtime;
- не используется автоторговлей.
Статус:
PASS
Проверка 6 — контроль использования parse-ошибки
Команда:
grep -RIn \
--exclude-dir="__pycache__" \
--exclude="*.pyc" \
-E "InstrumentReferenceParseError" \
src tests
Подтверждено:
- исключение объявлено в
exceptions.py; - используется parser;
- используется unit-тестами parser;
- не подключено к production-потребителям.
Статус:
PASS
15. Итог Build 004
Build 004 успешно завершён.
Реализован типизированный parser:
ValidatedExchangeInfoDocument
↓
parse_exchange_info()
↓
DzengiExchangeInfoResponse
Подтверждено:
Build 001 — Instrument domain model PASS
Build 002 — Dzengi raw transport models PASS
Build 003 — exchangeInfo schema validation PASS
Build 004 — Dzengi exchangeInfo parser PASS
Общий результат тестов после завершения Build 004:
50 passed in 0.05s
Работающий бот не затронут.
16. Условие завершения Build 004
Build 004 считается завершённым, поскольку выполнены все условия:
- parser реализован;
- parser принимает только
ValidatedExchangeInfoDocument; - parser возвращает
DzengiExchangeInfoResponse; - основные поля инструмента сохраняются;
- известные filters типизированы;
- неизвестные filters сохраняются;
- ошибки parser имеют отдельный тип;
- unit-тесты проходят;
- полный набор тестов проходит;
- production-потребители не изменены;
- обратная совместимость сохранена.
17. Следующий этап
Следующий этап миграции:
Build 005 — Проверка значений Instrument Reference Data
Его задача — преобразовать:
DzengiExchangeInfoSymbol
↓
Mapper
↓
Instrument
При этом Build 005 должен:
- использовать модель
Instrument, созданную в Build 001; - использовать транспортную модель
DzengiExchangeInfoSymbolиз Build 002; - не выполнять HTTP-запросы;
- не заниматься кэшированием;
- не подключаться к
ExchangeService; - не менять production-поведение бота;
- явно определить правила преобразования числовых значений в
Decimal; - явно определить соответствие полей Dzengi полям внутренней модели
Instrument; - отдельно классифицировать любые потенциальные изменения поведения.
До написания кода Build 005 необходимо сначала проанализировать существующие модели и контракты, относящиеся к преобразованию DzengiExchangeInfoSymbol в Instrument.