Files
dzentra_bot/docs/migrations/build_028.md

16 KiB
Raw Blame History

Build 028 — Dzengi REST Quote Models, Parser и Validation

Статус

Завершён


1. Цель Build 028

Цель Build 028 — реализовать специализированный слой приёма, разбора и первичной проверки REST-ответа Dzengi для текущей рыночной котировки инструмента, не изменяя существующее поведение работающего бота и не подключая новую реализацию к production runtime до следующих этапов миграции.

Build является частью поэтапной миграции подсистемы:

Quotes Feed

в новую архитектуру:

src/market_data/acquisition/

На данном этапе реализованы:

  • модель сырого REST-ответа Dzengi;
  • parser REST-ответа /api/v1/ticker/24hr;
  • проверка структуры входящего payload;
  • проверка допустимости значений котировки;
  • специализированные ошибки обработки quote payload.

Подключение mapper, handler, feed, service, store и перевод runtime-потребителей в данный Build не входят.


2. Место Build 028 в плане миграции Quotes Feed

Утверждённая последовательность:

Build 026 — Аудит текущего контура Quotes Feed
Build 027 — Каноническая модель Quote и специализированные контракты
Build 028 — Dzengi REST quote models, parser и validation
Build 029 — Dzengi mapper и Quotes Handler
Build 030 — Quotes Feed и регистрация в Acquisition Service
Build 031 — Подключение нового REST Quotes Feed под legacy ExchangeService facade
Build 032 — Канонический Quote Store
Build 033 — Перенос MarketPriceCache на Quote Store
Build 034 — Dzengi WebSocket quote parsing и адаптер
Build 035 — Перевод market runtime на Quotes Feed
Build 036 — Перевод read-only и UI-потребителей
Build 037 — Перевод execution-потребителей
Build 038 — Удаление legacy TickerPrice и market snapshot dict layer
Build 039 — Удаление legacy quote parsing и MarketPriceCache
Build 040 — Финальная архитектурная проверка Quotes Feed

Build 028 продолжает фундамент, созданный в Build 027.

Целевая цепочка после завершения следующих этапов:

Dzengi REST /api/v1/ticker/24hr
        ↓
adapters/dzengi/rest.py
        ↓
adapters/dzengi/parser.py
        ↓
adapters/dzengi/models.py
        ↓
validation/schema.py
        ↓
validation/values.py
        ↓
adapters/dzengi/mapper.py
        ↓
handlers/quotes_handler.py
        ↓
feeds/quotes_feed.py
        ↓
acquisition/service.py
        ↓
Quote Store
        ↓
потребители платформы

3. Исходные данные

Для проектирования реализации использован реальный успешный ответ Dzengi:

{
  "askPrice": "64159.55",
  "bidPrice": "64159.45",
  "closeTime": 1783887270312,
  "highPrice": "64261.45",
  "lastPrice": "64159.45",
  "lastQty": "5.0",
  "lowPrice": "63590.7",
  "openPrice": "63785.75",
  "openTime": 1783814400000,
  "prevClosePrice": "63785.75",
  "priceChange": "368.85",
  "priceChangePercent": "0.57822",
  "quoteVolume": "616402.92146",
  "symbol": "BTC/USD_LEVERAGE",
  "volume": "9.6002",
  "weightedAvgPrice": "64159.50"
}

Для базовой модели текущей котировки используются поля:

symbol
lastPrice
bidPrice
askPrice
closeTime

Остальные поля ответа /api/v1/ticker/24hr относятся к расширенной 24-часовой статистике рынка и не включаются в базовую модель Quote.

Это сохраняет правильное разделение ответственностей между:

  • текущей котировкой;
  • рыночной статистикой;
  • OHLCV;
  • trades;
  • order book;
  • другими специализированными типами рыночных данных.

4. Реализованные компоненты

В рамках Build 028 изменены следующие файлы:

src/market_data/acquisition/adapters/dzengi/models.py
src/market_data/acquisition/adapters/dzengi/parser.py
src/market_data/acquisition/validation/schema.py
src/market_data/acquisition/validation/values.py
src/market_data/acquisition/exceptions.py

Добавлены специализированные тесты:

tests/unit/market_data/acquisition/adapters/dzengi/test_quote_parser.py
tests/unit/market_data/acquisition/validation/test_quote_schema.py
tests/unit/market_data/acquisition/validation/test_quote_values.py

5. Dzengi REST Quote Model

В файле:

src/market_data/acquisition/adapters/dzengi/models.py

реализована модель сырой котировки Dzengi.

Её ответственность:

  • представить уже разобранные поля ответа Dzengi;
  • сохранить биржевую семантику полей;
  • не зависеть от legacy-моделей TickerPrice и MarketPriceSnapshot;
  • не выполнять бизнес-интерпретацию;
  • не выполнять преобразование во внутреннюю каноническую модель Quote.

Архитектурная граница:

Dzengi API payload
        ↓
Dzengi REST quote model
        ↓
mapper
        ↓
canonical Quote

Модель адаптера является специфичной для Dzengi и не должна использоваться напрямую верхними слоями платформы.


6. REST Quote Parser

В файле:

src/market_data/acquisition/adapters/dzengi/parser.py

реализован специализированный parser REST-котировки.

Его ответственность:

  1. принять необработанный ответ API;
  2. определить фактический quote payload;
  3. поддержать прямую структуру ответа;
  4. поддержать wrapped payload;
  5. проверить структуру через schema validation;
  6. извлечь необходимые поля;
  7. проверить значения через value validation;
  8. вернуть специализированную Dzengi quote model.

Поддерживаемые формы payload:

Прямой payload
{
  "symbol": "BTC/USD_LEVERAGE",
  "lastPrice": "64159.45",
  "bidPrice": "64159.45",
  "askPrice": "64159.55",
  "closeTime": 1783887270312
}

и wrapped payload:

{
  "payload": {
    "symbol": "BTC/USD_LEVERAGE",
    "lastPrice": "64159.45",
    "bidPrice": "64159.45",
    "askPrice": "64159.55",
    "closeTime": 1783887270312
  }
}

Parser не создаёт канонический Quote. Это ответственность mapper, реализуемого в Build 029.


7. Schema Validation

В файле:

src/market_data/acquisition/validation/schema.py

реализована проверка структуры quote payload.

Проверяются обязательные поля:

symbol
lastPrice
bidPrice
askPrice
closeTime

Schema validation отвечает только на вопрос:

Имеет ли входящее сообщение необходимую структуру для дальнейшей обработки?

Она не должна:

  • преобразовывать значения;
  • вычислять midpoint;
  • определять freshness;
  • создавать Quote;
  • обращаться к сети;
  • обращаться к store;
  • зависеть от ExchangeService.

8. Value Validation

В файле:

src/market_data/acquisition/validation/values.py

реализована проверка допустимости значений REST-котировки.

Контролируются следующие инварианты:

symbol != empty
last_price > 0
bid_price > 0
ask_price > 0
close_time >= 0
bid_price <= ask_price

Проверка:

bid_price <= ask_price

является важным базовым инвариантом котировки.

Payload, в котором:

bid_price > ask_price

не должен бесконтрольно попадать в канонический слой платформы.


9. Исключения

В файле:

src/market_data/acquisition/exceptions.py

используются специализированные исключения Acquisition layer для ошибок обработки рыночных данных.

Ошибки quote parsing и validation не должны зависеть от:

src/integrations/exchange/exceptions.py

Это необходимо для соблюдения направления зависимостей:

market_data/acquisition
        X
integrations/exchange legacy layer

Новая подсистема Acquisition не должна архитектурно зависеть от legacy ExchangeService.


10. Архитектурные решения Build 028

10.1. Каноническая модель не зависит от формата Dzengi

Поля API:

lastPrice
bidPrice
askPrice
closeTime

существуют только внутри Dzengi adapter layer.

Во внутренних слоях платформы используются канонические имена:

last_price
bid_price
ask_price
source_timestamp_ms

Преобразование между ними является ответственностью mapper.


10.2. Parser не выполняет mapping

Разделение сохраняется строго:

parser
    ↓
разбирает внешний payload

validation
    ↓
проверяет структуру и значения

mapper
    ↓
преобразует adapter model в canonical model

Это предотвращает смешивание:

  • API-specific parsing;
  • validation;
  • domain mapping.

10.3. REST quote не зависит от legacy TickerPrice

Новая цепочка не использует:

src.integrations.exchange.models.TickerPrice

TickerPrice остаётся временной legacy-моделью и будет удалён только после перевода всех потребителей согласно плану миграции.


10.4. REST quote не зависит от MarketPriceCache

Build 028 не изменяет:

src/integrations/exchange/market_cache.py

и не записывает данные в:

MarketPriceCache

Миграция хранения выполняется отдельно:

Build 032 — Канонический Quote Store
Build 033 — Перенос MarketPriceCache на Quote Store

10.5. Runtime-поведение бота не изменено

На этапе Build 028:

  • новый parser не подключён к production runtime;
  • ExchangeService продолжает работать по прежнему интерфейсу;
  • MarketPriceCache не изменён;
  • MarketDataRunner не изменён;
  • execution-потребители не изменены;
  • UI-потребители не изменены.

Таким образом, Build 028 является безопасным additive-этапом миграции.


11. Что намеренно не реализовано

В Build 028 не входят:

Dzengi mapper
Quotes Handler
Quotes Feed
регистрация Quotes Feed
подключение к Acquisition Service
подключение к ExchangeService facade
Quote Store
перенос MarketPriceCache
WebSocket quote parser
перевод MarketDataRunner
перевод UI-потребителей
перевод execution-потребителей
удаление TickerPrice
удаление market snapshot dict layer
удаление MarketPriceCache

Эти изменения выполняются только в соответствующих последующих Build.


12. Проверки

Выполнена синтаксическая проверка:

python -m py_compile \
  src/market_data/acquisition/adapters/dzengi/models.py \
  src/market_data/acquisition/adapters/dzengi/parser.py \
  src/market_data/acquisition/validation/schema.py \
  src/market_data/acquisition/validation/values.py \
  src/market_data/acquisition/exceptions.py \
  tests/unit/market_data/acquisition/adapters/dzengi/test_quote_parser.py \
  tests/unit/market_data/acquisition/validation/test_quote_schema.py \
  tests/unit/market_data/acquisition/validation/test_quote_values.py

Результат:

Успешно.

Выполнены специализированные тесты Build 028:

python -m pytest \
  tests/unit/market_data/acquisition/adapters/dzengi/test_quote_parser.py \
  tests/unit/market_data/acquisition/validation/test_quote_schema.py \
  tests/unit/market_data/acquisition/validation/test_quote_values.py \
  -q

Результат:

22 passed in 0.02s

Выполнена полная регрессия проекта:

python -m pytest -q

Результат:

445 passed in 0.27s

Регрессий не обнаружено.


13. Критерии завершения Build 028

Build 028 считается завершённым, поскольку выполнены все необходимые условия:

  • получен реальный успешный ответ /api/v1/ticker/24hr;
  • определён минимальный набор полей текущей котировки;
  • реализована специализированная Dzengi REST quote model;
  • реализован REST quote parser;
  • поддержан прямой payload;
  • поддержан wrapped payload;
  • реализована schema validation;
  • реализована value validation;
  • проверяются положительные цены;
  • проверяется временная метка;
  • проверяется инвариант bid_price <= ask_price;
  • новая реализация не зависит от legacy TickerPrice;
  • новая реализация не зависит от MarketPriceCache;
  • production runtime не изменён;
  • специализированные тесты проходят;
  • полная регрессия проходит.

14. Итог

В результате Build 028 создан специализированный входной контур для REST-котировок Dzengi:

Dzengi /api/v1/ticker/24hr
        ↓
raw payload
        ↓
schema validation
        ↓
Dzengi quote parser
        ↓
value validation
        ↓
Dzengi REST quote model

При этом сохранены ключевые архитектурные свойства миграции:

  • новая реализация добавлена параллельно legacy-контуру;
  • работающий бот не сломан;
  • публичное поведение ExchangeService не изменено;
  • отсутствует зависимость новой Acquisition subsystem от legacy quote models;
  • parsing, validation и будущий mapping разделены по ответственности;
  • сохранена возможность безопасного поэтапного переключения потребителей.

Build 028 завершён.

Следующий этап:

Build 029 — Dzengi mapper и Quotes Handler