13 KiB
Build 060.4 — REST Trade Parser
Статус
✅ Завершён
Цель Build
После завершения Build 060.3 система уже умела принимать REST-ответ endpoint /api/v2/aggTrades, проверять его структуру и преобразовывать в immutable-контракт:
REST JSON
↓
validate_rest_agg_trades_schema()
↓
ValidatedRestAggTradesDocument
Однако структурно проверенный документ ещё не преобразовывался в transport-модели адаптера Dzengi.
Цель Build 060.4 — добавить отдельный REST Trade Parser, который:
- принимает только
ValidatedRestAggTradesDocument; - проверяет типы полей каждой сделки;
- создаёт
DzengiRestAggTrade; - возвращает immutable-последовательность transport-моделей;
- не выполняет value validation и canonical mapping.
Итоговый участок pipeline после Build 060.4:
REST JSON
↓
Schema Validation
↓
ValidatedRestAggTradesDocument
↓
REST Trade Parser
↓
tuple[DzengiRestAggTrade, ...]
Причины появления Build
Schema Validation отвечает только за структуру внешнего документа. После Build 060.3 она гарантирует, что корень документа является JSON-массивом, каждый элемент является JSON-объектом, обязательные поля a, p, q, T, m присутствуют, ключи объектов являются строками, а документ переведён в immutable-представление.
Но Schema Validation намеренно не определяет, являются ли значения полей допустимыми транспортными типами. Именно Parser обязан отклонить такой документ и не допустить создание некорректной transport-модели.
Архитектурное решение
В Build реализован отдельный parser-функционал для REST Trades:
parse_rest_agg_trades(
document: ValidatedRestAggTradesDocument,
) -> tuple[DzengiRestAggTrade, ...]
Parser работает со всем REST-документом целиком и не принимает произвольный dict или list. Его входной контракт уже подтверждает, что документ прошёл Schema Validation.
ValidatedRestAggTradesDocument
↓
parse_rest_agg_trades()
↓
tuple[DzengiRestAggTrade, ...]
Граница ответственности Parser
REST Trade Parser отвечает за:
- проверку типов полей;
- исключение недопустимого использования
boolкак числа; - создание
DzengiRestAggTrade; - сохранение исходных raw numeric значений;
- формирование immutable
tuple; - диагностический путь с индексом элемента.
Parser не отвечает за:
- проверку положительности цены и количества;
- проверку диапазона timestamp;
- проверку неотрицательности aggregate trade id;
- преобразование
str | int | floatвDecimal; - построение canonical
Trade; - определение стороны сделки;
- REST transport, feed registry и runtime orchestration.
Новое исключение
В Build добавлено исключение:
class TradeParseError(MarketDataAcquisitionError):
pass
Оно используется только для ошибок преобразования структурно проверенного REST Trade документа в transport-модели Dzengi.
Build сознательно не добавляет TradeValueError, TradeMappingError, TradeTransportError и TradeFeedRegistryError. Каждое исключение должно появляться только в Build, реализующем соответствующий слой pipeline.
Входной контракт
Parser принимает:
ValidatedRestAggTradesDocument
Контракт был добавлен в Build 060.3:
@dataclass(frozen=True, slots=True)
class ValidatedRestAggTradesDocument:
items: tuple[Mapping[str, object], ...]
Каждый элемент items уже является mapping, содержит обязательные поля, имеет строковые ключи и защищён от изменения через MappingProxyType. Parser не повторяет Schema Validation.
Выходной контракт
Parser возвращает:
tuple[DzengiRestAggTrade, ...]
Каждый элемент является immutable transport-моделью:
@dataclass(frozen=True, slots=True)
class DzengiRestAggTrade:
aggregate_trade_id: int
price: DzengiRawNumeric
quantity: DzengiRawNumeric
timestamp: int
buyer_is_maker: bool
После Build 060.4 REST-ответ уже представлен типизированными transport-объектами, но ещё не преобразован во внутреннюю canonical-модель Dzentra.
Реализация Parser
Основная функция:
def parse_rest_agg_trades(
document: ValidatedRestAggTradesDocument,
) -> tuple[DzengiRestAggTrade, ...]:
return tuple(
_parse_rest_agg_trade_item(
item,
path=f"$[{index}]",
)
for index, item in enumerate(document.items)
)
Функция проходит по всем элементам документа, сохраняет исходный порядок сделок, передаёт индекс элемента в diagnostic path и создаёт immutable tuple.
Пустой документ преобразуется в () и не считается ошибкой.
Преобразование отдельной сделки
Каждый элемент преобразуется функцией _parse_rest_agg_trade_item().
Соответствие внешних полей transport-модели:
| REST поле | Transport поле |
|---|---|
a |
aggregate_trade_id |
p |
price |
q |
quantity |
T |
timestamp |
m |
buyer_is_maker |
Parser не изменяет семантику значений.
Контракт типов
Build фиксирует следующий transport type contract:
a— строгоint, но неbool;p—str | int | float, но неbool;q—str | int | float, но неbool;T— строгоint, но неbool;m— строгоbool.
Проверка диапазонов и смысловой допустимости значений в этот Build не входит.
Почему bool исключается из числовых полей
В Python выражение isinstance(True, int) возвращает True. Без специальной проверки значения True и False могли бы быть ошибочно приняты как идентификатор сделки, timestamp, цена или количество.
Поэтому числовые helper-функции сначала проверяют isinstance(value, bool) и отклоняют такое значение до общей проверки числа. Это является частью transport contract, а не value validation.
Raw numeric preservation
Поля price и quantity используют тип DzengiRawNumeric.
Parser не преобразует значения в Decimal и не нормализует их:
- строка
"1.2300"остаётся строкой"1.2300"; - целое число
5остаётся5; - число
1.25остаётся1.25.
Это сохраняет исходное представление данных биржи до отдельного этапа Value Validation и Normalization.
Diagnostic path
Parser формирует путь к каждому полю с учётом индекса элемента.
Например, ошибка цены во второй сделке будет представлена как:
$[1].p должен быть строкой или числом
Это позволяет точно определить индекс некорректной сделки и имя поля.
Отдельные Trade helper-функции
В Build добавлены специализированные helper-функции:
_trade_required_int()
_trade_required_raw_numeric()
_trade_required_bool()
Они следуют общей parser-инфраструктуре проекта, но возбуждают именно TradeParseError. Переиспользование helper-функций другого feed было бы некорректным, поскольку они используют другие error contracts.
Unit-тесты
В Build добавлен отдельный набор unit-тестов REST Trade Parser.
Проверяются:
- корректное преобразование нескольких сделок;
- сохранение порядка элементов;
- пустой документ;
- immutable
tuple; - создание
DzengiRestAggTrade; - сохранение raw numeric значений;
- отклонение неверных типов
a,T,p,q,m; - отдельное отклонение
boolв числовых полях; - корректный индекс элемента в сообщении об ошибке.
Результаты проверки
Target tests:
46 passed
Полная регрессия проекта:
1038 passed
Дополнительно успешно выполнены:
python -m compileall src
git diff --check
Файл unit-тестов содержит корректный завершающий перевод строки.
Изменённые файлы
В рамках Build изменены только три файла.
src/market_data/acquisition/exceptions.py
Добавлено TradeParseError.
src/market_data/acquisition/adapters/dzengi/parser.py
Добавлены:
parse_rest_agg_trades();_parse_rest_agg_trade_item();_trade_required_int();_trade_required_raw_numeric();_trade_required_bool();- необходимые импорты.
tests/unit/market_data/acquisition/adapters/dzengi/test_parser.py
Добавлены helper создания validated-документа и полный набор позитивных и негативных parser-тестов.
Другие части проекта не изменялись.
Что не изменялось
Build не затрагивает:
src/market_data/acquisition/adapters/dzengi/models.py
src/market_data/acquisition/adapters/dzengi/mapper.py
src/market_data/acquisition/validation/schema.py
src/market_data/acquisition/validation/values.py
src/market_data/acquisition/feeds/
src/market_data/acquisition/runtime/
Также не изменялись существующие parser-функции для ExchangeInfo, Quotes, Candles, WebSocket Quote и WebSocket OHLC.
Архитектурный результат
После Build 060.4 REST Trades pipeline выглядит так:
REST JSON
│
▼
validate_rest_agg_trades_schema()
│
▼
ValidatedRestAggTradesDocument
│
▼
parse_rest_agg_trades()
│
▼
tuple[DzengiRestAggTrade, ...]
Теперь внешний REST-документ структурно проверен, типизирован, преобразован в transport-модели, защищён от изменения и готов к Value Validation.
Итог
Build 060.4 завершает parser-этап REST Trades Acquisition Pipeline.
Система получила строго типизированное преобразование:
ValidatedRestAggTradesDocument
↓
DzengiRestAggTrade
При этом сохранены архитектурные границы:
- Schema Validation проверяет структуру;
- Parser проверяет transport-типы;
- Value Validation будет проверять допустимость значений;
- Mapper будет строить canonical
Trade.
Следующим этапом серии должен стать Build 060.5 — REST Trade Value Validation.