Spaces:
Sleeping
Sleeping
File size: 5,278 Bytes
f5e8b69 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | from collections import defaultdict
class OrderBook:
def __init__(self, arrival_price=100.0):
self.bids = defaultdict(list)
self.asks = defaultdict(list)
self.orders = {}
self.arrival_price = arrival_price
self.next_id = 0
self.total_traded_volume = 0.0
def add_order(self, side, price, size, agent_id):
"""
Add a limit order and immediately match it against the opposite side.
Returns (order_id, fills) where:
- order_id is the ID of the resting order (or None if fully filled)
- fills is a list of fill dicts with keys: price, size, buyer, seller
"""
order_id = self.next_id
self.next_id += 1
order = {
"id": order_id,
"side": side,
"price": price,
"size": size,
"agent_id": agent_id,
}
self.orders[order_id] = order
remaining = size
fills = []
if side == "bid":
while (
remaining > 0 and self.best_ask is not None and price >= self.best_ask
):
current_best_ask = self.best_ask
if current_best_ask not in self.asks or not self.asks[current_best_ask]:
if current_best_ask in self.asks:
del self.asks[current_best_ask]
continue
ask_order = self.asks[current_best_ask][0]
fill = min(remaining, ask_order["size"])
fills.append(
{
"price": ask_order["price"],
"size": fill,
"buyer": agent_id,
"seller": ask_order["agent_id"],
}
)
self.total_traded_volume += fill
remaining -= fill
ask_order["size"] -= fill
if ask_order["size"] == 0:
self.cancel_order(ask_order["id"])
if not self.asks[current_best_ask]:
del self.asks[current_best_ask]
if remaining > 0:
order["size"] = remaining
if price not in self.bids:
self.bids[price] = []
self.bids[price].append(order)
return order_id, fills
else:
if order_id in self.orders:
del self.orders[order_id]
return None, fills
else: # side == 'ask'
while (
remaining > 0 and self.best_bid is not None and price <= self.best_bid
):
current_best_bid = self.best_bid
if current_best_bid not in self.bids or not self.bids[current_best_bid]:
if current_best_bid in self.bids:
del self.bids[current_best_bid]
continue
bid_order = self.bids[current_best_bid][0]
fill = min(remaining, bid_order["size"])
fills.append(
{
"price": bid_order["price"],
"size": fill,
"buyer": bid_order["agent_id"],
"seller": agent_id,
}
)
self.total_traded_volume += fill
remaining -= fill
bid_order["size"] -= fill
if bid_order["size"] == 0:
self.cancel_order(bid_order["id"])
if not self.bids[current_best_bid]:
del self.bids[current_best_bid]
if remaining > 0:
order["size"] = remaining
if price not in self.asks:
self.asks[price] = []
self.asks[price].append(order)
return order_id, fills
else:
if order_id in self.orders:
del self.orders[order_id]
return None, fills
def cancel_order(self, order_id):
if order_id not in self.orders:
return False
order = self.orders.pop(order_id)
level = self.bids if order["side"] == "bid" else self.asks
level[order["price"]] = [
o for o in level[order["price"]] if o["id"] != order_id
]
if not level[order["price"]]:
del level[order["price"]]
@property
def best_bid(self):
return max(self.bids.keys()) if self.bids else None
@property
def best_ask(self):
return min(self.asks.keys()) if self.asks else None
@property
def mid(self):
if self.best_bid and self.best_ask:
return (self.best_bid + self.best_ask) / 2
return self.arrival_price
def get_depth(self, levels=5):
bid_levels = sorted(self.bids.keys(), reverse=True)[:levels]
ask_levels = sorted(self.asks.keys())[:levels]
return {
"bid_prices": bid_levels,
"bid_sizes": [sum(o["size"] for o in self.bids[p]) for p in bid_levels],
"ask_prices": ask_levels,
"ask_sizes": [sum(o["size"] for o in self.asks[p]) for p in ask_levels],
}
|