143 lines
4.7 KiB
Python
143 lines
4.7 KiB
Python
# app/src/market_data/acquisition/registry.py
|
|
|
|
from __future__ import annotations
|
|
|
|
from src.market_data.acquisition.exceptions import (
|
|
InstrumentFeedRegistryError,
|
|
QuoteFeedRegistryError,
|
|
)
|
|
from src.market_data.acquisition.protocol import (
|
|
InstrumentFeedProtocol,
|
|
QuoteFeedProtocol,
|
|
)
|
|
|
|
|
|
# Реестр доступных Feed справочника инструментов.
|
|
class InstrumentFeedRegistry:
|
|
def __init__(self) -> None:
|
|
self._feeds: dict[str, InstrumentFeedProtocol] = {}
|
|
|
|
def register(
|
|
self,
|
|
source_name: str,
|
|
feed: InstrumentFeedProtocol,
|
|
) -> None:
|
|
"""
|
|
Зарегистрировать Instrument Feed для указанного источника.
|
|
|
|
Повторная регистрация того же имени запрещена, чтобы исключить
|
|
неявную замену production-зависимости.
|
|
"""
|
|
|
|
normalized_source_name = self._normalize_source_name(source_name)
|
|
|
|
if not isinstance(feed, InstrumentFeedProtocol):
|
|
raise InstrumentFeedRegistryError(
|
|
f"Объект для источника '{normalized_source_name}' "
|
|
"не соответствует InstrumentFeedProtocol."
|
|
)
|
|
|
|
if normalized_source_name in self._feeds:
|
|
raise InstrumentFeedRegistryError(
|
|
f"Instrument Feed для источника "
|
|
f"'{normalized_source_name}' уже зарегистрирован."
|
|
)
|
|
|
|
self._feeds[normalized_source_name] = feed
|
|
|
|
def get(
|
|
self,
|
|
source_name: str,
|
|
) -> InstrumentFeedProtocol:
|
|
"""Вернуть зарегистрированный Instrument Feed по имени источника."""
|
|
|
|
normalized_source_name = self._normalize_source_name(source_name)
|
|
|
|
feed = self._feeds.get(normalized_source_name)
|
|
|
|
if feed is None:
|
|
raise InstrumentFeedRegistryError(
|
|
f"Instrument Feed для источника "
|
|
f"'{normalized_source_name}' не зарегистрирован."
|
|
)
|
|
|
|
return feed
|
|
|
|
def _normalize_source_name(
|
|
self,
|
|
source_name: str,
|
|
) -> str:
|
|
normalized_source_name = source_name.strip()
|
|
|
|
if not normalized_source_name:
|
|
raise InstrumentFeedRegistryError(
|
|
"Имя источника Instrument Feed не должно быть пустым."
|
|
)
|
|
|
|
return normalized_source_name
|
|
|
|
|
|
# Реестр доступных потоков текущих котировок.
|
|
class QuoteFeedRegistry:
|
|
def __init__(self) -> None:
|
|
self._feeds: dict[str, QuoteFeedProtocol] = {}
|
|
|
|
def register(
|
|
self,
|
|
source_name: str,
|
|
feed: QuoteFeedProtocol,
|
|
) -> None:
|
|
"""
|
|
Зарегистрировать Quotes Feed для указанного источника.
|
|
|
|
Повторная регистрация того же имени запрещена, чтобы исключить
|
|
неявную замену production-зависимости.
|
|
"""
|
|
|
|
normalized_source_name = self._normalize_source_name(source_name)
|
|
|
|
if not isinstance(feed, QuoteFeedProtocol):
|
|
raise QuoteFeedRegistryError(
|
|
f"Объект для источника '{normalized_source_name}' "
|
|
"не соответствует QuoteFeedProtocol."
|
|
)
|
|
|
|
if normalized_source_name in self._feeds:
|
|
raise QuoteFeedRegistryError(
|
|
f"Quotes Feed для источника "
|
|
f"'{normalized_source_name}' уже зарегистрирован."
|
|
)
|
|
|
|
self._feeds[normalized_source_name] = feed
|
|
|
|
def get(
|
|
self,
|
|
source_name: str,
|
|
) -> QuoteFeedProtocol:
|
|
"""Вернуть зарегистрированный Quotes Feed по имени источника."""
|
|
|
|
normalized_source_name = self._normalize_source_name(source_name)
|
|
|
|
feed = self._feeds.get(normalized_source_name)
|
|
|
|
if feed is None:
|
|
raise QuoteFeedRegistryError(
|
|
f"Quotes Feed для источника "
|
|
f"'{normalized_source_name}' не зарегистрирован."
|
|
)
|
|
|
|
return feed
|
|
|
|
def _normalize_source_name(
|
|
self,
|
|
source_name: str,
|
|
) -> str:
|
|
normalized_source_name = source_name.strip()
|
|
|
|
if not normalized_source_name:
|
|
raise QuoteFeedRegistryError(
|
|
"Имя источника Quotes Feed не должно быть пустым."
|
|
)
|
|
|
|
return normalized_source_name
|