class OrderBook(object): def __init__(self, tick_size=0.0001): self.tape = deque(maxlen=None) # Index[0] is most recent trade self.bids = OrderTree() self.asks = OrderTree() self.last_tick = None self.last_timestamp = 0 self.tick_size = tick_size self.time = 0 self.next_order_id = 0 def clip_price(self, price): '''Clips the price according to the tick size. May not make sense if not a currency''' return round(price, int(math.log10(1 / self.tick_size))) def update_time(self): self.time += 1 def process_order(self, quote, from_data, verbose): order_type = quote['type'] order_in_book = None if from_data: self.time = quote['timestamp'] else: self.update_time() quote['timestamp'] = self.time if quote['quantity'] <= 0: sys.exit('process_order() given order of quantity <= 0') if not from_data: self.next_order_id += 1 if order_type == 'market': trades = self.process_market_order(quote, verbose) elif order_type == 'limit': quote['price'] = self.clip_price(quote['price']) trades, order_in_book = self.process_limit_order( quote, from_data, verbose) else: sys.exit( "order_type for process_order() is neither 'market' or 'limit'" ) return trades, order_in_book def process_order_list(self, side, order_list, quantity_still_to_trade, quote, verbose): ''' Takes an OrderList (stack of orders at one price) and an incoming order and matches appropriate trades given the order's quantity. ''' trades = [] quantity_to_trade = quantity_still_to_trade while len(order_list) > 0 and quantity_to_trade > 0: head_order = order_list.get_head_order() traded_price = head_order.price counter_party = head_order.trade_id if quantity_to_trade < head_order.quantity: traded_quantity = quantity_to_trade # Do the transaction new_book_quantity = head_order.quantity - quantity_to_trade head_order.update_quantity(new_book_quantity, head_order.timestamp) quantity_to_trade = 0 elif quantity_to_trade == head_order.quantity: traded_quantity = quantity_to_trade if side == 'bid': self.bids.remove_order_by_id(head_order.order_id) else: self.asks.remove_order_by_id(head_order.order_id) quantity_to_trade = 0 else: # quantity to trade is larger than the head order traded_quantity = head_order.quantity if side == 'bid': self.bids.remove_order_by_id(head_order.order_id) else: self.asks.remove_order_by_id(head_order.order_id) quantity_to_trade -= traded_quantity if verbose: print( "TRADE: Time - %d, Price - %f, Quantity - %d, TradeID - %d, Matching TradeID - %d" % (self.time, traded_price, traded_quantity, counter_party, quote['trade_id'])) transaction_record = { 'timestamp': self.time, 'price': traded_price, 'quantity': traded_quantity, 'time': self.time } if side == 'bid': transaction_record['party1'] = [ counter_party, 'bid', head_order.order_id ] transaction_record['party2'] = [quote['trade_id'], 'ask', None] else: transaction_record['party1'] = [ counter_party, 'ask', head_order.order_id ] transaction_record['party2'] = [quote['trade_id'], 'bid', None] self.tape.append(transaction_record) trades.append(transaction_record) return quantity_to_trade, trades def process_market_order(self, quote, verbose): trades = [] quantity_to_trade = quote['quantity'] side = quote['side'] if side == 'bid': while quantity_to_trade > 0 and self.asks: best_price_asks = self.asks.min_price_list() quantity_to_trade, new_trades = self.process_order_list( 'ask', best_price_asks, quantity_to_trade, quote, verbose) trades += new_trades elif side == 'ask': while quantity_to_trade > 0 and self.bids: best_price_bids = self.bids.max_price_list() quantity_to_trade, new_trades = self.process_order_list( 'bid', best_price_bids, quantity_to_trade, quote, verbose) trades += new_trades else: sys.exit('process_market_order() recieved neither "bid" nor "ask"') return trades def process_limit_order(self, quote, from_data, verbose): order_in_book = None trades = [] quantity_to_trade = quote['quantity'] side = quote['side'] price = quote['price'] if side == 'bid': while (self.asks and price > self.asks.min_price() and quantity_to_trade > 0): best_price_asks = self.asks.min_price_list() quantity_to_trade, new_trades = self.process_order_list( 'ask', best_price_asks, quantity_to_trade, quote, verbose) trades += new_trades # If volume remains, need to update the book with new quantity if quantity_to_trade > 0: if not from_data: quote['order_id'] = self.next_order_id quote['quantity'] = quantity_to_trade self.bids.insert_order(quote) order_in_book = quote elif side == 'ask': while (self.bids and price < self.bids.max_price() and quantity_to_trade > 0): best_price_bids = self.bids.max_price_list() quantity_to_trade, new_trades = self.process_order_list( 'bid', best_price_bids, quantity_to_trade, quote, verbose) trades += new_trades # If volume remains, need to update the book with new quantity if quantity_to_trade > 0: if not from_data: quote['order_id'] = self.next_order_id quote['quantity'] = quantity_to_trade self.asks.insert_order(quote) order_in_book = quote else: sys.exit('process_limit_order() given neither "bid" nor "ask"') return trades, order_in_book def cancel_order(self, side, order_id, time=None): if time: self.time = time else: self.update_time() if side == 'bid': if self.bids.order_exists(order_id): self.bids.remove_order_by_id(order_id) elif side == 'ask': if self.asks.order_exists(order_id): self.bids.remove_order_by_id(order_id) else: sys.exit('cancel_order() given neither "bid" nor "ask"') def modify_order(self, order_id, order_update, time=None): if time: self.time = time else: self.update_time() side = order_update['side'] order_update['order_id'] = order_id order_update['timestamp'] = self.time if side == 'bid': if self.bids.order_exists(order_update['order_id']): self.bids.update_order(order_update) elif side == 'ask': if self.asks.order_exists(order_update['order_id']): self.asks.update_order(order_update) else: sys.exit('modify_order() given neither "bid" nor "ask"') def get_volume_at_price(self, side, price): price = self.clip_price(price) if side == 'bid': volume = 0 if self.bids.price_exists(price): volume = self.bids.get_price(price).volume return volume elif side == 'ask': volume = 0 if self.asks.price_exists(price): volume = self.asks.get_price(price).volume return volume else: sys.exit('get_volume_at_price() given neither "bid" nor "ask"') def get_best_bid(self): return self.bids.max_price() def get_worst_bid(self): return self.bids.min_price() def get_best_ask(self): return self.asks.min_price() def get_worst_ask(self): return self.asks.max_price() def tape_dump(self, filename, filemode, tapemode): dumpfile = open(filename, filemode) for tapeitem in self.tape: dumpfile.write( 'Time: %s, Price: %s, Quantity: %s\n' % (tapeitem['time'], tapeitem['price'], tapeitem['quantity'])) dumpfile.close() if tapemode == 'wipe': self.tape = [] def __str__(self): tempfile = StringIO() tempfile.write("***Bids***\n") if self.bids != None and len(self.bids) > 0: for key, value in self.bids.price_tree.items(reverse=True): tempfile.write('%s' % value) tempfile.write("\n***Asks***\n") if self.asks != None and len(self.bids) > 0: for key, value in self.asks.price_tree.items(): tempfile.write('%s' % value) tempfile.write("\n***Trades***\n") if self.tape != None and len(self.tape) > 0: num = 0 for entry in self.tape: if num < 5: # get last 5 entries tempfile.write( str(entry['quantity']) + " @ " + str(entry['price']) + " (" + str(entry['timestamp']) + ")\n") num += 1 else: break tempfile.write("\n") return tempfile.getvalue()
class OrderBook(object): def __init__(self, tick_size = 0.0001): self.tape = deque(maxlen=None) # Index[0] is most recent trade self.bids = OrderTree() self.asks = OrderTree() self.last_tick = None self.last_timestamp = 0 self.tick_size = tick_size self.time = 0 self.next_order_id = 0 def clip_price(self, price): '''Clips the price according to the tick size. May not make sense if not a currency''' return round(price, int(math.log10(1 / self.tick_size))) def update_time(self): self.time += 1 def process_order(self, quote, from_data, verbose): order_type = quote['type'] order_in_book = None if from_data: self.time = quote['timestamp'] else: self.update_time() quote['timestamp'] = self.time if quote['quantity'] <= 0: sys.exit('process_order() given order of quantity <= 0') if not from_data: self.next_order_id += 1 if order_type == 'market': trades = self.process_market_order(quote, verbose) elif order_type == 'limit': quote['price'] = self.clip_price(quote['price']) trades, order_in_book = self.process_limit_order(quote, from_data, verbose) else: sys.exit("order_type for process_order() is neither 'market' or 'limit'") return trades, order_in_book def process_order_list(self, side, order_list, quantity_still_to_trade, quote, verbose): ''' Takes an OrderList (stack of orders at one price) and an incoming order and matches appropriate trades given the order's quantity. ''' trades = [] quantity_to_trade = quantity_still_to_trade while len(order_list) > 0 and quantity_to_trade > 0: head_order = order_list.get_head_order() traded_price = head_order.price counter_party = head_order.trade_id if quantity_to_trade < head_order.quantity: traded_quantity = quantity_to_trade # Do the transaction new_book_quantity = head_order.quantity - quantity_to_trade head_order.update_quantity(new_book_quantity, head_order.timestamp) quantity_to_trade = 0 elif quantity_to_trade == head_order.quantity: traded_quantity = quantity_to_trade if side == 'bid': self.bids.remove_order_by_id(head_order.order_id) else: self.asks.remove_order_by_id(head_order.order_id) quantity_to_trade = 0 else: # quantity to trade is larger than the head order traded_quantity = head_order.quantity if side == 'bid': self.bids.remove_order_by_id(head_order.order_id) else: self.asks.remove_order_by_id(head_order.order_id) quantity_to_trade -= traded_quantity if verbose: print ("TRADE: Time - %d, Price - %f, Quantity - %d, TradeID - %d, Matching TradeID - %d" % (self.time, traded_price, traded_quantity, counter_party, quote['trade_id'])) transaction_record = { 'timestamp': self.time, 'price': traded_price, 'quantity': traded_quantity, 'time': self.time } if side == 'bid': transaction_record['party1'] = [counter_party, 'bid', head_order.order_id] transaction_record['party2'] = [quote['trade_id'], 'ask', None] else: transaction_record['party1'] = [counter_party, 'ask', head_order.order_id] transaction_record['party2'] = [quote['trade_id'], 'bid', None] self.tape.append(transaction_record) trades.append(transaction_record) return quantity_to_trade, trades def process_market_order(self, quote, verbose): trades = [] quantity_to_trade = quote['quantity'] side = quote['side'] if side == 'bid': while quantity_to_trade > 0 and self.asks: best_price_asks = self.asks.min_price_list() quantity_to_trade, new_trades = self.process_order_list('ask', best_price_asks, quantity_to_trade, quote, verbose) trades += new_trades elif side == 'ask': while quantity_to_trade > 0 and self.bids: best_price_bids = self.bids.max_price_list() quantity_to_trade, new_trades = self.process_order_list('bid', best_price_bids, quantity_to_trade, quote, verbose) trades += new_trades else: sys.exit('process_market_order() recieved neither "bid" nor "ask"') return trades def process_limit_order(self, quote, from_data, verbose): order_in_book = None trades = [] quantity_to_trade = quote['quantity'] side = quote['side'] price = quote['price'] if side == 'bid': while (self.asks and price > self.asks.min_price() and quantity_to_trade > 0): best_price_asks = self.asks.min_price_list() quantity_to_trade, new_trades = self.process_order_list('ask', best_price_asks, quantity_to_trade, quote, verbose) trades += new_trades # If volume remains, need to update the book with new quantity if quantity_to_trade > 0: if not from_data: quote['order_id'] = self.next_order_id quote['quantity'] = quantity_to_trade self.bids.insert_order(quote) order_in_book = quote elif side == 'ask': while (self.bids and price < self.bids.max_price() and quantity_to_trade > 0): best_price_bids = self.bids.max_price_list() quantity_to_trade, new_trades = self.process_order_list('bid', best_price_bids, quantity_to_trade, quote, verbose) trades += new_trades # If volume remains, need to update the book with new quantity if quantity_to_trade > 0: if not from_data: quote['order_id'] = self.next_order_id quote['quantity'] = quantity_to_trade self.asks.insert_order(quote) order_in_book = quote else: sys.exit('process_limit_order() given neither "bid" nor "ask"') return trades, order_in_book def cancel_order(self, side, order_id, time=None): if time: self.time = time else: self.update_time() if side == 'bid': if self.bids.order_exists(order_id): self.bids.remove_order_by_id(order_id) elif side == 'ask': if self.asks.order_exists(order_id): self.bids.remove_order_by_id(order_id) else: sys.exit('cancel_order() given neither "bid" nor "ask"') def modify_order(self, order_id, order_update, time=None): if time: self.time = time else: self.update_time() side = order_update['side'] order_update['order_id'] = order_id order_update['timestamp'] = self.time if side == 'bid': if self.bids.order_exists(order_update['order_id']): self.bids.update_order(order_update) elif side == 'ask': if self.asks.order_exists(order_update['order_id']): self.asks.update_order(order_update) else: sys.exit('modify_order() given neither "bid" nor "ask"') def get_volume_at_price(self, side, price): price = self.clip_price(price) if side == 'bid': volume = 0 if self.bids.price_exists(price): volume = self.bids.get_price(price).volume return volume elif side == 'ask': volume = 0 if self.asks.price_exists(price): volume = self.asks.get_price(price).volume return volume else: sys.exit('get_volume_at_price() given neither "bid" nor "ask"') def get_best_bid(self): return self.bids.max_price() def get_worst_bid(self): return self.bids.min_price() def get_best_ask(self): return self.asks.min_price() def get_worst_ask(self): return self.asks.max_price() def tape_dump(self, filename, filemode, tapemode): dumpfile = open(filename, filemode) for tapeitem in self.tape: dumpfile.write('Time: %s, Price: %s, Quantity: %s\n' % (tapeitem['time'], tapeitem['price'], tapeitem['quantity'])) dumpfile.close() if tapemode == 'wipe': self.tape = [] def __str__(self): tempfile = StringIO() tempfile.write("***Bids***\n") if self.bids != None and len(self.bids) > 0: for key, value in self.bids.price_tree.items(reverse=True): tempfile.write('%s' % value) tempfile.write("\n***Asks***\n") if self.asks != None and len(self.bids) > 0: for key, value in self.asks.price_tree.items(): tempfile.write('%s' % value) tempfile.write("\n***Trades***\n") if self.tape != None and len(self.tape) > 0: num = 0 for entry in self.tape: if num < 5: # get last 5 entries tempfile.write(str(entry['quantity']) + " @ " + str(entry['price']) + " (" + str(entry['timestamp']) + ")\n") num += 1 else: break tempfile.write("\n") return tempfile.getvalue()