def _get_order_book(base_asset, quote_asset, bid_book_min_pct_fee_provided=None, bid_book_min_pct_fee_required=None, bid_book_max_pct_fee_required=None, ask_book_min_pct_fee_provided=None, ask_book_min_pct_fee_required=None, ask_book_max_pct_fee_required=None): """Gets the current order book for a specified asset pair @param: normalized_fee_required: Only specify if buying BTC. If specified, the order book will be pruned down to only show orders at and above this fee_required @param: normalized_fee_provided: Only specify if selling BTC. If specified, the order book will be pruned down to only show orders at and above this fee_provided """ base_asset_info = config.mongo_db.tracked_assets.find_one( {'asset': base_asset}) quote_asset_info = config.mongo_db.tracked_assets.find_one( {'asset': quote_asset}) if not base_asset_info or not quote_asset_info: raise Exception("Invalid asset(s)") # TODO: limit # results to 8 or so for each book (we have to sort as well to limit) base_bid_filters = [ { "field": "get_asset", "op": "==", "value": base_asset }, { "field": "give_asset", "op": "==", "value": quote_asset }, ] base_ask_filters = [ { "field": "get_asset", "op": "==", "value": quote_asset }, { "field": "give_asset", "op": "==", "value": base_asset }, ] if base_asset == config.BTC or quote_asset == config.BTC: extra_filters = [ { 'field': 'give_remaining', 'op': '>', 'value': 0 }, # don't show empty BTC orders { 'field': 'get_remaining', 'op': '>', 'value': 0 }, # don't show empty BTC orders { 'field': 'fee_required_remaining', 'op': '>=', 'value': 0 }, { 'field': 'fee_provided_remaining', 'op': '>=', 'value': 0 }, ] base_bid_filters += extra_filters base_ask_filters += extra_filters base_bid_orders = util.call_jsonrpc_api("get_orders", { 'filters': base_bid_filters, 'show_expired': False, 'status': 'open', 'order_by': 'block_index', 'order_dir': 'asc', }, abort_on_error=True)['result'] base_ask_orders = util.call_jsonrpc_api("get_orders", { 'filters': base_ask_filters, 'show_expired': False, 'status': 'open', 'order_by': 'block_index', 'order_dir': 'asc', }, abort_on_error=True)['result'] def get_o_pct(o): if o['give_asset'] == config.BTC: # NB: fee_provided could be zero here pct_fee_provided = float( (D(o['fee_provided_remaining']) / D(o['give_quantity']))) else: pct_fee_provided = None if o['get_asset'] == config.BTC: # NB: fee_required could be zero here pct_fee_required = float( (D(o['fee_required_remaining']) / D(o['get_quantity']))) else: pct_fee_required = None return pct_fee_provided, pct_fee_required # filter results by pct_fee_provided and pct_fee_required for BTC pairs as appropriate filtered_base_bid_orders = [] filtered_base_ask_orders = [] if base_asset == config.BTC or quote_asset == config.BTC: for o in base_bid_orders: pct_fee_provided, pct_fee_required = get_o_pct(o) addToBook = True if bid_book_min_pct_fee_provided is not None and pct_fee_provided is not None and pct_fee_provided < bid_book_min_pct_fee_provided: addToBook = False if bid_book_min_pct_fee_required is not None and pct_fee_required is not None and pct_fee_required < bid_book_min_pct_fee_required: addToBook = False if bid_book_max_pct_fee_required is not None and pct_fee_required is not None and pct_fee_required > bid_book_max_pct_fee_required: addToBook = False if addToBook: filtered_base_bid_orders.append(o) for o in base_ask_orders: pct_fee_provided, pct_fee_required = get_o_pct(o) addToBook = True if ask_book_min_pct_fee_provided is not None and pct_fee_provided is not None and pct_fee_provided < ask_book_min_pct_fee_provided: addToBook = False if ask_book_min_pct_fee_required is not None and pct_fee_required is not None and pct_fee_required < ask_book_min_pct_fee_required: addToBook = False if ask_book_max_pct_fee_required is not None and pct_fee_required is not None and pct_fee_required > ask_book_max_pct_fee_required: addToBook = False if addToBook: filtered_base_ask_orders.append(o) else: filtered_base_bid_orders += base_bid_orders filtered_base_ask_orders += base_ask_orders def make_book(orders, isBidBook): book = {} for o in orders: if o['give_asset'] == base_asset: if base_asset == config.BTC and o[ 'give_quantity'] <= config.ORDER_BTC_DUST_LIMIT_CUTOFF: continue # filter dust orders, if necessary give_quantity = blockchain.normalize_quantity( o['give_quantity'], base_asset_info['divisible']) get_quantity = blockchain.normalize_quantity( o['get_quantity'], quote_asset_info['divisible']) unit_price = float((D(get_quantity) / D(give_quantity))) remaining = blockchain.normalize_quantity( o['give_remaining'], base_asset_info['divisible']) else: if quote_asset == config.BTC and o[ 'give_quantity'] <= config.ORDER_BTC_DUST_LIMIT_CUTOFF: continue # filter dust orders, if necessary give_quantity = blockchain.normalize_quantity( o['give_quantity'], quote_asset_info['divisible']) get_quantity = blockchain.normalize_quantity( o['get_quantity'], base_asset_info['divisible']) unit_price = float((D(give_quantity) / D(get_quantity))) remaining = blockchain.normalize_quantity( o['get_remaining'], base_asset_info['divisible']) id = "%s_%s_%s" % (base_asset, quote_asset, unit_price) #^ key = {base}_{bid}_{unit_price}, values ref entries in book book.setdefault(id, { 'unit_price': unit_price, 'quantity': 0, 'count': 0 }) book[id]['quantity'] += remaining # base quantity outstanding book[id]['count'] += 1 # num orders at this price level book = sorted(iter(book.values()), key=operator.itemgetter('unit_price'), reverse=isBidBook) #^ convert to list and sort -- bid book = descending, ask book = ascending return book # compile into a single book, at volume tiers base_bid_book = make_book(filtered_base_bid_orders, True) base_ask_book = make_book(filtered_base_ask_orders, False) # get stats like the spread and median if base_bid_book and base_ask_book: # don't do abs(), as this is "the amount by which the ask price exceeds the bid", so I guess it could be negative # if there is overlap in the book (right?) bid_ask_spread = float((D(base_ask_book[0]['unit_price']) - D(base_bid_book[0]['unit_price']))) bid_ask_median = float((D( max(base_ask_book[0]['unit_price'], base_bid_book[0]['unit_price'])) - (D(abs(bid_ask_spread)) / 2))) else: bid_ask_spread = 0 bid_ask_median = 0 # compose depth and round out quantities bid_depth = D(0) for o in base_bid_book: o['quantity'] = float(D(o['quantity'])) bid_depth += D(o['quantity']) o['depth'] = float(D(bid_depth)) bid_depth = float(D(bid_depth)) ask_depth = D(0) for o in base_ask_book: o['quantity'] = float(D(o['quantity'])) ask_depth += D(o['quantity']) o['depth'] = float(D(ask_depth)) ask_depth = float(D(ask_depth)) # compose raw orders orders = filtered_base_bid_orders + filtered_base_ask_orders for o in orders: # add in the blocktime to help makes interfaces more user-friendly (i.e. avoid displaying block # indexes and display datetimes instead) o['block_time'] = calendar.timegm( util.get_block_time(o['block_index']).timetuple()) * 1000 result = { 'base_bid_book': base_bid_book, 'base_ask_book': base_ask_book, 'bid_depth': bid_depth, 'ask_depth': ask_depth, 'bid_ask_spread': bid_ask_spread, 'bid_ask_median': bid_ask_median, 'raw_orders': orders, 'base_asset': base_asset, 'quote_asset': quote_asset } return result
def _get_order_book(base_asset, quote_asset, bid_book_min_pct_fee_provided=None, bid_book_min_pct_fee_required=None, bid_book_max_pct_fee_required=None, ask_book_min_pct_fee_provided=None, ask_book_min_pct_fee_required=None, ask_book_max_pct_fee_required=None): """Gets the current order book for a specified asset pair @param: normalized_fee_required: Only specify if buying BTC. If specified, the order book will be pruned down to only show orders at and above this fee_required @param: normalized_fee_provided: Only specify if selling BTC. If specified, the order book will be pruned down to only show orders at and above this fee_provided """ base_asset_info = config.mongo_db.tracked_assets.find_one({'asset': base_asset}) quote_asset_info = config.mongo_db.tracked_assets.find_one({'asset': quote_asset}) if not base_asset_info or not quote_asset_info: raise Exception("Invalid asset(s)") #TODO: limit # results to 8 or so for each book (we have to sort as well to limit) base_bid_filters = [ {"field": "get_asset", "op": "==", "value": base_asset}, {"field": "give_asset", "op": "==", "value": quote_asset}, ] base_ask_filters = [ {"field": "get_asset", "op": "==", "value": quote_asset}, {"field": "give_asset", "op": "==", "value": base_asset}, ] if base_asset == config.BTC or quote_asset == config.BTC: extra_filters = [ {'field': 'give_remaining', 'op': '>', 'value': 0}, #don't show empty BTC orders {'field': 'get_remaining', 'op': '>', 'value': 0}, #don't show empty BTC orders {'field': 'fee_required_remaining', 'op': '>=', 'value': 0}, {'field': 'fee_provided_remaining', 'op': '>=', 'value': 0}, ] base_bid_filters += extra_filters base_ask_filters += extra_filters base_bid_orders = util.call_jsonrpc_api("get_orders", { 'filters': base_bid_filters, 'show_expired': False, 'status': 'open', 'order_by': 'block_index', 'order_dir': 'asc', }, abort_on_error=True)['result'] base_ask_orders = util.call_jsonrpc_api("get_orders", { 'filters': base_ask_filters, 'show_expired': False, 'status': 'open', 'order_by': 'block_index', 'order_dir': 'asc', }, abort_on_error=True)['result'] def get_o_pct(o): if o['give_asset'] == config.BTC: #NB: fee_provided could be zero here pct_fee_provided = float(( D(o['fee_provided_remaining']) / D(o['give_quantity']) )) else: pct_fee_provided = None if o['get_asset'] == config.BTC: #NB: fee_required could be zero here pct_fee_required = float(( D(o['fee_required_remaining']) / D(o['get_quantity']) )) else: pct_fee_required = None return pct_fee_provided, pct_fee_required #filter results by pct_fee_provided and pct_fee_required for BTC pairs as appropriate filtered_base_bid_orders = [] filtered_base_ask_orders = [] if base_asset == config.BTC or quote_asset == config.BTC: for o in base_bid_orders: pct_fee_provided, pct_fee_required = get_o_pct(o) addToBook = True if bid_book_min_pct_fee_provided is not None and pct_fee_provided is not None and pct_fee_provided < bid_book_min_pct_fee_provided: addToBook = False if bid_book_min_pct_fee_required is not None and pct_fee_required is not None and pct_fee_required < bid_book_min_pct_fee_required: addToBook = False if bid_book_max_pct_fee_required is not None and pct_fee_required is not None and pct_fee_required > bid_book_max_pct_fee_required: addToBook = False if addToBook: filtered_base_bid_orders.append(o) for o in base_ask_orders: pct_fee_provided, pct_fee_required = get_o_pct(o) addToBook = True if ask_book_min_pct_fee_provided is not None and pct_fee_provided is not None and pct_fee_provided < ask_book_min_pct_fee_provided: addToBook = False if ask_book_min_pct_fee_required is not None and pct_fee_required is not None and pct_fee_required < ask_book_min_pct_fee_required: addToBook = False if ask_book_max_pct_fee_required is not None and pct_fee_required is not None and pct_fee_required > ask_book_max_pct_fee_required: addToBook = False if addToBook: filtered_base_ask_orders.append(o) else: filtered_base_bid_orders += base_bid_orders filtered_base_ask_orders += base_ask_orders def make_book(orders, isBidBook): book = {} for o in orders: if o['give_asset'] == base_asset: if base_asset == config.BTC and o['give_quantity'] <= config.ORDER_BTC_DUST_LIMIT_CUTOFF: continue #filter dust orders, if necessary give_quantity = blockchain.normalize_quantity(o['give_quantity'], base_asset_info['divisible']) get_quantity = blockchain.normalize_quantity(o['get_quantity'], quote_asset_info['divisible']) unit_price = float(( D(get_quantity) / D(give_quantity) )) remaining = blockchain.normalize_quantity(o['give_remaining'], base_asset_info['divisible']) else: if quote_asset == config.BTC and o['give_quantity'] <= config.ORDER_BTC_DUST_LIMIT_CUTOFF: continue #filter dust orders, if necessary give_quantity = blockchain.normalize_quantity(o['give_quantity'], quote_asset_info['divisible']) get_quantity = blockchain.normalize_quantity(o['get_quantity'], base_asset_info['divisible']) unit_price = float(( D(give_quantity) / D(get_quantity) )) remaining = blockchain.normalize_quantity(o['get_remaining'], base_asset_info['divisible']) id = "%s_%s_%s" % (base_asset, quote_asset, unit_price) #^ key = {base}_{bid}_{unit_price}, values ref entries in book book.setdefault(id, {'unit_price': unit_price, 'quantity': 0, 'count': 0}) book[id]['quantity'] += remaining #base quantity outstanding book[id]['count'] += 1 #num orders at this price level book = sorted(book.itervalues(), key=operator.itemgetter('unit_price'), reverse=isBidBook) #^ convert to list and sort -- bid book = descending, ask book = ascending return book #compile into a single book, at volume tiers base_bid_book = make_book(filtered_base_bid_orders, True) base_ask_book = make_book(filtered_base_ask_orders, False) #get stats like the spread and median if base_bid_book and base_ask_book: #don't do abs(), as this is "the amount by which the ask price exceeds the bid", so I guess it could be negative # if there is overlap in the book (right?) bid_ask_spread = float(( D(base_ask_book[0]['unit_price']) - D(base_bid_book[0]['unit_price']) )) bid_ask_median = float(( D( max(base_ask_book[0]['unit_price'], base_bid_book[0]['unit_price']) ) - (D(abs(bid_ask_spread)) / 2) )) else: bid_ask_spread = 0 bid_ask_median = 0 #compose depth and round out quantities bid_depth = D(0) for o in base_bid_book: o['quantity'] = float(D(o['quantity'])) bid_depth += D(o['quantity']) o['depth'] = float(D(bid_depth)) bid_depth = float(D(bid_depth)) ask_depth = D(0) for o in base_ask_book: o['quantity'] = float(D(o['quantity'])) ask_depth += D(o['quantity']) o['depth'] = float(D(ask_depth)) ask_depth = float(D(ask_depth)) #compose raw orders orders = filtered_base_bid_orders + filtered_base_ask_orders for o in orders: #add in the blocktime to help makes interfaces more user-friendly (i.e. avoid displaying block # indexes and display datetimes instead) o['block_time'] = time.mktime(util.get_block_time(o['block_index']).timetuple()) * 1000 result = { 'base_bid_book': base_bid_book, 'base_ask_book': base_ask_book, 'bid_depth': bid_depth, 'ask_depth': ask_depth, 'bid_ask_spread': bid_ask_spread, 'bid_ask_median': bid_ask_median, 'raw_orders': orders, 'base_asset': base_asset, 'quote_asset': quote_asset } return result