idea83-mercuriusmesh-cross-.../mercuriusmesh/adapters.py

62 lines
2.3 KiB
Python

from typing import Callable, List
from .contracts import QuoteSnapshot, OrderEvent
class ToyVenueAdapter:
"""A tiny in-process adapter that exposes a simple order acceptance model.
It accepts OrderEvent objects and updates an internal top-of-book. Deterministic
behaviour is achieved by not using random jitter and relying on caller ordering.
"""
def __init__(self, venue_id: str, initial_bid: float = 100.0, initial_ask: float = 100.5, asset: str = "TEST/USD"):
self.venue_id = venue_id
self.asset = asset
self.initial_bid = initial_bid
self.initial_ask = initial_ask
self.best_bid = initial_bid
self.best_ask = initial_ask
self.listeners: List[Callable[[QuoteSnapshot], None]] = []
def reset(self):
self.best_bid = self.initial_bid
self.best_ask = self.initial_ask
def _normalize_book(self):
if self.best_ask <= self.best_bid:
midpoint = (self.best_bid + self.best_ask) / 2
self.best_bid = round(midpoint - 0.005, 4)
self.best_ask = round(midpoint + 0.005, 4)
def on_snapshot(self, cb: Callable[[QuoteSnapshot], None]):
self.listeners.append(cb)
def publish_snapshot(self):
snap = QuoteSnapshot(
venue_id=self.venue_id,
asset=self.asset,
bids=[[self.best_bid, 100.0]],
asks=[[self.best_ask, 100.0]],
)
for cb in list(self.listeners):
cb(snap)
def handle_order(self, order: OrderEvent) -> OrderEvent:
# Very small deterministic matcher: marketable at top-of-book executes
impact = round(max(order.size, 1.0) * 0.05, 4)
if order.side == "buy" and order.price is None:
order.status = "filled"
self.best_bid = round(self.best_bid + impact, 4)
self.best_ask = round(self.best_ask + impact, 4)
self._normalize_book()
elif order.side == "sell" and order.price is None:
order.status = "filled"
self.best_bid = round(self.best_bid - impact, 4)
self.best_ask = round(self.best_ask - impact, 4)
self._normalize_book()
else:
order.status = "accepted"
# publish new snapshot deterministically
self.publish_snapshot()
return order