16 KiB
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-котировки.
Его ответственность:
- принять необработанный ответ API;
- определить фактический quote payload;
- поддержать прямую структуру ответа;
- поддержать wrapped payload;
- проверить структуру через schema validation;
- извлечь необходимые поля;
- проверить значения через value validation;
- вернуть специализированную 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