build 059.3: parse websocket OHLC events

This commit is contained in:
2026-07-16 23:21:14 +03:00
parent e0aa04b32a
commit c8c0bb6951
4 changed files with 896 additions and 0 deletions

View File

@@ -19,10 +19,12 @@ from src.market_data.acquisition.adapters.dzengi.models import (
DzengiRawNumeric,
DzengiUnknownFilter,
DzengiTicker24hrResponse,
DzengiWebSocketOhlcEvent,
DzengiWebSocketQuoteResponse,
)
from src.market_data.acquisition.exceptions import (
CandleParseError,
CandleWebSocketParseError,
InstrumentReferenceParseError,
QuoteParseError,
)
@@ -30,6 +32,7 @@ from src.market_data.acquisition.validation.schema import (
ValidatedCandlesDocument,
ValidatedExchangeInfoDocument,
ValidatedQuoteDocument,
ValidatedWebSocketOhlcDocument,
ValidatedWebSocketQuoteDocument,
)
@@ -719,6 +722,101 @@ def _websocket_optional_timestamp(
return _quote_required_int(value, path=path)
# Преобразовать структурно проверенное событие WebSocket OHLC
# в транспортную модель адаптера Dzengi.
def parse_dzengi_websocket_ohlc(
document: ValidatedWebSocketOhlcDocument,
) -> DzengiWebSocketOhlcEvent:
"""
Преобразовать проверенный payload события ``ohlc.event``
в transport-модель Dzengi.
Функция не выполняет предметную проверку значений, не проверяет
допустимость интервала или типа свечи, не преобразует timestamp
в datetime и не выполняет mapping во внутреннюю модель Dzentra.
"""
payload = document.payload
return DzengiWebSocketOhlcEvent(
symbol=_websocket_ohlc_required_string(
payload.get("symbol"),
path="$.payload.symbol",
),
interval=_websocket_ohlc_required_string(
payload.get("interval"),
path="$.payload.interval",
),
candle_type=_websocket_ohlc_required_string(
payload.get("type"),
path="$.payload.type",
),
open_time=_websocket_ohlc_required_int(
payload.get("t"),
path="$.payload.t",
),
open_price=_websocket_ohlc_required_raw_numeric(
payload.get("o"),
path="$.payload.o",
),
high_price=_websocket_ohlc_required_raw_numeric(
payload.get("h"),
path="$.payload.h",
),
low_price=_websocket_ohlc_required_raw_numeric(
payload.get("l"),
path="$.payload.l",
),
close_price=_websocket_ohlc_required_raw_numeric(
payload.get("c"),
path="$.payload.c",
),
)
def _websocket_ohlc_required_string(
value: object,
*,
path: str,
) -> str:
if not isinstance(value, str):
raise CandleWebSocketParseError(
f"{path} должен быть строкой, "
f"получен {type(value).__name__}."
)
return value
def _websocket_ohlc_required_int(
value: object,
*,
path: str,
) -> int:
if isinstance(value, bool) or not isinstance(value, int):
raise CandleWebSocketParseError(
f"{path} должен быть целым числом, "
f"получен {type(value).__name__}."
)
return value
def _websocket_ohlc_required_raw_numeric(
value: object,
*,
path: str,
) -> DzengiRawNumeric:
if isinstance(value, bool) or not isinstance(value, (str, int, float)):
raise CandleWebSocketParseError(
f"{path} должен быть строкой или числом, "
f"получен {type(value).__name__}."
)
return value
# Преобразовать структурно проверенный ответ klines в raw-модели Dzengi.
def parse_candles(
document: ValidatedCandlesDocument,

View File

@@ -83,6 +83,11 @@ class CandleWebSocketSchemaError(MarketDataAcquisitionError):
pass
# Ошибка преобразования проверенного WebSocket OHLC в raw-модель адаптера.
class CandleWebSocketParseError(MarketDataAcquisitionError):
pass
# Ошибка преобразования проверенного документа в raw-модели свечей.
class CandleParseError(MarketDataAcquisitionError):
pass