Build 060.20_1: align Trade Stream state ownership
This commit is contained in:
@@ -8,10 +8,10 @@ from src.market_data.acquisition.consistency.trade_stream_protocol import (
|
|||||||
from src.market_data.acquisition.consistency.trade_stream_state import (
|
from src.market_data.acquisition.consistency.trade_stream_state import (
|
||||||
TradeStreamState,
|
TradeStreamState,
|
||||||
)
|
)
|
||||||
from src.market_data.acquisition.models.trade import Trade
|
from src.market_data.acquisition.consistency.trade_stream_state_store_protocol import (
|
||||||
from src.market_data.acquisition.runtime.trade.trade_runtime_protocol import (
|
TradeStreamStateStoreProtocol,
|
||||||
TradeRuntimeProtocol,
|
|
||||||
)
|
)
|
||||||
|
from src.market_data.acquisition.models.trade import Trade
|
||||||
|
|
||||||
|
|
||||||
class TradeStreamConsistencyController(
|
class TradeStreamConsistencyController(
|
||||||
@@ -20,18 +20,16 @@ class TradeStreamConsistencyController(
|
|||||||
"""
|
"""
|
||||||
Контроллер проверки согласованности Canonical Trade Stream.
|
Контроллер проверки согласованности Canonical Trade Stream.
|
||||||
|
|
||||||
Для каждого торгового инструмента поддерживается
|
Для каждого торгового инструмента используется
|
||||||
независимое состояние проверки, которое хранится
|
специализированное хранилище состояний
|
||||||
в Trade Runtime Registry.
|
Trade Stream Consistency.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_RUNTIME_NAMESPACE = "consistency"
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
runtime: TradeRuntimeProtocol,
|
state_store: TradeStreamStateStoreProtocol,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._runtime = runtime
|
self._state_store = state_store
|
||||||
|
|
||||||
def accept(
|
def accept(
|
||||||
self,
|
self,
|
||||||
@@ -45,24 +43,11 @@ class TradeStreamConsistencyController(
|
|||||||
self,
|
self,
|
||||||
symbol: str,
|
symbol: str,
|
||||||
) -> TradeStreamState:
|
) -> TradeStreamState:
|
||||||
key = self._runtime_key(symbol)
|
|
||||||
|
|
||||||
if self._runtime.is_registered(key):
|
|
||||||
return self._runtime.get(key)
|
|
||||||
|
|
||||||
state = TradeStreamState(symbol=symbol)
|
|
||||||
self._runtime.register(key, state)
|
|
||||||
|
|
||||||
return state
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _runtime_key(
|
|
||||||
cls,
|
|
||||||
symbol: str,
|
|
||||||
) -> str:
|
|
||||||
"""
|
|
||||||
Возвращает ключ Runtime Registry
|
|
||||||
для состояния проверки Trade Stream.
|
|
||||||
"""
|
"""
|
||||||
|
Возвращает состояние проверки Trade Stream
|
||||||
|
для указанного торгового инструмента.
|
||||||
|
|
||||||
return f"{cls._RUNTIME_NAMESPACE}:{symbol}"
|
При первом обращении состояние автоматически
|
||||||
|
создаётся специализированным хранилищем.
|
||||||
|
"""
|
||||||
|
return self._state_store.get_or_create(symbol)
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
# app/src/market_data/acquisition/consistency/trade_stream_state_store.py
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""
|
||||||
|
Хранилище состояний Trade Stream Consistency.
|
||||||
|
|
||||||
|
Build 060.20.1 переносит владение инфраструктурным состоянием
|
||||||
|
из общего Runtime Registry в специализированное хранилище
|
||||||
|
подсистемы проверки согласованности потока сделок.
|
||||||
|
|
||||||
|
Хранилище отвечает исключительно за жизненный цикл объектов
|
||||||
|
TradeStreamState и не содержит логики проверки последовательности
|
||||||
|
или восстановления потока сделок.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from src.market_data.acquisition.consistency.trade_stream_state import (
|
||||||
|
TradeStreamState,
|
||||||
|
)
|
||||||
|
from src.market_data.acquisition.consistency.trade_stream_state_store_exceptions import (
|
||||||
|
TradeStreamStateNotFoundError,
|
||||||
|
)
|
||||||
|
from src.market_data.acquisition.consistency.trade_stream_state_store_protocol import (
|
||||||
|
TradeStreamStateStoreProtocol,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TradeStreamStateStore(TradeStreamStateStoreProtocol):
|
||||||
|
"""
|
||||||
|
Хранилище состояний Trade Stream.
|
||||||
|
|
||||||
|
Для каждого торгового инструмента существует
|
||||||
|
единственный экземпляр TradeStreamState.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
"""
|
||||||
|
Создаёт пустое хранилище состояний.
|
||||||
|
"""
|
||||||
|
self._states: dict[str, TradeStreamState] = {}
|
||||||
|
|
||||||
|
def get_or_create(
|
||||||
|
self,
|
||||||
|
symbol: str,
|
||||||
|
) -> TradeStreamState:
|
||||||
|
"""
|
||||||
|
Возвращает существующее состояние либо создаёт новое.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol:
|
||||||
|
Идентификатор торгового инструмента.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Экземпляр TradeStreamState.
|
||||||
|
"""
|
||||||
|
state = self._states.get(symbol)
|
||||||
|
|
||||||
|
if state is None:
|
||||||
|
state = TradeStreamState(symbol=symbol)
|
||||||
|
self._states[symbol] = state
|
||||||
|
|
||||||
|
return state
|
||||||
|
|
||||||
|
def get(
|
||||||
|
self,
|
||||||
|
symbol: str,
|
||||||
|
) -> TradeStreamState:
|
||||||
|
"""
|
||||||
|
Возвращает существующее состояние.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol:
|
||||||
|
Идентификатор торгового инструмента.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TradeStreamStateNotFoundError:
|
||||||
|
Если состояние отсутствует.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self._states[symbol]
|
||||||
|
except KeyError as error:
|
||||||
|
raise TradeStreamStateNotFoundError(
|
||||||
|
f"Состояние Trade Stream для символа {symbol!r} не найдено."
|
||||||
|
) from error
|
||||||
|
|
||||||
|
def contains(
|
||||||
|
self,
|
||||||
|
symbol: str,
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Проверяет наличие состояния.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol:
|
||||||
|
Идентификатор торгового инструмента.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True, если состояние существует.
|
||||||
|
"""
|
||||||
|
return symbol in self._states
|
||||||
|
|
||||||
|
def remove(
|
||||||
|
self,
|
||||||
|
symbol: str,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Удаляет состояние.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol:
|
||||||
|
Идентификатор торгового инструмента.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TradeStreamStateNotFoundError:
|
||||||
|
Если состояние отсутствует.
|
||||||
|
"""
|
||||||
|
if symbol not in self._states:
|
||||||
|
raise TradeStreamStateNotFoundError(
|
||||||
|
f"Состояние Trade Stream для символа "
|
||||||
|
f"{symbol!r} не найдено."
|
||||||
|
)
|
||||||
|
|
||||||
|
del self._states[symbol]
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
"""
|
||||||
|
Полностью очищает хранилище состояний.
|
||||||
|
"""
|
||||||
|
self._states.clear()
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# app/src/market_data/acquisition/consistency/trade_stream_state_store_exceptions.py
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""
|
||||||
|
Исключения хранилища состояний Trade Stream Consistency.
|
||||||
|
|
||||||
|
Build 060.20.1 вводит специализированное хранилище
|
||||||
|
состояния проверки согласованности потока сделок.
|
||||||
|
|
||||||
|
Исключения относятся исключительно к управлению
|
||||||
|
TradeStreamState и не описывают ошибки проверки
|
||||||
|
последовательности сделок.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class TradeStreamStateStoreError(Exception):
|
||||||
|
"""
|
||||||
|
Базовое исключение хранилища состояний Trade Stream.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class TradeStreamStateNotFoundError(
|
||||||
|
TradeStreamStateStoreError,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Состояние торгового инструмента отсутствует.
|
||||||
|
"""
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# app/src/market_data/acquisition/consistency/trade_stream_state_store_protocol.py
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""
|
||||||
|
Протокол хранилища состояния Trade Stream Consistency.
|
||||||
|
|
||||||
|
Build 060.20.1 заменяет универсальный Trade Runtime Registry
|
||||||
|
специализированным типизированным хранилищем состояния
|
||||||
|
Canonical Trade Stream.
|
||||||
|
|
||||||
|
TradeStreamStateStoreProtocol определяет минимальный контракт
|
||||||
|
доступа к состоянию проверки согласованности потока сделок.
|
||||||
|
|
||||||
|
Протокол не описывает внутреннюю реализацию хранения
|
||||||
|
и не содержит бизнес-логики Trade Stream Consistency.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Protocol, runtime_checkable
|
||||||
|
|
||||||
|
from src.market_data.acquisition.consistency.trade_stream_state import (
|
||||||
|
TradeStreamState,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@runtime_checkable
|
||||||
|
class TradeStreamStateStoreProtocol(Protocol):
|
||||||
|
"""
|
||||||
|
Протокол хранилища состояний Trade Stream Consistency.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_or_create(
|
||||||
|
self,
|
||||||
|
symbol: str,
|
||||||
|
) -> TradeStreamState:
|
||||||
|
"""
|
||||||
|
Возвращает существующее состояние торгового инструмента
|
||||||
|
либо создаёт новое состояние при первом обращении.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol:
|
||||||
|
Идентификатор торгового инструмента.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Существующий либо созданный TradeStreamState.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def get(
|
||||||
|
self,
|
||||||
|
symbol: str,
|
||||||
|
) -> TradeStreamState:
|
||||||
|
"""
|
||||||
|
Возвращает существующее состояние торгового инструмента.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol:
|
||||||
|
Идентификатор торгового инструмента.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Зарегистрированный TradeStreamState.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TradeStreamStateNotFoundError:
|
||||||
|
Если состояние торгового инструмента отсутствует.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def contains(
|
||||||
|
self,
|
||||||
|
symbol: str,
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Проверяет наличие состояния торгового инструмента.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol:
|
||||||
|
Идентификатор торгового инструмента.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True, если состояние существует,
|
||||||
|
иначе False.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def remove(
|
||||||
|
self,
|
||||||
|
symbol: str,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Удаляет состояние торгового инструмента.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol:
|
||||||
|
Идентификатор торгового инструмента.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TradeStreamStateNotFoundError:
|
||||||
|
Если состояние торгового инструмента отсутствует.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
"""
|
||||||
|
Полностью очищает хранилище состояний.
|
||||||
|
"""
|
||||||
|
...
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
# 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-компонентом.
|
|
||||||
"""
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
# 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-компонентов.
|
|
||||||
"""
|
|
||||||
...
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
# 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()
|
|
||||||
@@ -21,22 +21,22 @@ from src.market_data.acquisition.models.trade import (
|
|||||||
Trade,
|
Trade,
|
||||||
TradeAggressorSide,
|
TradeAggressorSide,
|
||||||
)
|
)
|
||||||
from src.market_data.acquisition.runtime.trade.trade_runtime_registry import (
|
from src.market_data.acquisition.consistency.trade_stream_state_store import (
|
||||||
TradeRuntimeRegistry,
|
TradeStreamStateStore,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def runtime() -> TradeRuntimeRegistry:
|
def state_store() -> TradeStreamStateStore:
|
||||||
return TradeRuntimeRegistry()
|
return TradeStreamStateStore()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def controller(
|
def controller(
|
||||||
runtime: TradeRuntimeRegistry,
|
state_store: TradeStreamStateStore,
|
||||||
) -> TradeStreamConsistencyController:
|
) -> TradeStreamConsistencyController:
|
||||||
return TradeStreamConsistencyController(
|
return TradeStreamConsistencyController(
|
||||||
runtime=runtime,
|
state_store=state_store,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -68,32 +68,32 @@ def _trade(
|
|||||||
|
|
||||||
def test_creates_state_for_first_symbol(
|
def test_creates_state_for_first_symbol(
|
||||||
controller: TradeStreamConsistencyController,
|
controller: TradeStreamConsistencyController,
|
||||||
runtime: TradeRuntimeRegistry,
|
state_store: TradeStreamStateStore,
|
||||||
) -> None:
|
) -> None:
|
||||||
trade = _trade(symbol="BTCUSD")
|
trade = _trade(symbol="BTCUSD")
|
||||||
|
|
||||||
result = controller.accept(trade)
|
result = controller.accept(trade)
|
||||||
|
|
||||||
assert result == trade
|
assert result == trade
|
||||||
assert runtime.is_registered("consistency:BTCUSD")
|
assert state_store.contains("BTCUSD")
|
||||||
|
|
||||||
|
|
||||||
def test_registers_trade_stream_state(
|
def test_registers_trade_stream_state(
|
||||||
controller: TradeStreamConsistencyController,
|
controller: TradeStreamConsistencyController,
|
||||||
runtime: TradeRuntimeRegistry,
|
state_store: TradeStreamStateStore,
|
||||||
) -> None:
|
) -> None:
|
||||||
controller.accept(
|
controller.accept(
|
||||||
_trade(symbol="BTCUSD"),
|
_trade(symbol="BTCUSD"),
|
||||||
)
|
)
|
||||||
|
|
||||||
state = runtime.get("consistency:BTCUSD")
|
state = state_store.get("BTCUSD")
|
||||||
|
|
||||||
assert isinstance(state, TradeStreamState)
|
assert isinstance(state, TradeStreamState)
|
||||||
|
|
||||||
|
|
||||||
def test_reuses_state_for_same_symbol(
|
def test_reuses_state_for_same_symbol(
|
||||||
controller: TradeStreamConsistencyController,
|
controller: TradeStreamConsistencyController,
|
||||||
runtime: TradeRuntimeRegistry,
|
state_store: TradeStreamStateStore,
|
||||||
) -> None:
|
) -> None:
|
||||||
first_trade = _trade(
|
first_trade = _trade(
|
||||||
symbol="BTCUSD",
|
symbol="BTCUSD",
|
||||||
@@ -105,10 +105,10 @@ def test_reuses_state_for_same_symbol(
|
|||||||
)
|
)
|
||||||
|
|
||||||
controller.accept(first_trade)
|
controller.accept(first_trade)
|
||||||
first_state = runtime.get("consistency:BTCUSD")
|
first_state = state_store.get("BTCUSD")
|
||||||
|
|
||||||
result = controller.accept(second_trade)
|
result = controller.accept(second_trade)
|
||||||
second_state = runtime.get("consistency:BTCUSD")
|
second_state = state_store.get("BTCUSD")
|
||||||
|
|
||||||
assert result == second_trade
|
assert result == second_trade
|
||||||
assert second_state is first_state
|
assert second_state is first_state
|
||||||
@@ -116,7 +116,7 @@ def test_reuses_state_for_same_symbol(
|
|||||||
|
|
||||||
def test_keeps_independent_state_per_symbol(
|
def test_keeps_independent_state_per_symbol(
|
||||||
controller: TradeStreamConsistencyController,
|
controller: TradeStreamConsistencyController,
|
||||||
runtime: TradeRuntimeRegistry,
|
state_store: TradeStreamStateStore,
|
||||||
) -> None:
|
) -> None:
|
||||||
btc_trade = _trade(
|
btc_trade = _trade(
|
||||||
symbol="BTCUSD",
|
symbol="BTCUSD",
|
||||||
@@ -130,8 +130,8 @@ def test_keeps_independent_state_per_symbol(
|
|||||||
btc_result = controller.accept(btc_trade)
|
btc_result = controller.accept(btc_trade)
|
||||||
eth_result = controller.accept(eth_trade)
|
eth_result = controller.accept(eth_trade)
|
||||||
|
|
||||||
btc_state = runtime.get("consistency:BTCUSD")
|
btc_state = state_store.get("BTCUSD")
|
||||||
eth_state = runtime.get("consistency:ETHUSD")
|
eth_state = state_store.get("ETHUSD")
|
||||||
|
|
||||||
assert btc_result == btc_trade
|
assert btc_result == btc_trade
|
||||||
assert eth_result == eth_trade
|
assert eth_result == eth_trade
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
# app/tests/unit/market_data/acquisition/consistency/test_trade_stream_state_store.py
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from src.market_data.acquisition.consistency.trade_stream_state import (
|
||||||
|
TradeStreamState,
|
||||||
|
)
|
||||||
|
from src.market_data.acquisition.consistency.trade_stream_state_store import (
|
||||||
|
TradeStreamStateStore,
|
||||||
|
)
|
||||||
|
from src.market_data.acquisition.consistency.trade_stream_state_store_exceptions import (
|
||||||
|
TradeStreamStateNotFoundError,
|
||||||
|
)
|
||||||
|
from src.market_data.acquisition.consistency.trade_stream_state_store_protocol import (
|
||||||
|
TradeStreamStateStoreProtocol,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_store_implements_protocol() -> None:
|
||||||
|
store = TradeStreamStateStore()
|
||||||
|
|
||||||
|
assert isinstance(
|
||||||
|
store,
|
||||||
|
TradeStreamStateStoreProtocol,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_store_is_empty_after_creation() -> None:
|
||||||
|
store = TradeStreamStateStore()
|
||||||
|
|
||||||
|
assert store.contains("BTCUSD") is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_or_create_creates_trade_stream_state() -> None:
|
||||||
|
store = TradeStreamStateStore()
|
||||||
|
|
||||||
|
state = store.get_or_create("BTCUSD")
|
||||||
|
|
||||||
|
assert isinstance(
|
||||||
|
state,
|
||||||
|
TradeStreamState,
|
||||||
|
)
|
||||||
|
assert state.symbol == "BTCUSD"
|
||||||
|
assert store.contains("BTCUSD") is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_or_create_returns_same_instance_for_same_symbol() -> None:
|
||||||
|
store = TradeStreamStateStore()
|
||||||
|
|
||||||
|
first_state = store.get_or_create("BTCUSD")
|
||||||
|
second_state = store.get_or_create("BTCUSD")
|
||||||
|
|
||||||
|
assert second_state is first_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_or_create_returns_independent_states_for_different_symbols() -> None:
|
||||||
|
store = TradeStreamStateStore()
|
||||||
|
|
||||||
|
btc_state = store.get_or_create("BTCUSD")
|
||||||
|
eth_state = store.get_or_create("ETHUSD")
|
||||||
|
|
||||||
|
assert btc_state is not eth_state
|
||||||
|
assert btc_state.symbol == "BTCUSD"
|
||||||
|
assert eth_state.symbol == "ETHUSD"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_returns_existing_state() -> None:
|
||||||
|
store = TradeStreamStateStore()
|
||||||
|
|
||||||
|
created_state = store.get_or_create("BTCUSD")
|
||||||
|
returned_state = store.get("BTCUSD")
|
||||||
|
|
||||||
|
assert returned_state is created_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_raises_not_found_error_for_missing_symbol() -> None:
|
||||||
|
store = TradeStreamStateStore()
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
TradeStreamStateNotFoundError,
|
||||||
|
match="BTCUSD",
|
||||||
|
):
|
||||||
|
store.get("BTCUSD")
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_deletes_existing_state() -> None:
|
||||||
|
store = TradeStreamStateStore()
|
||||||
|
store.get_or_create("BTCUSD")
|
||||||
|
|
||||||
|
store.remove("BTCUSD")
|
||||||
|
|
||||||
|
assert store.contains("BTCUSD") is False
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
TradeStreamStateNotFoundError,
|
||||||
|
match="BTCUSD",
|
||||||
|
):
|
||||||
|
store.get("BTCUSD")
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_does_not_delete_other_symbol_state() -> None:
|
||||||
|
store = TradeStreamStateStore()
|
||||||
|
|
||||||
|
store.get_or_create("BTCUSD")
|
||||||
|
eth_state = store.get_or_create("ETHUSD")
|
||||||
|
|
||||||
|
store.remove("BTCUSD")
|
||||||
|
|
||||||
|
assert store.contains("BTCUSD") is False
|
||||||
|
assert store.get("ETHUSD") is eth_state
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_raises_not_found_error_for_missing_symbol() -> None:
|
||||||
|
store = TradeStreamStateStore()
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
TradeStreamStateNotFoundError,
|
||||||
|
match="BTCUSD",
|
||||||
|
):
|
||||||
|
store.remove("BTCUSD")
|
||||||
|
|
||||||
|
|
||||||
|
def test_clear_removes_all_states() -> None:
|
||||||
|
store = TradeStreamStateStore()
|
||||||
|
|
||||||
|
store.get_or_create("BTCUSD")
|
||||||
|
store.get_or_create("ETHUSD")
|
||||||
|
|
||||||
|
store.clear()
|
||||||
|
|
||||||
|
assert store.contains("BTCUSD") is False
|
||||||
|
assert store.contains("ETHUSD") is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_clear_is_idempotent_for_empty_store() -> None:
|
||||||
|
store = TradeStreamStateStore()
|
||||||
|
|
||||||
|
store.clear()
|
||||||
|
store.clear()
|
||||||
|
|
||||||
|
assert store.contains("BTCUSD") is False
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
# 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")
|
|
||||||
1403
docs/migrations/build_060_20.md
Normal file
1403
docs/migrations/build_060_20.md
Normal file
File diff suppressed because it is too large
Load Diff
1061
docs/migrations/build_060_20_1.md
Normal file
1061
docs/migrations/build_060_20_1.md
Normal file
File diff suppressed because it is too large
Load Diff
1410
docs/migrations/build_060_20_1_architecture.md
Normal file
1410
docs/migrations/build_060_20_1_architecture.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user