Build 060.16: add Trade Subscription Layer and subscription builders

This commit is contained in:
2026-07-20 00:49:45 +03:00
parent 02868561d6
commit be6beac560
6 changed files with 1793 additions and 0 deletions

View File

@@ -0,0 +1 @@
# app/src/market_data/acquisition/adapters/dzengi/__init__.py

View File

@@ -0,0 +1,6 @@
"""
Формирование транспортных подписок Market Data Acquisition.
Модули пакета преобразуют параметры предметной области в универсальные
Runtime-команды и не выполняют сетевые операции самостоятельно.
"""

View 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,
),
)