Files
dzentra_bot/docs/migrations/build_004.md

18 KiB
Raw Blame History

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 001003 стало безопасно реализовать 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.