def test_sequence2(self): """ test sequence 1: add ,add ,update, update, cancel""" obj = OrderBook() self.assertEqual(obj.get_best_bid_and_ask('AAPL'), (0, 0)) obj.process_order("1568390201|abbb11|a|AAPL|B|209.00000|100") self.assertEqual(obj.get_best_bid_and_ask('AAPL'), (209.0, 0)) obj.process_order("1568390202|abbb12|a|AAPL|S|210.00000|10") self.assertEqual(obj.get_best_bid_and_ask('AAPL'), (209.0, 210.0)) obj.process_order("1568390204|abbb11|u|10") self.assertEqual(obj.get_best_bid_and_ask('AAPL'), (209.0, 210.0)) obj.process_order("1568390203|abbb12|u|101") self.assertEqual(obj.get_best_bid_and_ask('AAPL'), (209.0, 210.0)) obj.process_order("1568390243|abbb11|c") self.assertEqual(obj.get_best_bid_and_ask('AAPL'), (0, 210.0))
class test_order_book(unittest.TestCase): def setUp(self): self.completed_trades = [] self.completed_orders = [] self.orders = [] self.order_book = OrderBook("KEQ") for i in range(1, 10): quote = test_quotes[str(i)] if "price" in quote: quote["price"] = Decimal(quote["price"]) order = json.loads(self.order_book.process_order(quote)) self.orders.append(order) for trades in self.order_book.completed_trades: self.completed_trades.append(trades) for orders in self.order_book.completed_orders: self.completed_orders.append(orders) def test_get_min_ask(self): self.assertEqual(self.order_book.get_min_ask(), 99.5) def test_get_max_bid(self): self.assertEqual(self.order_book.get_max_bid(), 99) def test_process_order(self): self.assertEqual(len(self.order_book.bids.keys()), 1) self.assertEqual(len(self.order_book.asks.keys()), 2) self.assertEqual(len(self.completed_trades), 6) self.assertEqual(len(self.order_book.ongoing_orders), 3) self.assertEqual(len(self.completed_orders), 6) def test_modify_order(self): quote = test_quotes[str(10)] if "price" in quote: quote["price"] = Decimal(quote["price"]) new_order = json.loads( self.order_book.modify_order(self.orders[8]["order_id"], quote)) self.assertEqual(new_order["price"], '75') def test_cancel_order(self): quote = test_quotes[str(10)] if "price" in quote: quote["price"] = Decimal(quote["price"]) new_order = json.loads(self.order_book.process_order(quote)) self.order_book.cancel_order(new_order["order_id"]) self.assertEqual(len(self.order_book.bids.keys()), 1)
class OnlineDA(object): def __init__(self): self.ob=OrderBook() self.day=0 self.trades=[] def nextDay(self): self.day+=1 self.trades=[] self.ob.asks=OrderTree() self.ob.bids=OrderTree() def submitOrder(self, order): trades, remOrder=self.ob.process_order(order, False, False) if len(trades)>0: self.trades.extend(trades) if not remOrder: order['isFilled']=True return trades, order if remOrder['quantity']<order['quantity']: order['isFilled']=True return trades, order order['isFilled']=False return trades, order def getOpenPrice(self): if len(self.trades)>0: return self.trades[0]['price'] return 0 def getClosePrice(self): if len(self.trades)>0: return self.trades[-1]['price'] return 0 def getHighPrice(self): if len(self.trades)>0: return np.max([t['price'] for t in self.trades]) return 0 def getLowPrice(self): if len(self.trades)>0: return np.min([t['price'] for t in self.trades]) return 0 def getVolume(self): return np.sum([t['quantity'] for t in self.trades])
def main(): # TODO: Create three agents: order_book = OrderBook() agents = [101, 102, 103] # TODO: Agents send in buy/sell orders randomly with some probablility for i in range(30): print '\nTimestep# {}: '.format(i) for a in agents: if (random.random() > 0.5): # make an order # buy or sell? # TO BE REPLACED WITH TRADING STRATEGIES tradedir = random.choice(['bid', 'ask']) # how many? ordqty = random.randint(1, 20) # market or limit? tradetype = random.choice(['market', 'limit']) # if limit, what is your bid/ask price if tradetype == 'limit': # TO BE REPLACED WITH TRADING STRATEGIES limitprice = random.uniform(90, 100) # send in the order if tradetype == 'market': agent_order = { 'type': 'market', 'side': tradedir, 'quantity': ordqty, 'trade_id': a } print 'agent {} made a market order to {} {} shares'.format( a, tradedir, ordqty) else: agent_order = { 'type': 'limit', 'side': tradedir, 'quantity': ordqty, 'price': limitprice, 'trade_id': a } print 'agent {} made a limit order at {} to {} {} shares'.format( a, limitprice, tradedir, ordqty) trades, order_in_book = order_book.process_order( agent_order, False, False) else: print 'agent {} did not trade'.format(a) # plot how the stock changes print order_book
def main(): # TODO: Create three agents: order_book = OrderBook() agents = [101, 102, 103] # TODO: Agents send in buy/sell orders randomly with some probablility for i in range(30): print '\nTimestep# {}: '.format(i) for a in agents: if (random.random() > 0.5): # make an order # buy or sell? # TO BE REPLACED WITH TRADING STRATEGIES tradedir = random.choice(['bid','ask']) # how many? ordqty = random.randint(1,20) # market or limit? tradetype = random.choice(['market', 'limit']) # if limit, what is your bid/ask price if tradetype == 'limit': # TO BE REPLACED WITH TRADING STRATEGIES limitprice = random.uniform(90,100) # send in the order if tradetype == 'market': agent_order = {'type':'market', 'side':tradedir, 'quantity':ordqty, 'trade_id':a} print 'agent {} made a market order to {} {} shares'.format(a, tradedir, ordqty) else: agent_order = {'type':'limit', 'side':tradedir, 'quantity':ordqty, 'price':limitprice, 'trade_id':a} print 'agent {} made a limit order at {} to {} {} shares'.format(a, limitprice, tradedir, ordqty) trades, order_in_book = order_book.process_order(agent_order, False, False) else: print 'agent {} did not trade'.format(a) # plot how the stock changes print order_book
'quantity': 5, 'price': 99, 'trade_id': 102 }, { 'type': 'limit', 'side': 'bid', 'quantity': 5, 'price': 97, 'trade_id': 103 }, ] # Add orders to order book for order in limit_orders: trades, order_id = order_book.process_order(order, False, False) # The current book may be viewed using a print print order_book # Submitting a limit order that crosses the opposing best price will result in a trade crossing_limit_order = { 'type': 'limit', 'side': 'bid', 'quantity': 2, 'price': 102, 'trade_id': 109 } print crossing_limit_order trades, order_in_book = order_book.process_order(crossing_limit_order, False,
class MarketSim(object): def __init__(self, order_arrival_lambda=2, initial_price=100., price_sigma=.2, quantity_mu=2, quantity_sigma=1, periods=None, traders=50, tick_size=.0001, purge_after_vol=100, open_time=dt.datetime(2017, 1, 1, 8, 0, 0), close_time=dt.datetime(2017, 1, 1, 15, 0, 0)): self.p0 = initial_price self.p = initial_price self.tick_size = tick_size self.purge_after_vol = purge_after_vol self.order_arrival_lambda = order_arrival_lambda self.price_sigma = price_sigma self.quantity_mu = quantity_mu self.quantity_sigma = quantity_sigma if periods: self.periods = periods else: self.periods = (close_time-open_time).total_seconds() if type(traders) == int: traders = range(traders) self.traders = traders self.orderbook = OrderBook() self.total_orders_submitted = 0 self.sides = np.array(['ask', 'bid']) self.sign = np.array([1., -1.]) self.time = 0 self.orders_by_vol = deque(maxlen=None) def simulate_batch(self, size=10000, verbose=False): # number of orders that arrive inter_arrival_times = np.random.exponential(self.order_arrival_lambda, size=size) timestamps = inter_arrival_times.cumsum() + self.time self.process_orders(timestamps, verbose) self.purge_stale_orders() self.time = timestamps[-1] def simulate_periods(self, periods=None, verbose=False): if not periods: periods = self.periods while self.time < periods: self.simulate_batch(size=min(10000, periods/10), verbose=verbose) def process_orders(self, timestamps, verbose=False): # if new orders arrived--add them to the book n_orders = len(timestamps) # draw side (buy/sell) distribution is binomial, p=.5 order_side = np.random.choice([0, 1], size=n_orders, replace=True) # randomly assign traders to each slot order_trader = np.random.choice(self.traders, size=n_orders, replace=True) order_price_shocks = np.random.lognormal(0, self.price_sigma, size=n_orders) order_quantity = np.ceil(np.random.lognormal(self.quantity_mu, self.quantity_sigma, size=n_orders)) for i in xrange(n_orders): if timestamps[i] > self.periods: break side = self.sides[order_side][i] order_price = self.p * order_price_shocks[i] + self.sign[order_side][i] * self.tick_size * self.p quote = { "type": "limit", "side": side, "timestamp": timestamps[i], "trade_id": order_trader[i], "order_id": self.total_orders_submitted, "price": order_price, 'quantity': int(order_quantity[i]) } self.orderbook.process_order(quote, from_data=True, verbose=verbose) self.log_order_type(side) self.total_orders_submitted += 1 self.sync_book_price() def purge_stale_orders(self): if len(self.orders_by_vol) > 0 & len(self.orderbook.tape) >= 100: current_vol = len(self.orderbook.tape) while current_vol-self.purge_after_vol >= self.orders_by_vol[0][0]: _, side, order_id = self.orders_by_vol.popleft() self.orderbook.cancel_order(side=side, order_id=order_id) if len(self.orders_by_vol) == 0: break def sync_book_price(self): if len(self.orderbook.tape) > 0: self.p = float(self.orderbook.tape[-1]['price']) def plot_prices(self, time_scale=1.,plot_params={}): interval_trades = self.get_transactions(time_scale=time_scale) interval_trades.set_index('time')['price'].apply(lambda x: float(x)).plot(**plot_params) def get_tape_dataframe(self): return pd.DataFrame(list(self.orderbook.tape)) def show_stats(self): n_trades = len(self.orderbook.tape) mean_quantity = self.get_tape_dataframe()['quantity'].apply(lambda x: float(x)).mean() print("trades per second: %3.3f" % (n_trades / self.time)) print("quantity per 600: %3.3f" % (mean_quantity * 600)) def log_order_type(self, side): order_id = self.total_orders_submitted current_vol = len(self.orderbook.tape) self.orders_by_vol.append((current_vol, str(side), int(order_id))) @staticmethod def get_side_id(side, party1, party2): if party1[1] == side: return party1[0] else: return party2[0] def get_transactions(self, time_scale=1): tape = self.get_tape_dataframe() tape['price'] = tape['price'].apply(float) tape['quantity'] = tape['quantity'].apply(int) tape['time'] = tape['timestamp'].apply(lambda x: np.floor(x/time_scale)) tape['quantity'] = (tape.groupby('time')['quantity'].transform('sum')) agg = tape.groupby('time').last().reset_index() agg['ask_id'] = agg.apply(lambda row: MarketSim.get_side_id('ask', row['party1'], row['party2']), axis=1) agg['bid_id'] = agg.apply(lambda row: MarketSim.get_side_id('bid', row['party1'], row['party2']), axis=1) return agg[['time', 'ask_id', 'bid_id', 'quantity', 'price']] def export_transactions(self, filename): self.get_transactions().to_csv(filename, index=False)
from orderbook import OrderBook # Create an order book order_book = OrderBook() # Create some limit orders limit_orders = [ {'type' : 'limit', 'side' : 'bid', 'quantity' : 200,'price' : 161, 'trade_id' : 100, 'order_id' : 100, 'timestamp' : 1}, {'type' : 'limit', 'side' : 'bid', 'quantity' : 300,'price' : 162, 'trade_id' : 101, 'order_id' : 101, 'timestamp' : 2}, {'type' : 'limit', 'side' : 'bid', 'quantity' : 500,'price' : 163, 'trade_id' : 102, 'order_id' : 102, 'timestamp' : 3}, {'type' : 'limit', 'side' : 'bid', 'quantity' : 200,'price' : 164, 'trade_id' : 103, 'order_id' : 103, 'timestamp' : 4}, {'type' : 'limit', 'side' : 'ask', 'quantity' : 400,'price' : 160, 'trade_id' : 200, 'order_id' : 200, 'timestamp' : 5}, {'type' : 'limit', 'side' : 'ask', 'quantity' : 400,'price' : 161, 'trade_id' : 201, 'order_id' : 201, 'timestamp' : 6}, {'type' : 'limit', 'side' : 'ask', 'quantity' : 100,'price' : 162, 'trade_id' : 202, 'order_id' : 202, 'timestamp' : 7}, {'type' : 'limit', 'side' : 'ask', 'quantity' : 300,'price' : 163, 'trade_id' : 203, 'order_id' : 203, 'timestamp' : 8}, {'type' : 'limit', 'side' : 'ask', 'quantity' : 300,'price' : 164, 'trade_id' : 204, 'order_id' : 204, 'timestamp' : 9}, ] # Add orders to order book for order in limit_orders: trades, order_id = order_book.process_order(order, True, False) #print(trades, order_id) # The current book may be viewed using a print print(order_book)
q = order_book.asks else: sys.exit('not given bid or ask') for order in q.price_tree.get(Decimal(line['price']), []): if order.trade_id == line['trade_id']: order_book.cancel_order(line['side'], order.order_id) elif line['type'] == 'modify': order_book.modify_order(line['order_id'], { 'side': line['side'], 'price': line['price'], 'quantity': line['quantity']}) else: (trade, order) = order_book.process_order(line, False, False) myalgo.process_trade(trade, mode) if len(algo_orders) > 0: printme("\n") printme("After algo") print_orderbook(order_book, old_orderbook) stats=myalgo.stats() printme ("total volume=", stats[0]) printme ("my volume=", stats[1]) printme ("participation=", stats[3]) else: printme("No action by algo") printme("--------- END -------") reader.close() except IOError:
'trade_id' : 101}, {'type' : 'limit', 'side' : 'bid', 'quantity' : 5, 'price' : 99, 'trade_id' : 102}, {'type' : 'limit', 'side' : 'bid', 'quantity' : 5, 'price' : 97, 'trade_id' : 103}, ] # Add orders to order book for order in limit_orders: trades, order_id = order_book.process_order(order, False, False) # The current book may be viewed using a print print order_book # Submitting a limit order that crosses the opposing best price will result in a trade crossing_limit_order = {'type': 'limit', 'side': 'bid', 'quantity': 2, 'price': 102, 'trade_id': 109} print crossing_limit_order trades, order_in_book = order_book.process_order(crossing_limit_order, False, False) print "Trade occurs as incoming bid limit crosses best ask" print trades
def test_sequence3(self): """ test sequence 1: add ,add ,add,add,cancel,cancel """ obj = OrderBook() self.assertEqual(obj.get_best_bid_and_ask('AAPL'), (0, 0)) obj.process_order("1568390201|abbb11|a|AAPL|B|209.00000|100") self.assertEqual(obj.get_best_bid_and_ask('AAPL'), (209.0, 0)) obj.process_order("1568390201|abbb12|a|AAPL|B|209.50000|100") self.assertEqual(obj.get_best_bid_and_ask('AAPL'), (209.5, 0)) obj.process_order("1568390203|abbb13|a|AAPL|S|210.00000|10") self.assertEqual(obj.get_best_bid_and_ask('AAPL'), (209.5, 210.0)) obj.process_order("1568390204|abbb14|a|AAPL|S|209.90000|10") self.assertEqual(obj.get_best_bid_and_ask('AAPL'), (209.5, 209.9)) obj.process_order("1568390243|abbb12|c") self.assertEqual(obj.get_best_bid_and_ask('AAPL'), (209.0, 209.9)) obj.process_order("1568390244|abbb14|c") self.assertEqual(obj.get_best_bid_and_ask('AAPL'), (209.0, 210.0))
printme('') printme("RUNNING ALGO WITH MODE=", mode) old_orderbook = copy.deepcopy(order_book) for line in algo_orders: printme(pprint.pformat(line)) if line['type'] == 'cancel': order_book.cancel_order(line['side'], line['order_id']) elif line['type'] == 'modify': order_book.modify_order(line['order_id'], { 'side': line['side'], 'price': line['price'], 'quantity': line['quantity']}) else: (trade, order) = order_book.process_order(line, False, False) myalgo.process_trade(trade, mode) if len(algo_orders) > 0: printme("\n") printme("After algo") print_orderbook(order_book, old_orderbook) stats=myalgo.stats() printme ("total volume=", stats[0]) printme ("my volume=", stats[1]) printme ("participation=", stats[3]) else: printme("No action by algo") printme("--------- END -------") reader.close() except IOError:
class OrderBookTestCase(unittest.TestCase): ''' Unittest for the OrderBook module ''' def setUp(self): self.order_book = OrderBook() self.limit_orders = [{'type' : 'limit', 'side' : 'ask', 'quantity' : 5, 'price' : 101}, {'type' : 'limit', 'side' : 'ask', 'quantity' : 5, 'price' : 103}, {'type' : 'limit', 'side' : 'ask', 'quantity' : 5, 'price' : 101}, {'type' : 'limit', 'side' : 'ask', 'quantity' : 5, 'price' : 101}, {'type' : 'limit', 'side' : 'bid', 'quantity' : 5, 'price' : 99}, {'type' : 'limit', 'side' : 'bid', 'quantity' : 5, 'price' : 98}, {'type' : 'limit', 'side' : 'bid', 'quantity' : 5, 'price' : 99}, {'type' : 'limit', 'side' : 'bid', 'quantity' : 5, 'price' : 97}, ] for order in self.limit_orders: trades, order_id = self.order_book.process_order(order) def test_process_asks_order(self): ''' set up asks orders for unittests ''' self.assertEquals(self.order_book.asks.order_map[1].price, 101.0) self.assertEquals(self.order_book.asks.order_map[2].price, 103.0) self.assertEquals(self.order_book.asks.order_map[3].price, 101.0) self.assertEquals(self.order_book.asks.order_map[4].price, 101.0) def test_process_bids_order(self): ''' set up bids orders for unittests ''' self.assertEquals(self.order_book.bids.order_map[5].price, 99.0) self.assertEquals(self.order_book.bids.order_map[6].price, 98.0) self.assertEquals(self.order_book.bids.order_map[7].price, 99.0) self.assertEquals(self.order_book.bids.order_map[8].price, 97.0) def test_trades(self): ''' Submitting a limit order that crosses the opposing best price will result in a trade ''' crossing_limit_order = {'type': 'limit', 'side': 'bid', 'quantity': 2, 'price': 102} trades, order_in_book = self.order_book.process_order(\ crossing_limit_order) self.assertEquals(trades[0]['price'], 101) self.assertEquals(trades[0]['quantity'],2) def test_large_trades(self): ''' If a limit crosses but is only partially matched, the remaning volume will be placed in the book as an outstanding order ''' big_crossing_limit_order = {'type': 'limit', 'side': 'bid', 'quantity': 50, 'price': 102} trades, order_in_book = self.order_book.process_order(\ big_crossing_limit_order) self.assertEquals(order_in_book['price'], 102) self.assertEquals(order_in_book['quantity'], 35)
class OrderBookTestCase(unittest.TestCase): ''' Unittest for the OrderBook module ''' def setUp(self): self.order_book = OrderBook() self.limit_orders = [ { 'type': 'limit', 'side': 'ask', 'quantity': 5, 'price': 101 }, { 'type': 'limit', 'side': 'ask', 'quantity': 5, 'price': 103 }, { 'type': 'limit', 'side': 'ask', 'quantity': 5, 'price': 101 }, { 'type': 'limit', 'side': 'ask', 'quantity': 5, 'price': 101 }, { 'type': 'limit', 'side': 'bid', 'quantity': 5, 'price': 99 }, { 'type': 'limit', 'side': 'bid', 'quantity': 5, 'price': 98 }, { 'type': 'limit', 'side': 'bid', 'quantity': 5, 'price': 99 }, { 'type': 'limit', 'side': 'bid', 'quantity': 5, 'price': 97 }, ] for order in self.limit_orders: trades, order_id = self.order_book.process_order(order) def test_process_asks_order(self): ''' set up asks orders for unittests ''' self.assertEquals(self.order_book.asks.order_map[1].price, 101.0) self.assertEquals(self.order_book.asks.order_map[2].price, 103.0) self.assertEquals(self.order_book.asks.order_map[3].price, 101.0) self.assertEquals(self.order_book.asks.order_map[4].price, 101.0) def test_process_bids_order(self): ''' set up bids orders for unittests ''' self.assertEquals(self.order_book.bids.order_map[5].price, 99.0) self.assertEquals(self.order_book.bids.order_map[6].price, 98.0) self.assertEquals(self.order_book.bids.order_map[7].price, 99.0) self.assertEquals(self.order_book.bids.order_map[8].price, 97.0) def test_trades(self): ''' Submitting a limit order that crosses the opposing best price will result in a trade ''' crossing_limit_order = { 'type': 'limit', 'side': 'bid', 'quantity': 2, 'price': 102 } trades, order_in_book = self.order_book.process_order(\ crossing_limit_order) self.assertEquals(trades[0]['price'], 101) self.assertEquals(trades[0]['quantity'], 2) def test_large_trades(self): ''' If a limit crosses but is only partially matched, the remaning volume will be placed in the book as an outstanding order ''' big_crossing_limit_order = { 'type': 'limit', 'side': 'bid', 'quantity': 50, 'price': 102 } trades, order_in_book = self.order_book.process_order(\ big_crossing_limit_order) self.assertEquals(order_in_book['price'], 102) self.assertEquals(order_in_book['quantity'], 35)