build 059.4: validate websocket OHLC values
This commit is contained in:
@@ -88,6 +88,11 @@ class CandleWebSocketParseError(MarketDataAcquisitionError):
|
||||
pass
|
||||
|
||||
|
||||
# Ошибка допустимости значений WebSocket OHLC-события.
|
||||
class CandleWebSocketValueError(MarketDataAcquisitionError):
|
||||
pass
|
||||
|
||||
|
||||
# Ошибка преобразования проверенного документа в raw-модели свечей.
|
||||
class CandleParseError(MarketDataAcquisitionError):
|
||||
pass
|
||||
|
||||
@@ -15,15 +15,34 @@ from src.market_data.acquisition.adapters.dzengi.models import (
|
||||
DzengiUnknownFilter,
|
||||
DzengiKlinesResponse,
|
||||
DzengiTicker24hrResponse,
|
||||
DzengiWebSocketOhlcEvent,
|
||||
DzengiWebSocketQuoteResponse,
|
||||
)
|
||||
from src.market_data.acquisition.exceptions import (
|
||||
CandleValueError,
|
||||
CandleWebSocketValueError,
|
||||
InstrumentReferenceValueError,
|
||||
QuoteValueError,
|
||||
)
|
||||
|
||||
|
||||
_SUPPORTED_WEBSOCKET_OHLC_INTERVALS = frozenset(
|
||||
{
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
}
|
||||
)
|
||||
|
||||
_SUPPORTED_WEBSOCKET_OHLC_TYPES = frozenset(
|
||||
{
|
||||
"classic",
|
||||
"heikin-ashi",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def validate_exchange_info_values(
|
||||
response: DzengiExchangeInfoResponse,
|
||||
) -> None:
|
||||
@@ -553,6 +572,126 @@ def validate_dzengi_websocket_quote_values(
|
||||
"$.payload.timestamp должно быть больше нуля."
|
||||
)
|
||||
|
||||
|
||||
def validate_dzengi_websocket_ohlc_values(
|
||||
event: DzengiWebSocketOhlcEvent,
|
||||
) -> None:
|
||||
"""
|
||||
Проверить предметную допустимость значений Dzengi WebSocket OHLC.
|
||||
|
||||
Функция не изменяет transport-модель, не преобразует timestamp
|
||||
в datetime и не выполняет mapping во внутреннюю модель Dzentra.
|
||||
"""
|
||||
|
||||
if not event.symbol.strip():
|
||||
raise CandleWebSocketValueError(
|
||||
"$.payload.symbol не должен быть пустым."
|
||||
)
|
||||
|
||||
if event.interval not in _SUPPORTED_WEBSOCKET_OHLC_INTERVALS:
|
||||
supported = ", ".join(
|
||||
sorted(_SUPPORTED_WEBSOCKET_OHLC_INTERVALS)
|
||||
)
|
||||
raise CandleWebSocketValueError(
|
||||
"$.payload.interval содержит неподдерживаемый интервал "
|
||||
f"'{event.interval}'. Поддерживаются: {supported}."
|
||||
)
|
||||
|
||||
if event.candle_type not in _SUPPORTED_WEBSOCKET_OHLC_TYPES:
|
||||
supported = ", ".join(
|
||||
sorted(_SUPPORTED_WEBSOCKET_OHLC_TYPES)
|
||||
)
|
||||
raise CandleWebSocketValueError(
|
||||
"$.payload.type содержит неподдерживаемый тип свечи "
|
||||
f"'{event.candle_type}'. Поддерживаются: {supported}."
|
||||
)
|
||||
|
||||
if event.open_time <= 0:
|
||||
raise CandleWebSocketValueError(
|
||||
"$.payload.t должно быть целым числом больше нуля."
|
||||
)
|
||||
|
||||
open_price = _websocket_ohlc_positive_decimal(
|
||||
event.open_price,
|
||||
path="$.payload.o",
|
||||
)
|
||||
high_price = _websocket_ohlc_positive_decimal(
|
||||
event.high_price,
|
||||
path="$.payload.h",
|
||||
)
|
||||
low_price = _websocket_ohlc_positive_decimal(
|
||||
event.low_price,
|
||||
path="$.payload.l",
|
||||
)
|
||||
close_price = _websocket_ohlc_positive_decimal(
|
||||
event.close_price,
|
||||
path="$.payload.c",
|
||||
)
|
||||
|
||||
if high_price < low_price:
|
||||
raise CandleWebSocketValueError(
|
||||
"$.payload.h не должно быть меньше $.payload.l."
|
||||
)
|
||||
|
||||
if high_price < open_price:
|
||||
raise CandleWebSocketValueError(
|
||||
"$.payload.h не должно быть меньше $.payload.o."
|
||||
)
|
||||
|
||||
if high_price < close_price:
|
||||
raise CandleWebSocketValueError(
|
||||
"$.payload.h не должно быть меньше $.payload.c."
|
||||
)
|
||||
|
||||
if low_price > open_price:
|
||||
raise CandleWebSocketValueError(
|
||||
"$.payload.l не должно превышать $.payload.o."
|
||||
)
|
||||
|
||||
if low_price > close_price:
|
||||
raise CandleWebSocketValueError(
|
||||
"$.payload.l не должно превышать $.payload.c."
|
||||
)
|
||||
|
||||
|
||||
def _websocket_ohlc_positive_decimal(
|
||||
value: DzengiRawNumeric,
|
||||
*,
|
||||
path: str,
|
||||
) -> Decimal:
|
||||
decimal_value = _websocket_ohlc_decimal(
|
||||
value,
|
||||
path=path,
|
||||
)
|
||||
|
||||
if decimal_value <= 0:
|
||||
raise CandleWebSocketValueError(
|
||||
f"{path} должно быть больше нуля."
|
||||
)
|
||||
|
||||
return decimal_value
|
||||
|
||||
|
||||
def _websocket_ohlc_decimal(
|
||||
value: DzengiRawNumeric,
|
||||
*,
|
||||
path: str,
|
||||
) -> Decimal:
|
||||
try:
|
||||
decimal_value = Decimal(str(value))
|
||||
except (InvalidOperation, ValueError) as exc:
|
||||
raise CandleWebSocketValueError(
|
||||
f"{path} должно быть корректным числом."
|
||||
) from exc
|
||||
|
||||
if not decimal_value.is_finite():
|
||||
raise CandleWebSocketValueError(
|
||||
f"{path} должно быть конечным числом."
|
||||
)
|
||||
|
||||
return decimal_value
|
||||
|
||||
|
||||
def validate_candles_values(
|
||||
response: DzengiKlinesResponse,
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user