Build 060.10: add WebSocket Trade Schema Validation

This commit is contained in:
2026-07-19 17:25:51 +03:00
parent 0b4edca287
commit 034f5984ca
3 changed files with 1427 additions and 0 deletions

View File

@@ -811,4 +811,132 @@ def _require_websocket_ohlc_mapping(
f"типа {type(key).__name__}."
)
return value
# Структурно проверенное представление события Dzengi WebSocket internal.trade.
@dataclass(frozen=True, slots=True)
class ValidatedWebSocketTradeDocument:
payload: Mapping[str, object]
status: object
destination: object
correlation_id: object | None
def validate_dzengi_websocket_trade_schema(
document: object,
) -> ValidatedWebSocketTradeDocument:
"""
Проверить структуру события Dzengi WebSocket Trade без проверки значений.
Ожидаемый runtime-контракт:
{
"status": "OK",
"destination": "internal.trade",
"payload": {
"buyer": false,
"id": 2134846831,
"orderId": "00a02503-0079-54c4-0000-000081e62b58",
"price": 64555.55,
"size": 0.002,
"symbol": "BTC/USD_LEVERAGE",
"ts": 1784218012030
}
}
Функция проверяет только структуру сообщения:
- корневой JSON-объект;
- наличие status;
- наличие destination;
- наличие payload;
- обязательный набор полей Trade;
- строковые ключи объектов.
Функция не проверяет предметные значения, не преобразует timestamp,
не преобразует цену и объём и не создаёт transport-модель адаптера.
"""
root = _require_websocket_trade_mapping(
document,
path="$",
)
if "status" not in root:
raise TradeSchemaError(
"$.status отсутствует в событии WebSocket Trade."
)
if "destination" not in root:
raise TradeSchemaError(
"$.destination отсутствует в событии WebSocket Trade."
)
if "payload" not in root:
raise TradeSchemaError(
"$.payload отсутствует в событии WebSocket Trade."
)
payload = _require_websocket_trade_mapping(
root.get("payload"),
path="$.payload",
)
_validate_websocket_trade_payload(payload)
return ValidatedWebSocketTradeDocument(
payload=MappingProxyType(dict(payload)),
status=root.get("status"),
destination=root.get("destination"),
correlation_id=root.get("correlationId"),
)
def _validate_websocket_trade_payload(
payload: Mapping[str, object],
) -> None:
required_fields = (
"id",
"price",
"size",
"ts",
"symbol",
"buyer",
"orderId",
)
missing_fields = tuple(
field_name
for field_name in required_fields
if field_name not in payload
)
if missing_fields:
formatted_fields = ", ".join(missing_fields)
raise TradeSchemaError(
"$.payload не содержит обязательные поля WebSocket Trade: "
f"{formatted_fields}."
)
def _require_websocket_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