Created
August 25, 2024 15:08
-
-
Save williamardianto/c57eb47d4e1b84516793c61b9cd8f1b5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from enum import Enum | |
| from typing import List | |
| from dataclasses import dataclass, field | |
| class OrderType(Enum): | |
| LIMIT = 1 | |
| STOP = 2 | |
| MARKET = 3 | |
| @dataclass | |
| class Order: | |
| id: str | |
| symbol: str | |
| quantity: int | |
| price: float | |
| order_type: OrderType | |
| @dataclass | |
| class Bid: | |
| price: float | |
| orders: List[Order] = field(default_factory=list) | |
| @dataclass | |
| class Ask: | |
| price: float | |
| orders: List[Order] = field(default_factory=list) | |
| class OrderBook: | |
| def __init__(self): | |
| self.bids: List[Bid] = [] | |
| self.asks: List[Ask] = [] | |
| def place_order(self, order: Order): | |
| if order.order_type == OrderType.MARKET: | |
| self._match_market_order(order) | |
| elif order.order_type == OrderType.LIMIT: | |
| self._place_limit_order(order) | |
| elif order.order_type == OrderType.STOP: | |
| self._place_stop_order(order) | |
| def _match_market_order(self, order: Order): | |
| if order.quantity > 0: | |
| self._match_buy_market_order(order) | |
| else: | |
| self._match_sell_market_order(order) | |
| def _match_buy_market_order(self, order: Order): | |
| remaining_quantity = order.quantity | |
| while remaining_quantity > 0 and self.asks: | |
| ask = self.asks[0] | |
| for ask_order in ask.orders: | |
| if remaining_quantity <= 0: | |
| break | |
| matched_quantity = min(remaining_quantity, ask_order.quantity) | |
| remaining_quantity -= matched_quantity | |
| ask_order.quantity -= matched_quantity | |
| print(f"Matched {matched_quantity} shares at price {ask.price}") | |
| if not ask.orders: | |
| self.asks.pop(0) | |
| def _match_sell_market_order(self, order: Order): | |
| remaining_quantity = -order.quantity | |
| while remaining_quantity > 0 and self.bids: | |
| bid = self.bids[-1] | |
| for bid_order in bid.orders: | |
| if remaining_quantity <= 0: | |
| break | |
| matched_quantity = min(remaining_quantity, bid_order.quantity) | |
| remaining_quantity -= matched_quantity | |
| bid_order.quantity -= matched_quantity | |
| print(f"Matched {matched_quantity} shares at price {bid.price}") | |
| if not bid.orders: | |
| self.bids.pop() | |
| def _place_limit_order(self, order: Order): | |
| if order.quantity > 0: | |
| self._place_limit_bid(order) | |
| else: | |
| self._place_limit_ask(order) | |
| def _place_limit_bid(self, order: Order): | |
| for ask in self.asks: | |
| if order.price >= ask.price: | |
| self._match_limit_bid_with_ask(order, ask) | |
| if order.quantity == 0: | |
| return | |
| self._add_bid(order) | |
| def _match_limit_bid_with_ask(self, bid_order: Order, ask: Ask): | |
| for ask_order in ask.orders: | |
| if bid_order.quantity <= 0: | |
| break | |
| matched_quantity = min(bid_order.quantity, ask_order.quantity) | |
| bid_order.quantity -= matched_quantity | |
| ask_order.quantity -= matched_quantity | |
| print(f"Matched {matched_quantity} shares at price {ask.price}") | |
| ask.orders = [order for order in ask.orders if order.quantity > 0] | |
| if not ask.orders: | |
| self.asks.remove(ask) | |
| def _add_bid(self, order: Order): | |
| for bid in self.bids: | |
| if order.price == bid.price: | |
| bid.orders.append(order) | |
| return | |
| self.bids.append(Bid(order.price, [order])) | |
| self.bids.sort(key=lambda b: b.price, reverse=True) | |
| def _place_limit_ask(self, order: Order): | |
| for bid in self.bids: | |
| if order.price <= bid.price: | |
| self._match_limit_ask_with_bid(order, bid) | |
| if order.quantity == 0: | |
| return | |
| self._add_ask(order) | |
| def _match_limit_ask_with_bid(self, ask_order: Order, bid: Bid): | |
| for bid_order in bid.orders: | |
| if ask_order.quantity <= 0: | |
| break | |
| matched_quantity = min(-ask_order.quantity, bid_order.quantity) | |
| ask_order.quantity += matched_quantity | |
| bid_order.quantity -= matched_quantity | |
| print(f"Matched {matched_quantity} shares at price {bid.price}") | |
| bid.orders = [order for order in bid.orders if order.quantity > 0] | |
| if not bid.orders: | |
| self.bids.remove(bid) | |
| def _add_ask(self, order: Order): | |
| for ask in self.asks: | |
| if order.price == ask.price: | |
| ask.orders.append(order) | |
| return | |
| self.asks.append(Ask(order.price, [order])) | |
| self.asks.sort(key=lambda a: a.price) | |
| def _place_stop_order(self, order: Order): | |
| # Simplified implementation, assuming stop orders are placed as market orders when the stop price is reached | |
| if order.quantity > 0: | |
| for ask in self.asks: | |
| if ask.price >= order.price: | |
| self._match_market_order(Order(order.id, order.symbol, order.quantity, 0, OrderType.MARKET)) | |
| break | |
| else: | |
| for bid in self.bids: | |
| if bid.price <= order.price: | |
| self._match_market_order(Order(order.id, order.symbol, order.quantity, 0, OrderType.MARKET)) | |
| break | |
| # Usage example | |
| order_book = OrderBook() | |
| # Place limit orders | |
| order_book.place_order(Order("1", "AAPL", 100, 150.0, OrderType.LIMIT)) | |
| order_book.place_order(Order("2", "AAPL", -50, 160.0, OrderType.LIMIT)) | |
| order_book.place_order(Order("3", "AAPL", 200, 140.0, OrderType.LIMIT)) | |
| order_book.place_order(Order("4", "AAPL", -100, 155.0, OrderType.LIMIT)) | |
| # Place market orders | |
| order_book.place_order(Order("5", "AAPL", 150, 0, OrderType.MARKET)) | |
| order_book.place_order(Order("6", "AAPL", -120, 0, OrderType.MARKET)) | |
| # Place stop orders | |
| order_book.place_order(Order("7", "AAPL", 100, 145.0, OrderType.STOP)) | |
| order_book.place_order(Order("8", "AAPL", -80, 165.0, OrderType.STOP)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment