45 lines
1.6 KiB
Python
45 lines
1.6 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):
|
|
self.venue_id = venue_id
|
|
self.best_bid = initial_bid
|
|
self.best_ask = initial_ask
|
|
self.listeners: List[Callable[[QuoteSnapshot], None]] = []
|
|
|
|
def on_snapshot(self, cb: Callable[[QuoteSnapshot], None]):
|
|
self.listeners.append(cb)
|
|
|
|
def publish_snapshot(self):
|
|
snap = QuoteSnapshot(
|
|
venue_id=self.venue_id,
|
|
asset="TEST/USD",
|
|
bids=[[self.best_bid, 100.0]],
|
|
asks=[[self.best_ask, 100.0]],
|
|
)
|
|
for cb in self.listeners:
|
|
cb(snap)
|
|
|
|
def handle_order(self, order: OrderEvent) -> OrderEvent:
|
|
# Very small deterministic matcher: marketable at top-of-book executes
|
|
if order.side == "buy" and order.price is None:
|
|
order.status = "filled"
|
|
# naive mid update
|
|
self.best_bid = max(self.best_bid, order.price or self.best_bid)
|
|
elif order.side == "sell" and order.price is None:
|
|
order.status = "filled"
|
|
self.best_ask = min(self.best_ask, order.price or self.best_ask)
|
|
else:
|
|
order.status = "accepted"
|
|
# publish new snapshot deterministically
|
|
self.publish_snapshot()
|
|
return order
|