def test_orderbook_init(): with pytest.raises(TypeError): OrderBook('a') with pytest.raises(TypeError): OrderBook(blah=3) with pytest.raises(TypeError): OrderBook(max_depth='a')
def test_kraken_checksum(): # This checksum is from the kraken docs ob = OrderBook(max_depth=10, checksum_format='KRAKEN') asks = [["0.05005", "0.00000500", "1582905487.684110"], ["0.05010", "0.00000500", "1582905486.187983"], ["0.05015", "0.00000500", "1582905484.480241"], ["0.05020", "0.00000500", "1582905486.645658"], ["0.05025", "0.00000500", "1582905486.859009"], ["0.05030", "0.00000500", "1582905488.601486"], ["0.05035", "0.00000500", "1582905488.357312"], ["0.05040", "0.00000500", "1582905488.785484"], ["0.05045", "0.00000500", "1582905485.302661"], ["0.05050", "0.00000500", "1582905486.157467"]] bids = [["0.05000", "0.00000500", "1582905487.439814"], ["0.04995", "0.00000500", "1582905485.119396"], ["0.04990", "0.00000500", "1582905486.432052"], ["0.04980", "0.00000500", "1582905480.609351"], ["0.04975", "0.00000500", "1582905476.793880"], ["0.04970", "0.00000500", "1582905486.767461"], ["0.04965", "0.00000500", "1582905481.767528"], ["0.04960", "0.00000500", "1582905487.378907"], ["0.04955", "0.00000500", "1582905483.626664"], ["0.04950", "0.00000500", "1582905488.509872"]] for a in asks: ob.asks[Decimal(a[0])] = Decimal(a[1]) for b in bids: ob.bids[Decimal(b[0])] = Decimal(b[1]) assert ob.checksum() == 974947235
def __init__(self): super(ExchangeListener, self).__init__() # So I don't love having the public client call here but it seems like a reasonable place to init the book # using the level 3 feed to seed the order book public_client = PublicClient() seed_book = public_client.get_product_order_book('BTC-USD', level=3) self.order_book = OrderBook(seed_book=seed_book)
def test_get_best_bid_price_with_no_ask(): book = OrderBook() order_stream1 = '1568390201|abbb11|a|AAPL|S|209.00000|100' order_stream2 = '1568390202|abbb13|a|AAPL|S|220.00000|1000' book.process_order(order_stream1) book.process_order(order_stream2) assert book.best_bid_and_ask('AAPL') == (0, 220)
def test_get_bid_and_ask_for_missing_ticker(): book = OrderBook() order_stream1 = '1568390201|abbb11|a|AAPL|S|209.00000|100' order_stream2 = '1568390202|abbb13|a|AAPL|S|220.00000|1000' book.process_order(order_stream1) book.process_order(order_stream2) assert book.best_bid_and_ask('ZZZZ') == (0, 0)
def test_ftx_checksum(): BID = 'bid' ASK = 'ask' r = requests.get( "https://ftx.com/api/markets/BTC-PERP/orderbook?depth=100") r.raise_for_status() ftx_data = json.loads(r.text, parse_float=Decimal) def ftx_checksum(book): bid_it = reversed(book[BID]) ask_it = iter(book[ASK]) bids = [f"{bid}:{book[BID][bid]}" for bid in bid_it] asks = [f"{ask}:{book[ASK][ask]}" for ask in ask_it] if len(bids) == len(asks): combined = [val for pair in zip(bids, asks) for val in pair] elif len(bids) > len(asks): combined = [ val for pair in zip(bids[:len(asks)], asks) for val in pair ] combined += bids[len(asks):] else: combined = [ val for pair in zip(bids, asks[:len(bids)]) for val in pair ] combined += asks[len(bids):] computed = ":".join(combined).encode() return zlib.crc32(computed) ob = OrderBook(checksum_format='FTX') book = { BID: sd({ Decimal(update[0]): Decimal(update[1]) for update in ftx_data['result']['bids'] }), ASK: sd({ Decimal(update[0]): Decimal(update[1]) for update in ftx_data['result']['asks'] }) } ob.bids = { Decimal(update[0]): Decimal(update[1]) for update in ftx_data['result']['bids'] } ob.asks = { Decimal(update[0]): Decimal(update[1]) for update in ftx_data['result']['asks'] } ob.checksum() == ftx_checksum(book)
def __init__(self, name, addr, balance, base_cur, desired_cur): self.name = name self.exchange = Exchange(name, addr, balance) self.book = OrderBook(base_cur, desired_cur) self.exchange.book = self.book self.graph = Graph(self.exchange) self.traders = {} self.html = '' self.display_graph = False self.clearing_price = np.random.uniform(20, 50) self.first_time = True
def test_add_order_missing_details(): order_stream = '1568390243|abbb11|a|AAPL|B|209.00000' book = OrderBook() book.process_order(order_stream) assert book.exception_queue == [[ 'Invalid order string', '1568390243', 'abbb11', 'a', 'AAPL', 'B', '209.00000' ]] with pytest.raises(KeyError): book.orders['abbb11']
def setup(json_file='demo.json'): ''' ''' with open(json_file) as json_data: data = json.load(json_data) bids = TradeObject(data['bids']) asks = TradeObject(data['asks'], 'asks') order_book = OrderBook(bids, asks) return order_book
def __init__(self, accounts, products): self.accounts = accounts self.products = products self.order_books = { product: OrderBook(product) for product in products } self.orders_dict = {} # key = (account, product, side), value = price self.transaction_history = []
def profile_orderbook(): ob = OrderBook() for side, d in data.items(): if side == 'bids': for price, size, _ in d: ob.bids[Decimal(price)] = size elif side == 'asks': for price, size, _ in d: ob.asks[Decimal(price)] = size ob.to_dict()
def test_get_best_bid_and_ask_prices(): book = OrderBook() order_stream1 = '1568390201|abbb11|a|AAPL|B|209.00000|100' order_stream2 = '1568390202|abbb12|a|AAPL|S|210.00000|10' order_stream3 = '1568390202|abbb13|a|AAPL|B|220.00000|1000' order_stream4 = '1568390202|abbb14|a|AAPL|S|230.00000|500' book.process_order(order_stream1) book.process_order(order_stream2) book.process_order(order_stream3) book.process_order(order_stream4) assert book.best_bid_and_ask('AAPL') == (209, 230)
def test_market_data_check_max_ask_print_size_limiter(self): order_book = OrderBook(DEFAULT_TRADING_PAIR, 2, 2) orders = [generate_order_obj(type=OrderType.ASK) for _ in range(5)] for ord in orders: order_book.add_order(ord) old_market_data = order_book.market_data orders = sorted(orders, key=lambda i: i.price) delete_order = orders[0] order_book.remove_order(delete_order.id) assert order_book.market_data != old_market_data expected_data = self.expected_market_data( asks=self.aggregate_orders(orders[1:3])) assert order_book.market_data == expected_data
def test_orderbook_setitem(): ob = OrderBook() data = requests.get( "https://api.pro.coinbase.com/products/BTC-USD/book?level=2").json() ob.bids = {Decimal(price): size for price, size, _ in data['bids']} ob.asks = {Decimal(price): size for price, size, _ in data['asks']} assert ob.bids.index(0)[0] < ob.asks.index(0)[0] assert ob.bids.index(-1)[0] < ob.asks.index(-1)[0] assert ob.bids.index(-1)[0] < ob.bids.index(0)[0] assert ob.asks.index(-1)[0] > ob.asks.index(0)[0]
def test_add_order(): order_stream = '1568390243|abbb11|a|AAPL|B|209.00000|100' book = OrderBook() book.process_order(order_stream) assert book.orders['abbb11'] == { 'timestamp': 1568390243, 'id': 'abbb11', 'action': 'a', 'ticker': 'AAPL', 'side': 'B', 'price': 209, 'size': 100 }
def test_orderbook_setitem_invalid(): ob = OrderBook() with pytest.raises(ValueError): ob[123] = {} with pytest.raises(ValueError): ob['invalid'] = {} with pytest.raises(ValueError): del ob['bids'] with pytest.raises(ValueError): ob['bids'] = 'a'
def retrieve_order_data(book_data, pair, agg_data): """ Get spread, midpoint, and liquidity from book data. The information represents the status of the OrderBook at specific time. Time of retrieving is the last moment in each minute defined in aggregated market data (agg_data). :param book_data: order book data containing all limit order arrivals :type book_data: pandas.DataFrame :param pair: pair name of two currencies :type pair: str :param agg_data: minute level aggregated market information data :type agg_data: pandas.DataFrame :return: a minute level aggregated market information data with spread, midpoint, and liquidity added :rtype: pandas.DataFrame """ # filter order data based on pair book_data_sub = book_data.loc[book_data["pair"] == pair] book_data_sub.reset_index(drop=True, inplace=True) # print(book_data_sub.head()) order_book = OrderBook() agg_index = 0 for i in range(len(book_data_sub)): # update order book order_book.update_order(book_data_sub["price"][i], book_data_sub["amount"][i]) # get the time of next order if i + 1 < len(book_data_sub): next_time = book_data_sub["servert"][i + 1] # book data reached to the end, set next time as last time else: next_time = book_data_sub["servert"][i] curr_end_time = agg_data["period_end_time"][agg_index] # record data to agg_data when (1) next time surpasses the end time of current period and agg_data has at least # two rows unfilled. (2) agg_data reaches to the last row. # In case (2) the last row of agg_data will keep updating until book data depletes if ((next_time > curr_end_time) and (agg_index < len(agg_data))) or (agg_index == len(agg_data) - 1): # get info from order book curr_spread = order_book.get_spread() curr_midpoint = order_book.get_midpoint() curr_bid_liquidity, curr_ask_liquidity = order_book.get_liquidity() # fill in values into agg data agg_data["spread"][agg_index] = curr_spread agg_data["midpoint"][agg_index] = curr_midpoint agg_data["liquidity_bid"][agg_index] = curr_bid_liquidity agg_data["liquidity_ask"][agg_index] = curr_ask_liquidity # when agg_data reaches to the last row, index stop moving forward if agg_index < len(agg_data) - 1: agg_index += 1 return agg_data
def test_okcoin_checksum(): ob = OrderBook(checksum_format='OKCOIN') asks = { Decimal("3366.8"): Decimal("9"), Decimal("3368"): Decimal("8"), Decimal("3372"): Decimal("8") } bids = {Decimal("3366.1"): Decimal("7")} expected = 831078360 ob.bids = bids ob.asks = asks assert ob.checksum() == expected
def test_update_order(): order_stream1 = '1568390243|abbb11|a|AAPL|B|209.00000|100' order_stream2 = '1568390244|abbb11|u|101' book = OrderBook() book.process_order(order_stream1) book.process_order(order_stream2) assert book.orders['abbb11'] == { 'timestamp': 1568390244, 'id': 'abbb11', 'action': 'u', 'ticker': 'AAPL', 'side': 'B', 'price': 209, 'size': 101 }
def setUp(self): self.lp_2_gateway = deque() self.ob_2_ts = deque() self.ts_2_om = deque() self.ms_2_om = deque() self.om_2_ts = deque() self.gw_2_om = deque() self.om_2_gw = deque() self.liquidityProvider = LiquidityProvider(self.lp_2_gateway) self.bookBuilder = OrderBook(self.lp_2_gateway, self.ob_2_ts) self.tradingStrategy = TradingStrategy(self.ob_2_ts, self.ts_2_om, self.om_2_ts) self.marketSimulator = MarketSimulator(self.om_2_gw, self.gw_2_om) self.orderManager = OrderManager(self.ts_2_om, self.om_2_ts, self.om_2_gw, self.gw_2_om)
def books_from_lines_v1(lines, debug=False, end=None, drop_out_of_order=False): currBook = None # keep track of which side the book starts on, # if that side repeats we've reached a new book startSide = None nLine = 0 nBooks = 0 keep_out_of_order = not drop_out_of_order maxTimestamp = None book_list = [] for line in lines: if end and nBooks > end: break nLine += 1 if line[0:9] == "ORDERBOOK": nBooks += 1 if currBook is not None: if keep_out_of_order or currBook.last_update_time == maxTimestamp: book_list.append(currBook) timestr = line[10:] lastUpdateTime = parse_datetime_opt(timestr) if maxTimestamp is None or lastUpdateTime > maxTimestamp: maxTimestamp = lastUpdateTime currBook = OrderBook(day=None, last_update_time=lastUpdateTime, last_update_monotonic=None, bids=[], offers=[], actions=[]) else: row = line.split(',') side = row[obc.SIDE] entry = Order( timestamp=parse_datetime_opt(row[obc.TIMESTAMP]), side=side, level=int(row[obc.LEVEL]), price=float(row[obc.PRICE]), size=long(row[obc.SIZE]), #orderdepthcount = int(row[obc.ORDERDEPTHCOUNT]) #ccy = row[obc.CURRENCY] ) if (side == obc.BID): currBook.bids.append(entry) elif (side == obc.OFFER): currBook.offers.append(entry) return book_list
def test_orderbook_keys(): ob = OrderBook() ob['bids'][1] = 1 ob['BIDS'][1] = 2 ob['bid'][1] = 3 ob['BID'][1] = 4 assert ob.bids.to_dict() == {1: 4} assert ob.bid.to_dict() == {1: 4} ob['asks'][1] = 1 ob['ASKS'][1] = 2 ob['ask'][1] = 3 ob['ASK'][1] = 4 assert ob.asks.to_dict() == {1: 4} assert ob.ask.to_dict() == {1: 4}
def test_orderbook_getitem(): ob = OrderBook() data = requests.get( "https://api.pro.coinbase.com/products/BTC-USD/book?level=2").json() for side, d in data.items(): if side in {'bids', 'asks'}: for price, size, _ in d: ob[side][Decimal(price)] = size assert ob.bids.index(0)[0] < ob.asks.index(0)[0] assert ob.bids.index(-1)[0] < ob.asks.index(-1)[0] assert ob.bids.index(-1)[0] < ob.bids.index(0)[0] assert ob.asks.index(-1)[0] > ob.asks.index(0)[0] with pytest.raises(KeyError): # legal keys are BID, bid, BIDS, bids, ASK, ask, ASKS, asks ob['invalid'][1] = 3
def test_cancel_for_invalid_order(): order_stream1 = '1568390243|abbb11|a|AAPL|B|209.00000|100' order_stream2 = '1568390244|ZZZZZZ|c' book = OrderBook() book.process_order(order_stream1) book.process_order(order_stream2) assert book.orders['abbb11'] == { 'timestamp': 1568390243, 'id': 'abbb11', 'action': 'a', 'ticker': 'AAPL', 'side': 'B', 'price': 209, 'size': 100 } assert book.exception_queue == [[ 'Cancel for non existent order', '1568390244', 'ZZZZZZ', 'c' ]]
def populate_orderbook(): ob = OrderBook() data = requests.get( "https://api.pro.coinbase.com/products/BTC-USD/book?level=2").json() for side, d in data.items(): if side == 'bids': for price, size, _ in d: ob.bids[Decimal(price)] = size elif side == 'asks': for price, size, _ in d: ob.asks[Decimal(price)] = size assert ob.bids.index(0)[0] < ob.asks.index(0)[0] assert ob.bids.index(-1)[0] < ob.asks.index(-1)[0] assert ob.bids.index(-1)[0] < ob.bids.index(0)[0] assert ob.asks.index(-1)[0] > ob.asks.index(0)[0] return ob
def test_orderbook_len(): random.seed() bids = [] asks = [] for _ in range(500): bids.append(random.uniform(0.0, 100000.0)) bids = list(set(bids)) for _ in range(500): asks.append(random.uniform(0.0, 100000.0)) asks = list(set(asks)) ob = OrderBook() for b in bids: ob['BIDS'][b] = str(b) for a in asks: ob['ASKS'][a] = str(a) assert len(ob) == len(asks) + len(bids)
def start_new_orderbook(self, line): self.at_start_of_file = False # periodically clear the order cache so it doesn't eat all the memory if len(self.order_cache) > 5000: self.order_cache.clear() if self.currBook: self.books.append(self.currBook) _, _, monotonic_time, exchange_time = line.split(',') monotonic_seconds, _, monotonic_nanoseconds = monotonic_time.partition( ':') epoch_seconds, _, exchange_nanoseconds = exchange_time.partition(':') epoch_seconds = long(epoch_seconds) exchange_seconds = epoch_seconds % self.SECONDS_PER_DAY exchange_day = epoch_seconds / self.SECONDS_PER_DAY update_time = exchange_seconds * 1000 + long(exchange_nanoseconds) / ( 10**6) monotonic_time = long(monotonic_seconds) * 1000 + long( monotonic_nanoseconds) / (10**6) self.currBook = OrderBook(exchange_day, update_time, monotonic_time, [], [], self.actions) self.actions = []
def main(): lp_2_gateway = deque() ob_2_ts = deque() ts_2_om = deque() om_2_ts = deque() gw_2_om = deque() om_2_gw = deque() lp = LiquidityProvider(lp_2_gateway) ob = OrderBook(lp_2_gateway, ob_2_ts) ts = TradingStrategy(ob_2_ts, ts_2_om, om_2_ts) ms = MarketSimulator(om_2_gw, gw_2_om) om = OrderManager(ts_2_om, om_2_ts, om_2_gw, gw_2_om) lp.read_tick_data_from_data_source() while len(lp_2_gateway) > 0: ob.handle_order_from_gateway() ts.handle_input_from_bb() om.handle_input_from_ts() ms.handle_order_from_gw() om.handle_input_from_market() ts.handle_response_from_om() lp.read_tick_data_from_data_source()
def setUp(self): self.reforderbook = OrderBook()
def test_minimum_depth_kraken(): ob = OrderBook(max_depth=9, checksum_format='KRAKEN') with pytest.raises(ValueError): ob.checksum()