1"""Periodic call auction (robustness alternative to continuous matching).23Every `interval` ticks the engine collects the resting limit orders of a book and clears them at the4uniform price that maximizes executable volume (ties: the price closest to the previous mid). Market5orders submitted during the batch are treated as limit orders at the price band edge. Orders rest6between auctions without being matched continuously (the engine skips `match` while in batch mode)."""7from __future__ import annotations89from .book import LimitOrderBook10from .orders import Order, Side111213def clearing_price(book: LimitOrderBook, ref_mid: float, collar: float = 0.02) -> tuple[int | None, int]:14 """Uniform price maximizing executable volume among limit prices inside a collar of ±`collar` around15 the reference (market orders converted to band-edge limits are price takers, not candidates)."""16 bids = sorted(((-k, sum(o.remaining for o in q)) for k, q in book.bids.items()), reverse=True)17 asks = sorted(((k, sum(o.remaining for o in q)) for k, q in book.asks.items()))18 if not bids or not asks or bids[0][0] < asks[0][0]:19 return None, 020 lo, hi = ref_mid * (1 - collar), ref_mid * (1 + collar)21 prices = sorted({p for p, _ in bids if lo <= p <= hi} | {p for p, _ in asks if lo <= p <= hi} | {int(round(ref_mid))})22 best, best_vol, best_dist = None, 0, float("inf")23 for p in prices:24 demand = sum(q for bp, q in bids if bp >= p)25 supply = sum(q for ap, q in asks if ap <= p)26 vol = min(demand, supply)27 dist = abs(p - ref_mid)28 if vol > best_vol or (vol == best_vol and vol > 0 and dist < best_dist):29 best, best_vol, best_dist = p, vol, dist30 return best, best_vol313233def executable_orders(book: LimitOrderBook, price: int) -> tuple[list[Order], list[Order]]:34 """Orders that trade at `price`, in price-time priority (bids high→low, asks low→high)."""35 bids = [o for k, q in book.bids.items() for o in q if -k >= price]36 asks = [o for k, q in book.asks.items() for o in q if k <= price]37 return bids, asks38