Build 060.16: add Trade Subscription Layer and subscription builders
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# app/src/market_data/acquisition/adapters/dzengi/__init__.py
|
||||
@@ -0,0 +1,6 @@
|
||||
"""
|
||||
Формирование транспортных подписок Market Data Acquisition.
|
||||
|
||||
Модули пакета преобразуют параметры предметной области в универсальные
|
||||
Runtime-команды и не выполняют сетевые операции самостоятельно.
|
||||
"""
|
||||
111
app/src/market_data/acquisition/subscriptions/trades.py
Normal file
111
app/src/market_data/acquisition/subscriptions/trades.py
Normal file
@@ -0,0 +1,111 @@
|
||||
# app/src/market_data/acquisition/subscriptions/trades.py
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from collections.abc import Sequence
|
||||
from uuid import uuid4
|
||||
|
||||
from src.market_data.acquisition.runtime.runtime_commands import (
|
||||
SubscribeCommand,
|
||||
)
|
||||
from src.market_data.acquisition.runtime.transport_messages import (
|
||||
TransportTextMessage,
|
||||
)
|
||||
|
||||
|
||||
TRADE_SUBSCRIPTION_DESTINATION = "trades.subscribe"
|
||||
TRADE_SUBSCRIPTION_KEY_PREFIX = "trades"
|
||||
|
||||
|
||||
def _normalize_trade_subscription_symbols(
|
||||
symbols: Sequence[str],
|
||||
) -> tuple[str, ...]:
|
||||
"""
|
||||
Нормализовать символы Trade-подписки.
|
||||
|
||||
Удаляет внешние пробелы, пустые значения и дубликаты.
|
||||
Возвращает символы в стабильном лексикографическом порядке.
|
||||
"""
|
||||
|
||||
normalized_symbols = {
|
||||
symbol.strip()
|
||||
for symbol in symbols
|
||||
if symbol.strip()
|
||||
}
|
||||
|
||||
if not normalized_symbols:
|
||||
raise ValueError(
|
||||
"Trade subscription requires at least one non-empty symbol."
|
||||
)
|
||||
|
||||
return tuple(sorted(normalized_symbols))
|
||||
|
||||
|
||||
def build_trade_subscription_key(
|
||||
symbols: Sequence[str],
|
||||
) -> str:
|
||||
"""
|
||||
Построить стабильный идентификатор Trade-подписки.
|
||||
|
||||
Идентификатор не зависит от порядка входных символов и используется
|
||||
Runtime для регистрации и последующего восстановления подписки.
|
||||
"""
|
||||
|
||||
normalized_symbols = _normalize_trade_subscription_symbols(symbols)
|
||||
|
||||
return (
|
||||
f"{TRADE_SUBSCRIPTION_KEY_PREFIX}:"
|
||||
f"{','.join(normalized_symbols)}"
|
||||
)
|
||||
|
||||
|
||||
def build_trade_subscribe_message(
|
||||
symbols: Sequence[str],
|
||||
*,
|
||||
correlation_id: str | None = None,
|
||||
) -> TransportTextMessage:
|
||||
"""
|
||||
Построить текстовое сообщение подписки Dzengi Trade WebSocket.
|
||||
|
||||
correlation_id может быть передан вызывающим кодом для детерминированных
|
||||
тестов и сопоставления ACK. Если значение не передано, создаётся новый UUID.
|
||||
"""
|
||||
|
||||
normalized_symbols = _normalize_trade_subscription_symbols(symbols)
|
||||
|
||||
document = {
|
||||
"correlationId": correlation_id or str(uuid4()),
|
||||
"destination": TRADE_SUBSCRIPTION_DESTINATION,
|
||||
"payload": {
|
||||
"symbols": list(normalized_symbols),
|
||||
},
|
||||
}
|
||||
|
||||
return TransportTextMessage(
|
||||
payload=json.dumps(
|
||||
document,
|
||||
separators=(",", ":"),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def build_trade_subscribe_command(
|
||||
symbols: Sequence[str],
|
||||
*,
|
||||
correlation_id: str | None = None,
|
||||
) -> SubscribeCommand:
|
||||
"""
|
||||
Построить универсальную Runtime-команду подписки на Trade Feed.
|
||||
|
||||
Runtime получает стабильный ключ и готовое транспортное сообщение,
|
||||
не интерпретируя Dzengi-specific JSON.
|
||||
"""
|
||||
|
||||
return SubscribeCommand(
|
||||
subscription_key=build_trade_subscription_key(symbols),
|
||||
message=build_trade_subscribe_message(
|
||||
symbols,
|
||||
correlation_id=correlation_id,
|
||||
),
|
||||
)
|
||||
Reference in New Issue
Block a user