Build 060.3: add REST trade schema validation
This commit is contained in:
@@ -119,6 +119,11 @@ class CandleFeedRegistryError(MarketDataAcquisitionError):
|
||||
pass
|
||||
|
||||
|
||||
# Ошибка структуры документа Trades Feed.
|
||||
class TradeSchemaError(MarketDataAcquisitionError):
|
||||
pass
|
||||
|
||||
|
||||
# Ошибка определения типа входящего WebSocket-сообщения
|
||||
# и выбора специализированного адаптера.
|
||||
class WebSocketMessageRoutingError(MarketDataAcquisitionError):
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user