Build 060.20.1: implement Trade Runtime Layer
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
# app/src/market_data/__init__.py
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# app/src/market_data/acquisition/runtime/__init__.py
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# app/src/market_data/acquisition/runtime/trade/trade_runtime_exceptions.py
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""
|
||||||
|
Инфраструктурные исключения Trade Runtime.
|
||||||
|
|
||||||
|
Build 060.20 вводит инфраструктурный слой Trade Runtime.
|
||||||
|
|
||||||
|
Данный модуль содержит только инфраструктурные исключения,
|
||||||
|
связанные с регистрацией и получением Runtime-компонентов.
|
||||||
|
|
||||||
|
Бизнес-исключения отдельных Runtime-модулей (Recovery,
|
||||||
|
Stream Consistency и других) должны оставаться
|
||||||
|
внутри соответствующих подсистем.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class TradeRuntimeError(Exception):
|
||||||
|
"""
|
||||||
|
Базовый класс для всех инфраструктурных исключений Trade Runtime.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class RuntimeAlreadyRegisteredError(TradeRuntimeError):
|
||||||
|
"""
|
||||||
|
Вызывается при попытке повторной регистрации Runtime-компонента
|
||||||
|
под уже существующим ключом.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class RuntimeNotRegisteredError(TradeRuntimeError):
|
||||||
|
"""
|
||||||
|
Вызывается при попытке получить Runtime-компонент,
|
||||||
|
который отсутствует в TradeRuntimeRegistry.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidRuntimeComponentError(TradeRuntimeError):
|
||||||
|
"""
|
||||||
|
Вызывается при попытке зарегистрировать объект,
|
||||||
|
который не может являться Runtime-компонентом.
|
||||||
|
"""
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
# app/src/market_data/acquisition/runtime/trade/trade_runtime_protocol.py
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""
|
||||||
|
Протокол Trade Runtime.
|
||||||
|
|
||||||
|
Build 060.20 вводит единый Runtime Layer для управления
|
||||||
|
жизненным циклом компонентов, обеспечивающих обработку
|
||||||
|
биржевого потока сделок.
|
||||||
|
|
||||||
|
TradeRuntimeProtocol определяет минимальный контракт,
|
||||||
|
которому должна соответствовать реализация Runtime Registry.
|
||||||
|
|
||||||
|
Протокол не описывает внутреннюю реализацию хранения
|
||||||
|
компонентов и не содержит бизнес-логики.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Any, Protocol
|
||||||
|
|
||||||
|
|
||||||
|
class TradeRuntimeProtocol(Protocol):
|
||||||
|
"""
|
||||||
|
Протокол реестра Runtime-компонентов.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def register(self, key: str, component: Any) -> None:
|
||||||
|
"""
|
||||||
|
Регистрирует Runtime-компонент.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key:
|
||||||
|
Уникальный идентификатор Runtime-компонента.
|
||||||
|
|
||||||
|
component:
|
||||||
|
Экземпляр Runtime-компонента.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def unregister(self, key: str) -> None:
|
||||||
|
"""
|
||||||
|
Удаляет Runtime-компонент из реестра.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key:
|
||||||
|
Уникальный идентификатор Runtime-компонента.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def get(self, key: str) -> Any:
|
||||||
|
"""
|
||||||
|
Возвращает зарегистрированный Runtime-компонент.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key:
|
||||||
|
Уникальный идентификатор Runtime-компонента.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Зарегистрированный Runtime-компонент.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeNotRegisteredError:
|
||||||
|
Если компонент отсутствует в реестре.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_registered(self, key: str) -> bool:
|
||||||
|
"""
|
||||||
|
Проверяет наличие Runtime-компонента в реестре.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key:
|
||||||
|
Уникальный идентификатор Runtime-компонента.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True, если компонент зарегистрирован,
|
||||||
|
иначе False.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
"""
|
||||||
|
Полностью очищает реестр Runtime-компонентов.
|
||||||
|
"""
|
||||||
|
...
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
# app/src/market_data/acquisition/runtime/trade/trade_runtime_registry.py
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""
|
||||||
|
Реестр компонентов Trade Runtime.
|
||||||
|
|
||||||
|
Build 060.20 вводит единый инфраструктурный реестр,
|
||||||
|
который владеет экземплярами Runtime-компонентов
|
||||||
|
и предоставляет их по уникальным строковым ключам.
|
||||||
|
|
||||||
|
Реестр не управляет бизнес-состоянием компонентов
|
||||||
|
и не содержит логики Stream Consistency, Recovery
|
||||||
|
или других подсистем обработки потока сделок.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from src.market_data.acquisition.runtime.trade.trade_runtime_exceptions import (
|
||||||
|
InvalidRuntimeComponentError,
|
||||||
|
RuntimeAlreadyRegisteredError,
|
||||||
|
RuntimeNotRegisteredError,
|
||||||
|
)
|
||||||
|
from src.market_data.acquisition.runtime.trade.trade_runtime_protocol import (
|
||||||
|
TradeRuntimeProtocol,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TradeRuntimeRegistry(TradeRuntimeProtocol):
|
||||||
|
"""
|
||||||
|
Реестр Runtime-компонентов потока сделок.
|
||||||
|
|
||||||
|
Каждый компонент регистрируется под уникальным строковым ключом.
|
||||||
|
Повторная регистрация под тем же ключом запрещена.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""
|
||||||
|
Создаёт пустой реестр Runtime-компонентов.
|
||||||
|
"""
|
||||||
|
self._components: dict[str, Any] = {}
|
||||||
|
|
||||||
|
def register(self, key: str, component: Any) -> None:
|
||||||
|
"""
|
||||||
|
Регистрирует Runtime-компонент под уникальным ключом.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key:
|
||||||
|
Уникальный идентификатор Runtime-компонента.
|
||||||
|
|
||||||
|
component:
|
||||||
|
Экземпляр Runtime-компонента.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
InvalidRuntimeComponentError:
|
||||||
|
Если вместо Runtime-компонента передан None.
|
||||||
|
|
||||||
|
RuntimeAlreadyRegisteredError:
|
||||||
|
Если указанный ключ уже используется.
|
||||||
|
"""
|
||||||
|
if component is None:
|
||||||
|
raise InvalidRuntimeComponentError(
|
||||||
|
f"Runtime-компонент для ключа {key!r} не может быть None."
|
||||||
|
)
|
||||||
|
|
||||||
|
if key in self._components:
|
||||||
|
raise RuntimeAlreadyRegisteredError(
|
||||||
|
f"Runtime-компонент с ключом {key!r} уже зарегистрирован."
|
||||||
|
)
|
||||||
|
|
||||||
|
self._components[key] = component
|
||||||
|
|
||||||
|
def unregister(self, key: str) -> None:
|
||||||
|
"""
|
||||||
|
Удаляет Runtime-компонент из реестра.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key:
|
||||||
|
Уникальный идентификатор Runtime-компонента.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeNotRegisteredError:
|
||||||
|
Если компонент с указанным ключом отсутствует.
|
||||||
|
"""
|
||||||
|
if key not in self._components:
|
||||||
|
raise RuntimeNotRegisteredError(
|
||||||
|
f"Runtime-компонент с ключом {key!r} не зарегистрирован."
|
||||||
|
)
|
||||||
|
|
||||||
|
del self._components[key]
|
||||||
|
|
||||||
|
def get(self, key: str) -> Any:
|
||||||
|
"""
|
||||||
|
Возвращает зарегистрированный Runtime-компонент.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key:
|
||||||
|
Уникальный идентификатор Runtime-компонента.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Зарегистрированный Runtime-компонент.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeNotRegisteredError:
|
||||||
|
Если компонент с указанным ключом отсутствует.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self._components[key]
|
||||||
|
except KeyError as error:
|
||||||
|
raise RuntimeNotRegisteredError(
|
||||||
|
f"Runtime-компонент с ключом {key!r} не зарегистрирован."
|
||||||
|
) from error
|
||||||
|
|
||||||
|
def is_registered(self, key: str) -> bool:
|
||||||
|
"""
|
||||||
|
Проверяет наличие Runtime-компонента в реестре.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key:
|
||||||
|
Уникальный идентификатор Runtime-компонента.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True, если компонент зарегистрирован,
|
||||||
|
иначе False.
|
||||||
|
"""
|
||||||
|
return key in self._components
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
"""
|
||||||
|
Удаляет из реестра все зарегистрированные Runtime-компоненты.
|
||||||
|
"""
|
||||||
|
self._components.clear()
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
# app/tests/unit/market_data/acquisition/runtime/trade/test_trade_runtime_registry.py
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from src.market_data.acquisition.runtime.trade.trade_runtime_exceptions import (
|
||||||
|
InvalidRuntimeComponentError,
|
||||||
|
RuntimeAlreadyRegisteredError,
|
||||||
|
RuntimeNotRegisteredError,
|
||||||
|
)
|
||||||
|
from src.market_data.acquisition.runtime.trade.trade_runtime_registry import (
|
||||||
|
TradeRuntimeRegistry,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DummyComponent:
|
||||||
|
"""Тестовый Runtime-компонент."""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def component() -> DummyComponent:
|
||||||
|
"""Создаёт тестовый Runtime-компонент."""
|
||||||
|
return DummyComponent()
|
||||||
|
|
||||||
|
|
||||||
|
def test_registry_is_empty_after_creation() -> None:
|
||||||
|
registry = TradeRuntimeRegistry()
|
||||||
|
|
||||||
|
assert registry.is_registered("component") is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_registered_returns_false_for_unknown_key() -> None:
|
||||||
|
registry = TradeRuntimeRegistry()
|
||||||
|
|
||||||
|
assert registry.is_registered("unknown") is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_register_component(component: DummyComponent) -> None:
|
||||||
|
registry = TradeRuntimeRegistry()
|
||||||
|
|
||||||
|
registry.register("component", component)
|
||||||
|
|
||||||
|
assert registry.is_registered("component") is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_returns_registered_component(
|
||||||
|
component: DummyComponent,
|
||||||
|
) -> None:
|
||||||
|
registry = TradeRuntimeRegistry()
|
||||||
|
|
||||||
|
registry.register("component", component)
|
||||||
|
|
||||||
|
assert registry.get("component") is component
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_registered_returns_true_after_registration(
|
||||||
|
component: DummyComponent,
|
||||||
|
) -> None:
|
||||||
|
registry = TradeRuntimeRegistry()
|
||||||
|
|
||||||
|
registry.register("component", component)
|
||||||
|
|
||||||
|
assert registry.is_registered("component") is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_register_none_raises_invalid_runtime_component_error() -> None:
|
||||||
|
registry = TradeRuntimeRegistry()
|
||||||
|
|
||||||
|
with pytest.raises(InvalidRuntimeComponentError):
|
||||||
|
registry.register("component", None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_register_duplicate_key_raises_runtime_already_registered_error(
|
||||||
|
component: DummyComponent,
|
||||||
|
) -> None:
|
||||||
|
registry = TradeRuntimeRegistry()
|
||||||
|
|
||||||
|
registry.register("component", component)
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeAlreadyRegisteredError):
|
||||||
|
registry.register("component", DummyComponent())
|
||||||
|
|
||||||
|
|
||||||
|
def test_unregister_component(
|
||||||
|
component: DummyComponent,
|
||||||
|
) -> None:
|
||||||
|
registry = TradeRuntimeRegistry()
|
||||||
|
|
||||||
|
registry.register("component", component)
|
||||||
|
registry.unregister("component")
|
||||||
|
|
||||||
|
assert registry.is_registered("component") is False
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeNotRegisteredError):
|
||||||
|
registry.get("component")
|
||||||
|
|
||||||
|
|
||||||
|
def test_unregister_unknown_component_raises_runtime_not_registered_error() -> None:
|
||||||
|
registry = TradeRuntimeRegistry()
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeNotRegisteredError):
|
||||||
|
registry.unregister("unknown")
|
||||||
|
|
||||||
|
|
||||||
|
def test_clear_removes_all_components() -> None:
|
||||||
|
registry = TradeRuntimeRegistry()
|
||||||
|
|
||||||
|
registry.register("component_1", DummyComponent())
|
||||||
|
registry.register("component_2", DummyComponent())
|
||||||
|
registry.register("component_3", DummyComponent())
|
||||||
|
|
||||||
|
registry.clear()
|
||||||
|
|
||||||
|
assert registry.is_registered("component_1") is False
|
||||||
|
assert registry.is_registered("component_2") is False
|
||||||
|
assert registry.is_registered("component_3") is False
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeNotRegisteredError):
|
||||||
|
registry.get("component_1")
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeNotRegisteredError):
|
||||||
|
registry.get("component_2")
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeNotRegisteredError):
|
||||||
|
registry.get("component_3")
|
||||||
2459
docs/migrations/build_060_20_architecture.md
Normal file
2459
docs/migrations/build_060_20_architecture.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user