Build 060.6: add REST trade mapper
This commit is contained in:
@@ -13,6 +13,7 @@ from src.market_data.acquisition.adapters.dzengi.models import (
|
||||
DzengiLotSizeFilter,
|
||||
DzengiMinNotionalFilter,
|
||||
DzengiRawNumeric,
|
||||
DzengiRestAggTrade,
|
||||
DzengiTicker24hrResponse,
|
||||
DzengiWebSocketOhlcEvent,
|
||||
DzengiWebSocketQuoteResponse,
|
||||
@@ -22,11 +23,16 @@ from src.market_data.acquisition.exceptions import (
|
||||
CandleWebSocketMappingError,
|
||||
InstrumentReferenceMappingError,
|
||||
QuoteMappingError,
|
||||
TradeMappingError,
|
||||
)
|
||||
from src.market_data.acquisition.models.candle import Candle
|
||||
from src.market_data.acquisition.models.candle_close import CandleCloseEvent
|
||||
from src.market_data.acquisition.models.instrument import Instrument
|
||||
from src.market_data.acquisition.models.quote import Quote
|
||||
from src.market_data.acquisition.models.trade import (
|
||||
Trade,
|
||||
TradeAggressorSide,
|
||||
)
|
||||
|
||||
|
||||
_DZENGI_SOURCE_NAME = "dzengi"
|
||||
@@ -506,4 +512,98 @@ def _required_candle_decimal(
|
||||
f"Поле {field_name} свечи должно быть конечным числом."
|
||||
)
|
||||
|
||||
return result
|
||||
return result
|
||||
|
||||
|
||||
def map_dzengi_rest_agg_trades_to_trades(
|
||||
trades: tuple[DzengiRestAggTrade, ...],
|
||||
*,
|
||||
symbol: str,
|
||||
) -> tuple[Trade, ...]:
|
||||
"""
|
||||
Преобразовать проверенные transport-модели Dzengi aggTrades
|
||||
в канонический immutable-набор Trade.
|
||||
|
||||
Функция предполагает, что до mapper уже были выполнены:
|
||||
schema validation, parsing и value validation.
|
||||
"""
|
||||
|
||||
return tuple(
|
||||
_map_dzengi_rest_agg_trade(
|
||||
trade,
|
||||
symbol=symbol,
|
||||
)
|
||||
for trade in trades
|
||||
)
|
||||
|
||||
|
||||
def _map_dzengi_rest_agg_trade(
|
||||
trade: DzengiRestAggTrade,
|
||||
*,
|
||||
symbol: str,
|
||||
) -> Trade:
|
||||
return Trade(
|
||||
symbol=symbol.strip(),
|
||||
trade_id=trade.aggregate_trade_id,
|
||||
price=_required_trade_decimal(
|
||||
trade.price,
|
||||
field_name="price",
|
||||
),
|
||||
quantity=_required_trade_decimal(
|
||||
trade.quantity,
|
||||
field_name="quantity",
|
||||
),
|
||||
executed_at=_trade_timestamp_ms_to_utc_datetime(
|
||||
trade.timestamp,
|
||||
),
|
||||
aggressor_side=_trade_aggressor_side(
|
||||
trade.buyer_is_maker,
|
||||
),
|
||||
source=_DZENGI_SOURCE_NAME,
|
||||
)
|
||||
|
||||
|
||||
def _trade_aggressor_side(
|
||||
buyer_is_maker: bool,
|
||||
) -> TradeAggressorSide:
|
||||
if buyer_is_maker:
|
||||
return TradeAggressorSide.SELL
|
||||
|
||||
return TradeAggressorSide.BUY
|
||||
|
||||
|
||||
def _required_trade_decimal(
|
||||
value: DzengiRawNumeric,
|
||||
*,
|
||||
field_name: str,
|
||||
) -> Decimal:
|
||||
try:
|
||||
result = Decimal(str(value))
|
||||
except (InvalidOperation, ValueError) as exc:
|
||||
raise TradeMappingError(
|
||||
f"Поле {field_name} сделки невозможно "
|
||||
"преобразовать в Decimal."
|
||||
) from exc
|
||||
|
||||
if not result.is_finite():
|
||||
raise TradeMappingError(
|
||||
f"Поле {field_name} сделки должно быть "
|
||||
"конечным числом."
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _trade_timestamp_ms_to_utc_datetime(
|
||||
value: int,
|
||||
) -> datetime:
|
||||
try:
|
||||
return datetime.fromtimestamp(
|
||||
value / 1000,
|
||||
tz=timezone.utc,
|
||||
)
|
||||
except (OverflowError, OSError, ValueError) as exc:
|
||||
raise TradeMappingError(
|
||||
"Поле timestamp сделки невозможно "
|
||||
"преобразовать в UTC datetime."
|
||||
) from exc
|
||||
@@ -134,6 +134,12 @@ class TradeValueError(MarketDataAcquisitionError):
|
||||
pass
|
||||
|
||||
|
||||
# Ошибка преобразования raw-модели источника
|
||||
# во внутреннюю модель Trade.
|
||||
class TradeMappingError(MarketDataAcquisitionError):
|
||||
pass
|
||||
|
||||
|
||||
# Ошибка определения типа входящего WebSocket-сообщения
|
||||
# и выбора специализированного адаптера.
|
||||
class WebSocketMessageRoutingError(MarketDataAcquisitionError):
|
||||
|
||||
Reference in New Issue
Block a user