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_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 try_to_get_ask(book: OrderBook, ask_id: int) -> str: try: book.get_ask(ask_id) except ValueError as err: return err.__str__() except Exception as exp: return exp.__str__()
def try_to_set_ask(book: OrderBook, price: float, quantity: int) -> str: try: book.set_ask(price, quantity) except ValueError as err: return err.__str__() except Exception as exp: return exp.__str__()
def try_to_del_bid(book: OrderBook, bid_id: int) -> str: try: book.del_bid(bid_id) except ValueError as err: return err.__str__() except Exception as exp: return exp.__str__()
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_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_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 __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_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_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 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 __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 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
class MarketOperator: def __init__(self): self.book = OrderBook() self.next_id = 0 def get_public_key(self): return str(self.book.public_key.n) def get_encrypted_book(self): return self.book.get_encrypted_book() def get_unencrypted_book(self): return self.book.get_unencrypted_book() def submit_bid(self, price_ciphertext, qty_ciphertext, price_nonce, qty_nonce): self.book.add_bid(Order(self.book, price_ciphertext, qty_ciphertext, price_nonce, qty_nonce, "bid", self.next_id)) self.next_id = self.next_id + 1 def submit_ask(self, price_ciphertext, qty_ciphertext, price_nonce, qty_nonce): self.book.add_ask(Order(self.book, price_ciphertext, qty_ciphertext, price_nonce, qty_nonce, "ask", self.next_id)) self.next_id = self.next_id + 1 def get_history(self): s = "" for action in self.book.history: s += action + '\n' return s
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_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'
class Engine(object): def __init__(self): self.order_book = OrderBook() self.ts = 0 def execute(self, message): self.ts += 1 try: order_action = parse_input_message(message, self.ts) if order_action.action_type == ACTION_TYPE_ADD_ORDER: trades = self.order_book.add_order(order_action.order) for trade in trades: output_trade(trade) elif order_action.action_type == ACTION_TYPE_CANCEL_ORDER: self.order_book.cancel_order(order_action.order_id) except WrongInputFormatException: sys.stderr.write('Wrong input format: {}\n'.format(message)) except WrongMessageTypeException: sys.stderr.write('Wrong message type: {}\n'.format(message)) except UnknownOrderException: sys.stderr.write('Unknown order: {}\n'.format(message)) except DuplicateOrderException: sys.stderr.write('Duplicate order: {}\n'.format(message))
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 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 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 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 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_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 _run_order_book(self): os.system('clear') from order_book import OrderBook order_book = OrderBook() order_book.print_table()