| | |
| | | class OrderSide(Enum): |
| | | """Order side enum.""" |
| | | |
| | | Buy = 0, |
| | | Sell = 1 |
| | | Buy = "BUY", |
| | | Sell = "SELL" |
| | | |
| | | |
| | | class ProductTypeEnum(Enum): |
| | |
| | | side: OrderSide |
| | | remaining_volume: int |
| | | filled_volume: int |
| | | timestamp: float |
| | | id: str |
| | | |
| | | def __init__(self, price: decimal.Decimal(3), side: OrderSide, |
| | | volume: int): |
| | | volume: int, timestamp: float, id: str): |
| | | """Initialise class.""" |
| | | self.price = price |
| | | self.side = side |
| | | self.remaining_volume = volume |
| | | self.filled_volume = 0 |
| | | self.timestamp = timestamp |
| | | self.id = id |
| | | |
| | | |
| | | class Level: |
| | |
| | | |
| | | price: decimal.Decimal |
| | | volume: int |
| | | side: OrderSide |
| | | timestamp: float |
| | | id: str |
| | | |
| | | def __init__(self, price: decimal.Decimal(3), volume): |
| | | def __init__(self, order: Order): |
| | | """Initialise class.""" |
| | | self.priority = int(price) |
| | | self.price = price |
| | | self.volume = volume |
| | | self.price = order.price |
| | | self.volume = order.remaining_volume |
| | | self.side = order.side |
| | | self.timestamp = order.timestamp |
| | | self.id = order.id |
| | | |
| | | def __lt__(self, other): |
| | | """Less than comparator.""" |
| | | if (self.side == OrderSide.Sell): |
| | | if (self.price < other.price): |
| | | return True |
| | | elif (self.price == other.price and |
| | | self.timestamp < other.timestamp): |
| | | return True |
| | | else: |
| | | return False |
| | | else: |
| | | if (self.price > other.price): |
| | | return True |
| | | elif (self.price == other.price and |
| | | self.timestamp < other.timestamp): |
| | | return True |
| | | else: |
| | | return False |
| | | |
| | | def __gt__(self, other): |
| | | """Greater than comparator.""" |
| | | if (self.side == OrderSide.Sell): |
| | | if (self.price > other.price): |
| | | return True |
| | | elif (self.price == other.price and |
| | | self.timestamp < other.timestamp): |
| | | return True |
| | | else: |
| | | return False |
| | | else: |
| | | if (self.price < other.price): |
| | | return True |
| | | elif (self.price == other.price and |
| | | self.timestamp < other.timestamp): |
| | | return True |
| | | else: |
| | | return False |
| | | |
| | | def __le__(self, other): |
| | | """Less than or equal compatator.""" |
| | | if (self.side == OrderSide.Sell): |
| | | if (self.price <= other.price): |
| | | return True |
| | | elif (self.price == other.price and |
| | | self.timestamp <= other.timestamp): |
| | | return True |
| | | else: |
| | | return False |
| | | else: |
| | | if (self.price >= other.price): |
| | | return True |
| | | elif (self.price == other.price and |
| | | self.timestamp <= other.timestamp): |
| | | return True |
| | | else: |
| | | return False |
| | | |
| | | def __ge__(self, other): |
| | | """Greater than or equal comparator.""" |
| | | if (self.side == OrderSide.Sell): |
| | | if (self.price >= other.price): |
| | | return True |
| | | elif (self.price == other.price and |
| | | self.timestamp <= other.timestamp): |
| | | return True |
| | | else: |
| | | return False |
| | | else: |
| | | if (self.price <= other.price): |
| | | return True |
| | | elif (self.price == other.price and |
| | | self.timestamp <= other.timestamp): |
| | | return True |
| | | else: |
| | | return False |
| | | |
| | | def __eq__(self, other): |
| | | """Equalty compatator.""" |
| | | if (self.price == other.price): |
| | | if (self.price == other.price and self.timestamp == other.timestamp): |
| | | return True |
| | | else: |
| | | return False |
| | |
| | | |
| | | def __str__(self): |
| | | """Turn into str for printing.""" |
| | | return "Price: " + str(self.price) + ", volume: " + str(self.volume) |
| | | return ("Price: " + str(self.price) + ", volume: " + str(self.volume) + |
| | | ", time: " + str(self.timestamp) + ", id: " + self.id) |
| | | |
| | | |
| | | class Side: |
| | |
| | | def __init__(self): |
| | | """Initialise class.""" |
| | | self.levels = [] |
| | | |
| | | def delete(self, orderId: str): |
| | | """Delete order from side.""" |
| | | for i in self.levels: |
| | | if (i.id == orderId): |
| | | self.levels.remove(i) |
| | | heapq.heapify(self.levels) |
| | | |
| | | |
| | | class Book: |
| | |
| | | def ask(self, order: Order): |
| | | """Add ask to book.""" |
| | | while(len(self.bidSide.levels) > 0 and |
| | | self.bidSide.levels[0][1].price >= order.price): |
| | | if (self.bidSide.levels[0][1].volume > order.remaining_volume): |
| | | temp = self.bidSide.levels[0][1].volume |
| | | order.filled_volume += self.bidSide.levels[0][1].volume |
| | | self.bidSide.levels[0][1].volume -= order.remaining_volume |
| | | self.bidSide.levels[0].price >= order.price): |
| | | if (self.bidSide.levels[0].volume > order.remaining_volume): |
| | | temp = self.bidSide.levels[0].volume |
| | | order.filled_volume += self.bidSide.levels[0].volume |
| | | self.bidSide.levels[0].volume -= order.remaining_volume |
| | | order.remaining_volume -= temp |
| | | break |
| | | else: |
| | | order.remaining_volume -= self.bidSide.levels[0][1].volume |
| | | order.filled_volume += self.bidSide.levels[0][1].volume |
| | | order.remaining_volume -= self.bidSide.levels[0].volume |
| | | order.filled_volume += self.bidSide.levels[0].volume |
| | | heapq.heappop(self.bidSide.levels) |
| | | if (order.remaining_volume > 0): |
| | | if (len(self.askSide.levels) > 0 and |
| | | self.askSide.levels[0][1].price == order.price): |
| | | self.askSide.levels[0][1].addVolume(order.remaining_volume) |
| | | else: |
| | | heapq.heappush(self.askSide.levels, |
| | | (order.price, |
| | | Level(order.price, order.remaining_volume))) |
| | | heapq.heappush(self.askSide.levels, Level(order)) |
| | | |
| | | def bid(self, order: Order): |
| | | """Add bid to book.""" |
| | | while(len(self.askSide.levels) > 0 and |
| | | self.askSide.levels[0][1].price <= order.price): |
| | | if (self.askSide.levels[0][1].volume > order.remaining_volume): |
| | | temp = self.askSide.levels[0][1].volume |
| | | order.filled_volume += self.askSide.levels[0][1].volume |
| | | self.askSide.levels[0][1].volume -= order.remaining_volume |
| | | self.askSide.levels[0].price <= order.price): |
| | | if (self.askSide.levels[0].volume > order.remaining_volume): |
| | | temp = self.askSide.levels[0].volume |
| | | order.filled_volume += self.askSide.levels[0].volume |
| | | self.askSide.levels[0].volume -= order.remaining_volume |
| | | order.remaining_volume -= temp |
| | | break |
| | | else: |
| | | order.remaining_volume -= self.askSide.levels[0][1].volume |
| | | order.filled_volume -= self.askSide.levels[0][1].volume |
| | | order.remaining_volume -= self.askSide.levels[0].volume |
| | | order.filled_volume -= self.askSide.levels[0].volume |
| | | heapq.heappop(self.askSide.levels) |
| | | if (order.remaining_volume > 0): |
| | | if (len(self.bidSide.levels) > 0 and |
| | | self.bidSide.levels[0][1].price == order.price): |
| | | self.bidSide.levels[0][1].addVolume(order.remaining_volume) |
| | | else: |
| | | heapq.heappush(self.bidSide.levels, |
| | | (100 - order.price, |
| | | Level(order.price, order.remaining_volume))) |
| | | heapq.heappush(self.bidSide.levels, Level(order)) |
| | | |
| | | def printBook(self): |
| | | """Test print book. Book is a heap so not strictly sorted.""" |
| | | print("Sell side") |
| | | for i in sorted(self.askSide.levels, reverse=True): |
| | | print(i[1]) |
| | | for i in sorted(self.askSide.levels, reverse=True)[-10:]: |
| | | print(i) |
| | | print("Buy side") |
| | | for i in sorted(self.bidSide.levels): |
| | | print(str(i[1])) |
| | | for i in sorted(self.bidSide.levels)[:10]: |
| | | print(str(i)) |
| | | |
| | | |
| | | def testBook(): |
| | | def testBook(orders: int = 10, printBook=True): |
| | | """Test an example book.""" |
| | | b = Book() |
| | | for i in range(1, 10): |
| | | b.addOrder(Order(i, OrderSide.Buy, 10)) |
| | | for i in range(11, 20): |
| | | b.addOrder(Order(i, OrderSide.Sell, 10)) |
| | | time = 1 |
| | | for i in range(1, orders): |
| | | b.addOrder(Order(price=i, side=OrderSide.Buy, volume=10, |
| | | timestamp=time, id="a")) |
| | | time += 1 |
| | | for i in range(orders + 1, 2 * orders): |
| | | b.addOrder(Order(i, OrderSide.Sell, 10, time, "a")) |
| | | time += 1 |
| | | if (printBook): |
| | | b.printBook() |
| | | return b |