72 lines
2.6 KiB
Python
72 lines
2.6 KiB
Python
|
|
"""VIOLET observe-only hard guard (Stage V1).
|
||
|
|
|
||
|
|
``ObserveOnlyVenue`` wraps any venue adapter and makes order placement
|
||
|
|
structurally impossible: ``submit``/``submit_async``/``cancel``/
|
||
|
|
``cancel_async`` raise ``ObserveOnlyViolation`` and log CRITICAL. Every
|
||
|
|
other attribute (including assignment — e.g. the runtime's
|
||
|
|
``venue._kernel_ref = kernel``) delegates to the wrapped adapter, so
|
||
|
|
account streams, reconcile reads and connection management work unchanged.
|
||
|
|
|
||
|
|
This is the hard guarantee, independent of policy configuration: even if a
|
||
|
|
policy step were ever wired into the V1 runtime by mistake, no order can
|
||
|
|
reach the exchange.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import logging
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
LOGGER = logging.getLogger("violet.observe_guard")
|
||
|
|
|
||
|
|
_BLOCKED = ("submit", "submit_async", "cancel", "cancel_async")
|
||
|
|
_SELF_ATTRS = ("_inner", "_logger")
|
||
|
|
|
||
|
|
|
||
|
|
class ObserveOnlyViolation(RuntimeError):
|
||
|
|
"""An order-placement call reached the observe-only venue guard."""
|
||
|
|
|
||
|
|
|
||
|
|
class ObserveOnlyVenue:
|
||
|
|
"""Delegating wrapper that refuses order placement."""
|
||
|
|
|
||
|
|
def __init__(self, inner: Any, logger: logging.Logger | None = None) -> None:
|
||
|
|
object.__setattr__(self, "_inner", inner)
|
||
|
|
object.__setattr__(self, "_logger", logger or LOGGER)
|
||
|
|
|
||
|
|
# -- blocked surface -----------------------------------------------------
|
||
|
|
|
||
|
|
def _refuse(self, name: str, *args: Any, **kwargs: Any) -> None:
|
||
|
|
msg = (
|
||
|
|
f"OBSERVE-ONLY VIOLATION: venue.{name}() called — VIOLET V1 must "
|
||
|
|
f"never place or cancel orders (args={args!r:.200})"
|
||
|
|
)
|
||
|
|
object.__getattribute__(self, "_logger").critical(msg)
|
||
|
|
raise ObserveOnlyViolation(msg)
|
||
|
|
|
||
|
|
def submit(self, *a: Any, **k: Any) -> Any:
|
||
|
|
self._refuse("submit", *a, **k)
|
||
|
|
|
||
|
|
async def submit_async(self, *a: Any, **k: Any) -> Any:
|
||
|
|
self._refuse("submit_async", *a, **k)
|
||
|
|
|
||
|
|
def cancel(self, *a: Any, **k: Any) -> Any:
|
||
|
|
self._refuse("cancel", *a, **k)
|
||
|
|
|
||
|
|
async def cancel_async(self, *a: Any, **k: Any) -> Any:
|
||
|
|
self._refuse("cancel_async", *a, **k)
|
||
|
|
|
||
|
|
# -- delegation ----------------------------------------------------------
|
||
|
|
|
||
|
|
def __getattr__(self, name: str) -> Any:
|
||
|
|
return getattr(object.__getattribute__(self, "_inner"), name)
|
||
|
|
|
||
|
|
def __setattr__(self, name: str, value: Any) -> None:
|
||
|
|
if name in _SELF_ATTRS:
|
||
|
|
object.__setattr__(self, name, value)
|
||
|
|
else:
|
||
|
|
setattr(object.__getattribute__(self, "_inner"), name, value)
|
||
|
|
|
||
|
|
def __repr__(self) -> str: # pragma: no cover — debug helper
|
||
|
|
return f"ObserveOnlyVenue({object.__getattribute__(self, '_inner')!r})"
|