Build 060.3: add REST trade schema validation

This commit is contained in:
2026-07-18 14:16:47 +03:00
parent 013efd8a97
commit c0f69ee94a
4 changed files with 896 additions and 1 deletions

View File

@@ -119,6 +119,11 @@ class CandleFeedRegistryError(MarketDataAcquisitionError):
pass
# Ошибка структуры документа Trades Feed.
class TradeSchemaError(MarketDataAcquisitionError):
pass
# Ошибка определения типа входящего WebSocket-сообщения
# и выбора специализированного адаптера.
class WebSocketMessageRoutingError(MarketDataAcquisitionError):

View File

@@ -11,6 +11,7 @@ from src.market_data.acquisition.exceptions import (
CandleWebSocketSchemaError,
InstrumentReferenceSchemaError,
QuoteSchemaError,
TradeSchemaError,
)
@@ -595,6 +596,94 @@ def _require_candle_mapping(
return value
# Структурно проверенное представление ответа Dzengi /api/v2/aggTrades.
@dataclass(frozen=True, slots=True)
class ValidatedRestAggTradesDocument:
items: tuple[Mapping[str, object], ...]
def validate_rest_agg_trades_schema(
document: object,
) -> ValidatedRestAggTradesDocument:
"""
Проверить структуру ответа Dzengi aggTrades без проверки значений.
Ожидается корневой JSON-массив, каждый элемент которого является
JSON-объектом с обязательными полями:
- a — идентификатор агрегированной сделки;
- p — цена;
- q — количество;
- T — временная метка;
- m — признак buyer is maker.
Валидатор проверяет только структуру документа. Типы и допустимость
значений должны проверяться последующими слоями acquisition pipeline.
"""
if not isinstance(document, list):
raise TradeSchemaError(
"$ должен быть JSON-массивом, "
f"получен {type(document).__name__}."
)
validated_items: list[Mapping[str, object]] = []
for index, item in enumerate(document):
path = f"$[{index}]"
mapping = _require_trade_mapping(
item,
path=path,
)
_validate_rest_agg_trade_item(
mapping,
path=path,
)
validated_items.append(
MappingProxyType(dict(mapping))
)
return ValidatedRestAggTradesDocument(
items=tuple(validated_items),
)
def _validate_rest_agg_trade_item(
item: Mapping[str, object],
*,
path: str,
) -> None:
for key in ("a", "p", "q", "T", "m"):
if key not in item:
raise TradeSchemaError(
f"{path}.{key} отсутствует в документе aggTrades."
)
def _require_trade_mapping(
value: object,
*,
path: str,
) -> Mapping[str, object]:
if not isinstance(value, dict):
raise TradeSchemaError(
f"{path} должен быть JSON-объектом, "
f"получен {type(value).__name__}."
)
for key in value:
if not isinstance(key, str):
raise TradeSchemaError(
f"{path} содержит нестроковый ключ "
f"типа {type(key).__name__}."
)
return value
# Структурно проверенное представление события Dzengi WebSocket ohlc.event.
@dataclass(frozen=True, slots=True)
class ValidatedWebSocketOhlcDocument: