def get_trade_history(asset1=None, asset2=None, start_ts=None, end_ts=None, limit=50): """ Gets last N of trades within a specific date range (normally, for a specified asset pair, but this can be left blank to get any/all trades). """ assert (asset1 and asset2) or (not asset1 and not asset2) #cannot have one asset, but not the other if limit > 500: raise Exception("Requesting history of too many trades") now_ts = time.mktime(datetime.datetime.utcnow().timetuple()) if not end_ts: #default to current datetime end_ts = now_ts if not start_ts: #default to 30 days before the end date start_ts = end_ts - (30 * 24 * 60 * 60) filters = { "block_time": { "$gte": datetime.datetime.utcfromtimestamp(start_ts) } if end_ts == now_ts else { "$gte": datetime.datetime.utcfromtimestamp(start_ts), "$lte": datetime.datetime.utcfromtimestamp(end_ts) } } if asset1 and asset2: base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) filters["base_asset"] = base_asset filters["quote_asset"] = quote_asset last_trades = config.mongo_db.trades.find(filters, {'_id': 0}).sort("block_time", pymongo.DESCENDING).limit(limit) if not last_trades.count(): return False #no suitable trade data to form a market price last_trades = list(last_trades) return last_trades
def parse_trade_book(msg, msg_data): #book trades if (msg['category'] == 'order_matches' and ((msg['command'] == 'update' and msg_data['status'] == 'completed') #for a trade with BTC involved, but that is settled (completed) or ('forward_asset' in msg_data and msg_data['forward_asset'] != config.BTC and msg_data['backward_asset'] != config.BTC))): #or for a trade without BTC on either end if msg['command'] == 'update' and msg_data['status'] == 'completed': #an order is being updated to a completed status (i.e. a BTCpay has completed) tx0_hash, tx1_hash = msg_data['order_match_id'][:64], msg_data['order_match_id'][65:] #get the order_match this btcpay settles order_match = util.jsonrpc_api("get_order_matches", {'filters': [ {'field': 'tx0_hash', 'op': '==', 'value': tx0_hash}, {'field': 'tx1_hash', 'op': '==', 'value': tx1_hash}] }, abort_on_error=False)['result'][0] else: assert msg_data['status'] == 'completed' #should not enter a pending state for non BTC matches order_match = msg_data forward_asset_info = config.mongo_db.tracked_assets.find_one({'asset': order_match['forward_asset']}) backward_asset_info = config.mongo_db.tracked_assets.find_one({'asset': order_match['backward_asset']}) assert forward_asset_info and backward_asset_info base_asset, quote_asset = util.assets_to_asset_pair(order_match['forward_asset'], order_match['backward_asset']) #don't create trade records from order matches with BTC that are under the dust limit if (order_match['forward_asset'] == config.BTC and order_match['forward_quantity'] <= config.ORDER_BTC_DUST_LIMIT_CUTOFF) \ or (order_match['backward_asset'] == config.BTC and order_match['backward_quantity'] <= config.ORDER_BTC_DUST_LIMIT_CUTOFF): logger.debug("Order match %s ignored due to %s under dust limit." % (order_match['tx0_hash'] + order_match['tx1_hash'], config.BTC)) return 'ABORT_THIS_MESSAGE_PROCESSING' #take divisible trade quantities to floating point forward_quantity = blockchain.normalize_quantity(order_match['forward_quantity'], forward_asset_info['divisible']) backward_quantity = blockchain.normalize_quantity(order_match['backward_quantity'], backward_asset_info['divisible']) #compose trade trade = { 'block_index': config.state['cur_block']['block_index'], 'block_time': config.state['cur_block']['block_time_obj'], 'message_index': msg['message_index'], #secondary temporaral ordering off of when 'order_match_id': order_match['tx0_hash'] + '_' + order_match['tx1_hash'], 'order_match_tx0_index': order_match['tx0_index'], 'order_match_tx1_index': order_match['tx1_index'], 'order_match_tx0_address': order_match['tx0_address'], 'order_match_tx1_address': order_match['tx1_address'], 'base_asset': base_asset, 'quote_asset': quote_asset, 'base_quantity': order_match['forward_quantity'] if order_match['forward_asset'] == base_asset else order_match['backward_quantity'], 'quote_quantity': order_match['backward_quantity'] if order_match['forward_asset'] == base_asset else order_match['forward_quantity'], 'base_quantity_normalized': forward_quantity if order_match['forward_asset'] == base_asset else backward_quantity, 'quote_quantity_normalized': backward_quantity if order_match['forward_asset'] == base_asset else forward_quantity, } trade['unit_price'] = float( ( D(trade['quote_quantity_normalized']) / D(trade['base_quantity_normalized']) ).quantize( D('.00000000'), rounding=decimal.ROUND_HALF_EVEN)) trade['unit_price_inverse'] = float( ( D(trade['base_quantity_normalized']) / D(trade['quote_quantity_normalized']) ).quantize( D('.00000000'), rounding=decimal.ROUND_HALF_EVEN)) config.mongo_db.trades.insert(trade) logger.info("Procesed Trade from tx %s :: %s" % (msg['message_index'], trade))
def get_pairs_with_orders(addresses=[], max_pairs=12): pairs_with_orders = [] sources = '''AND source IN ({})'''.format(','.join(['?' for e in range(0, len(addresses))])) sql = '''SELECT (MIN(give_asset, get_asset) || '/' || MAX(give_asset, get_asset)) AS pair, COUNT(*) AS order_count FROM orders WHERE give_asset != get_asset AND status = ? {} GROUP BY pair ORDER BY order_count DESC LIMIT ?'''.format(sources) bindings = ['open'] + addresses + [max_pairs] my_pairs = util.call_jsonrpc_api('sql', {'query': sql, 'bindings': bindings})['result'] for my_pair in my_pairs: base_asset, quote_asset = util.assets_to_asset_pair(*tuple(my_pair['pair'].split("/"))) top_pair = { 'base_asset': base_asset, 'quote_asset': quote_asset, 'my_order_count': my_pair['order_count'] } if my_pair['pair'] == 'BTC/XCP': # XCP/BTC always in first pairs_with_orders.insert(0, top_pair) else: pairs_with_orders.append(top_pair) return pairs_with_orders
def get_order_book_simple(asset1, asset2, min_pct_fee_provided=None, max_pct_fee_required=None): #DEPRECATED 1.5 base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) result = _get_order_book(base_asset, quote_asset, bid_book_min_pct_fee_provided=min_pct_fee_provided, bid_book_max_pct_fee_required=max_pct_fee_required, ask_book_min_pct_fee_provided=min_pct_fee_provided, ask_book_max_pct_fee_required=max_pct_fee_required) return result
def get_market_details(asset1, asset2, min_fee_provided=0.95, max_fee_required=0.95): yesterday = int(time.time() - (24 * 60 * 60)) base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) supplies = get_assets_supply([base_asset, quote_asset]) price, trend, price24h, progression = get_price_movement(base_asset, quote_asset, supplies=supplies) buy_orders = [] sell_orders = [] market_orders = get_market_orders(base_asset, quote_asset, supplies=supplies, min_fee_provided=min_fee_provided, max_fee_required=max_fee_required) for order in market_orders: if order['type'] == 'SELL': sell_orders.append(order) elif order['type'] == 'BUY': buy_orders.append(order) last_trades = get_market_trades(base_asset, quote_asset, supplies=supplies) ext_info = False if config.mongo_db: ext_info = config.mongo_db.asset_extended_info.find_one( {'asset': base_asset}, {'_id': 0}) if ext_info and 'info_data' in ext_info: ext_info = ext_info['info_data'] else: ext_info = False return { 'base_asset': base_asset, 'quote_asset': quote_asset, 'price': format(price, ".8f"), 'trend': trend, 'progression': format(progression, ".2f"), 'price_24h': format(price24h, ".8f"), 'supply': supplies[base_asset][0], 'base_asset_divisible': supplies[base_asset][1], 'quote_asset_divisible': supplies[quote_asset][1], 'buy_orders': sorted(buy_orders, key=lambda x: D(x['price']), reverse=True), 'sell_orders': sorted(sell_orders, key=lambda x: D(x['price'])), 'last_trades': last_trades, 'base_asset_infos': ext_info }
def get_asset_pair_market_info(asset1=None, asset2=None, limit=50): """Given two arbitrary assets, returns the base asset and the quote asset. """ #DEPRECATED 1.5 assert (asset1 and asset2) or (asset1 is None and asset2 is None) if asset1 and asset2: base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) pair_info = config.mongo_db.asset_pair_market_info.find({'base_asset': base_asset, 'quote_asset': quote_asset}, {'_id': 0}) else: pair_info = config.mongo_db.asset_pair_market_info.find({}, {'_id': 0}).sort('completed_trades_count', pymongo.DESCENDING).limit(limit) #^ sort by this for now, may want to sort by a market_cap value in the future return list(pair_info) or []
def get_asset_pair_market_info(asset1=None, asset2=None, limit=50): """Given two arbitrary assets, returns the base asset and the quote asset. """ # DEPRECATED 1.5 assert (asset1 and asset2) or (asset1 is None and asset2 is None) if asset1 and asset2: base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) pair_info = config.mongo_db.asset_pair_market_info.find({'base_asset': base_asset, 'quote_asset': quote_asset}, {'_id': 0}) else: pair_info = config.mongo_db.asset_pair_market_info.find({}, {'_id': 0}).sort('completed_trades_count', pymongo.DESCENDING).limit(limit) #^ sort by this for now, may want to sort by a market_cap value in the future return list(pair_info) or []
def get_order_book_buysell(buy_asset, sell_asset, pct_fee_provided=None, pct_fee_required=None): # DEPRECATED 1.5 base_asset, quote_asset = util.assets_to_asset_pair(buy_asset, sell_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 if base_asset == config.BTC: if buy_asset == config.BTC: # if BTC is base asset and we're buying it, we're buying the BASE. we require a BTC fee (we're on the bid (bottom) book and we want a lower price) # - show BASE buyers (bid book) that require a BTC fee >= what we require (our side of the book) # - show BASE sellers (ask book) that provide a BTC fee >= what we require bid_book_min_pct_fee_required = pct_fee_required # my competition at the given fee required ask_book_min_pct_fee_provided = pct_fee_required elif sell_asset == config.BTC: # if BTC is base asset and we're selling it, we're selling the BASE. we provide a BTC fee (we're on the ask (top) book and we want a higher price) # - show BASE buyers (bid book) that provide a BTC fee >= what we provide # - show BASE sellers (ask book) that require a BTC fee <= what we provide (our side of the book) bid_book_max_pct_fee_required = pct_fee_provided ask_book_min_pct_fee_provided = pct_fee_provided # my competition at the given fee provided elif quote_asset == config.BTC: assert base_asset == config.XCP # only time when this is the case if buy_asset == config.BTC: # if BTC is quote asset and we're buying it, we're selling the BASE. we require a BTC fee (we're on the ask (top) book and we want a higher price) # - show BASE buyers (bid book) that provide a BTC fee >= what we require # - show BASE sellers (ask book) that require a BTC fee >= what we require (our side of the book) bid_book_min_pct_fee_provided = pct_fee_required ask_book_min_pct_fee_required = pct_fee_required # my competition at the given fee required elif sell_asset == config.BTC: # if BTC is quote asset and we're selling it, we're buying the BASE. we provide a BTC fee (we're on the bid (bottom) book and we want a lower price) # - show BASE buyers (bid book) that provide a BTC fee >= what we provide (our side of the book) # - show BASE sellers (ask book) that require a BTC fee <= what we provide bid_book_min_pct_fee_provided = pct_fee_provided # my compeitition at the given fee provided ask_book_max_pct_fee_required = pct_fee_provided result = _get_order_book( base_asset, quote_asset, bid_book_min_pct_fee_provided=bid_book_min_pct_fee_provided, bid_book_min_pct_fee_required=bid_book_min_pct_fee_required, bid_book_max_pct_fee_required=bid_book_max_pct_fee_required, ask_book_min_pct_fee_provided=ask_book_min_pct_fee_provided, ask_book_min_pct_fee_required=ask_book_min_pct_fee_required, ask_book_max_pct_fee_required=ask_book_max_pct_fee_required) # filter down raw_orders to be only open sell orders for what the caller is buying open_sell_orders = [] for o in result['raw_orders']: if o['give_asset'] == buy_asset: open_sell_orders.append(o) result['raw_orders'] = open_sell_orders return result
def get_order_book_buysell(buy_asset, sell_asset, pct_fee_provided=None, pct_fee_required=None): #DEPRECATED 1.5 base_asset, quote_asset = util.assets_to_asset_pair(buy_asset, sell_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 if base_asset == config.BTC: if buy_asset == config.BTC: #if BTC is base asset and we're buying it, we're buying the BASE. we require a BTC fee (we're on the bid (bottom) book and we want a lower price) # - show BASE buyers (bid book) that require a BTC fee >= what we require (our side of the book) # - show BASE sellers (ask book) that provide a BTC fee >= what we require bid_book_min_pct_fee_required = pct_fee_required #my competition at the given fee required ask_book_min_pct_fee_provided = pct_fee_required elif sell_asset == config.BTC: #if BTC is base asset and we're selling it, we're selling the BASE. we provide a BTC fee (we're on the ask (top) book and we want a higher price) # - show BASE buyers (bid book) that provide a BTC fee >= what we provide # - show BASE sellers (ask book) that require a BTC fee <= what we provide (our side of the book) bid_book_max_pct_fee_required = pct_fee_provided ask_book_min_pct_fee_provided = pct_fee_provided #my competition at the given fee provided elif quote_asset == config.BTC: assert base_asset == config.XCP #only time when this is the case if buy_asset == config.BTC: #if BTC is quote asset and we're buying it, we're selling the BASE. we require a BTC fee (we're on the ask (top) book and we want a higher price) # - show BASE buyers (bid book) that provide a BTC fee >= what we require # - show BASE sellers (ask book) that require a BTC fee >= what we require (our side of the book) bid_book_min_pct_fee_provided = pct_fee_required ask_book_min_pct_fee_required = pct_fee_required #my competition at the given fee required elif sell_asset == config.BTC: #if BTC is quote asset and we're selling it, we're buying the BASE. we provide a BTC fee (we're on the bid (bottom) book and we want a lower price) # - show BASE buyers (bid book) that provide a BTC fee >= what we provide (our side of the book) # - show BASE sellers (ask book) that require a BTC fee <= what we provide bid_book_min_pct_fee_provided = pct_fee_provided #my compeitition at the given fee provided ask_book_max_pct_fee_required = pct_fee_provided result = _get_order_book(base_asset, quote_asset, bid_book_min_pct_fee_provided=bid_book_min_pct_fee_provided, bid_book_min_pct_fee_required=bid_book_min_pct_fee_required, bid_book_max_pct_fee_required=bid_book_max_pct_fee_required, ask_book_min_pct_fee_provided=ask_book_min_pct_fee_provided, ask_book_min_pct_fee_required=ask_book_min_pct_fee_required, ask_book_max_pct_fee_required=ask_book_max_pct_fee_required) #filter down raw_orders to be only open sell orders for what the caller is buying open_sell_orders = [] for o in result['raw_orders']: if o['give_asset'] == buy_asset: open_sell_orders.append(o) result['raw_orders'] = open_sell_orders return result
def get_market_price_summary(asset1, asset2, with_last_trades=0, start_dt=None, end_dt=None): """Gets a synthesized trading "market price" for a specified asset pair (if available), as well as additional info. If no price is available, False is returned. """ if not end_dt: end_dt = datetime.datetime.utcnow() if not start_dt: start_dt = end_dt - datetime.timedelta(days=10) #default to 10 days in the past #look for the last max 6 trades within the past 10 day window base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) 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 isinstance(with_last_trades, int) or with_last_trades < 0 or with_last_trades > 30: raise Exception("Invalid with_last_trades") if not base_asset_info or not quote_asset_info: raise Exception("Invalid asset(s)") last_trades = config.mongo_db.trades.find({ "base_asset": base_asset, "quote_asset": quote_asset, 'block_time': { "$gte": start_dt, "$lte": end_dt } }, {'_id': 0, 'block_index': 1, 'block_time': 1, 'unit_price': 1, 'base_quantity_normalized': 1, 'quote_quantity_normalized': 1} ).sort("block_time", pymongo.DESCENDING).limit(max(config.MARKET_PRICE_DERIVE_NUM_POINTS, with_last_trades)) if not last_trades.count(): return None #no suitable trade data to form a market price (return None, NOT False here) last_trades = list(last_trades) last_trades.reverse() #from newest to oldest market_price = get_market_price( [last_trades[i]['unit_price'] for i in xrange(min(len(last_trades), config.MARKET_PRICE_DERIVE_NUM_POINTS))], [(last_trades[i]['base_quantity_normalized'] + last_trades[i]['quote_quantity_normalized']) for i in xrange(min(len(last_trades), config.MARKET_PRICE_DERIVE_NUM_POINTS))]) result = { 'market_price': float(D(market_price)), 'base_asset': base_asset, 'quote_asset': quote_asset, } if with_last_trades: #[0]=block_time, [1]=unit_price, [2]=base_quantity_normalized, [3]=quote_quantity_normalized, [4]=block_index result['last_trades'] = [[ t['block_time'], t['unit_price'], t['base_quantity_normalized'], t['quote_quantity_normalized'], t['block_index'] ] for t in last_trades] else: result['last_trades'] = [] return result
def get_order_book_simple(asset1, asset2, min_pct_fee_provided=None, max_pct_fee_required=None): # DEPRECATED 1.5 base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) result = _get_order_book( base_asset, quote_asset, bid_book_min_pct_fee_provided=min_pct_fee_provided, bid_book_max_pct_fee_required=max_pct_fee_required, ask_book_min_pct_fee_provided=min_pct_fee_provided, ask_book_max_pct_fee_required=max_pct_fee_required) return result
def get_market_orders(asset1, asset2, addresses=[], supplies=None, min_fee_provided=0.95, max_fee_required=0.95): base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) if not supplies: supplies = get_assets_supply([asset1, asset2]) market_orders = [] buy_orders = [] sell_orders = [] sql = '''SELECT orders.*, blocks.block_time FROM orders INNER JOIN blocks ON orders.block_index=blocks.block_index WHERE status = ? ''' bindings = ['open'] if len(addresses) > 0: sql += '''AND source IN ({}) '''.format(','.join(['?' for e in range(0,len(addresses))])) bindings += addresses sql += '''AND give_remaining > 0 AND give_asset IN (?, ?) AND get_asset IN (?, ?) ORDER BY tx_index DESC''' bindings += [asset1, asset2, asset1, asset2] orders = util.call_jsonrpc_api('sql', {'query': sql, 'bindings': bindings})['result'] for order in orders: market_order = {} exclude = False if order['give_asset'] == 'BTC': try: fee_provided = order['fee_provided'] / (order['give_quantity'] / 100) market_order['fee_provided'] = format(D(order['fee_provided']) / (D(order['give_quantity']) / D(100)), '.2f') except Exception, e: fee_provided = min_fee_provided - 1 # exclude exclude = fee_provided < min_fee_provided elif order['get_asset'] == 'BTC': try: fee_required = order['fee_required'] / (order['get_quantity'] / 100) market_order['fee_required'] = format(D(order['fee_required']) / (D(order['get_quantity']) / D(100)), '.2f') except Exception, e: fee_required = max_fee_required + 1 # exclude
def get_base_quote_asset(asset1, asset2): """Given two arbitrary assets, returns the base asset and the quote asset. """ #DEPRECATED 1.5 base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) 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}) pair_name = "%s/%s" % (base_asset, quote_asset) if not base_asset_info or not quote_asset_info: raise Exception("Invalid asset(s)") return { 'base_asset': base_asset, 'quote_asset': quote_asset, 'pair_name': pair_name }
def get_base_quote_asset(asset1, asset2): """Given two arbitrary assets, returns the base asset and the quote asset. """ # DEPRECATED 1.5 base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) 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}) pair_name = "%s/%s" % (base_asset, quote_asset) if not base_asset_info or not quote_asset_info: raise Exception("Invalid asset(s)") return { 'base_asset': base_asset, 'quote_asset': quote_asset, 'pair_name': pair_name }
def get_market_details(asset1, asset2, min_fee_provided=0.95, max_fee_required=0.95): yesterday = int(time.time() - (24 * 60 * 60)) base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) supplies = get_assets_supply([base_asset, quote_asset]) price, trend, price24h, progression = get_price_movement(base_asset, quote_asset, supplies=supplies) buy_orders = [] sell_orders = [] market_orders = get_market_orders(base_asset, quote_asset, supplies=supplies, min_fee_provided=min_fee_provided, max_fee_required=max_fee_required) for order in market_orders: if order['type'] == 'SELL': sell_orders.append(order) elif order['type'] == 'BUY': buy_orders.append(order) last_trades = get_market_trades(base_asset, quote_asset, supplies=supplies) ext_info = False if config.mongo_db: ext_info = config.mongo_db.asset_extended_info.find_one({'asset': base_asset}, {'_id': 0}) if ext_info and 'info_data' in ext_info: ext_info = ext_info['info_data'] else: ext_info = False return { 'base_asset': base_asset, 'quote_asset': quote_asset, 'price': format(price, ".8f"), 'trend': trend, 'progression': format(progression, ".2f"), 'price_24h': format(price24h, ".8f"), 'supply': supplies[base_asset][0], 'base_asset_divisible': supplies[base_asset][1], 'quote_asset_divisible': supplies[quote_asset][1], 'buy_orders': sorted(buy_orders, key=lambda x: D(x['price']), reverse=True), 'sell_orders': sorted(sell_orders, key=lambda x: D(x['price'])), 'last_trades': last_trades, 'base_asset_infos': ext_info }
def get_trade_history(asset1=None, asset2=None, start_ts=None, end_ts=None, limit=50): """ Gets last N of trades within a specific date range (normally, for a specified asset pair, but this can be left blank to get any/all trades). """ assert (asset1 and asset2) or ( not asset1 and not asset2) # cannot have one asset, but not the other if limit > 500: raise Exception("Requesting history of too many trades") now_ts = calendar.timegm(time.gmtime()) if not end_ts: # default to current datetime end_ts = now_ts if not start_ts: # default to 30 days before the end date start_ts = end_ts - (30 * 24 * 60 * 60) filters = { "block_time": { "$gte": datetime.datetime.utcfromtimestamp(start_ts) } if end_ts == now_ts else { "$gte": datetime.datetime.utcfromtimestamp(start_ts), "$lte": datetime.datetime.utcfromtimestamp(end_ts) } } if asset1 and asset2: base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) filters["base_asset"] = base_asset filters["quote_asset"] = quote_asset last_trades = config.mongo_db.trades.find(filters, { '_id': 0 }).sort("block_time", pymongo.DESCENDING).limit(limit) if not last_trades.count(): return False # no suitable trade data to form a market price last_trades = list(last_trades) return last_trades
def get_pairs_with_orders(addresses=[], max_pairs=12): pairs_with_orders = [] sources = '''AND source IN ({})'''.format(','.join( ['?' for e in range(0, len(addresses))])) sql = '''SELECT (MIN(give_asset, get_asset) || '/' || MAX(give_asset, get_asset)) AS pair, COUNT(*) AS order_count FROM orders WHERE give_asset != get_asset AND status = ? {} GROUP BY pair ORDER BY order_count DESC LIMIT ?'''.format(sources) bindings = ['open'] + addresses + [max_pairs] my_pairs = util.call_jsonrpc_api('sql', { 'query': sql, 'bindings': bindings })['result'] for my_pair in my_pairs: base_asset, quote_asset = util.assets_to_asset_pair( *tuple(my_pair['pair'].split("/"))) top_pair = { 'base_asset': base_asset, 'quote_asset': quote_asset, 'my_order_count': my_pair['order_count'] } if my_pair['pair'] == config.BTC_TO_XCP: # XCP/BTC always in first pairs_with_orders.insert(0, top_pair) else: pairs_with_orders.append(top_pair) return pairs_with_orders
def compile_asset_pair_market_info(): """Compiles the pair-level statistics that show on the View Prices page of counterwallet, for instance""" #loop through all open orders, and compile a listing of pairs, with a count of open orders for each pair mongo_db = config.mongo_db end_dt = datetime.datetime.utcnow() start_dt = end_dt - datetime.timedelta(days=1) start_block_index, end_block_index = database.get_block_indexes_for_dates( start_dt=start_dt, end_dt=end_dt) open_orders = util.call_jsonrpc_api("get_orders", { 'filters': [ { 'field': 'give_remaining', 'op': '>', 'value': 0 }, { 'field': 'get_remaining', 'op': '>', 'value': 0 }, { 'field': 'fee_required_remaining', 'op': '>=', 'value': 0 }, { 'field': 'fee_provided_remaining', 'op': '>=', 'value': 0 }, ], 'status': 'open', 'show_expired': False, }, abort_on_error=True)['result'] pair_data = {} asset_info = {} def get_price(base_quantity_normalized, quote_quantity_normalized): return float(D(quote_quantity_normalized / base_quantity_normalized)) #COMPOSE order depth, lowest ask, and highest bid column data for o in open_orders: (base_asset, quote_asset) = util.assets_to_asset_pair(o['give_asset'], o['get_asset']) pair = '%s/%s' % (base_asset, quote_asset) base_asset_info = asset_info.get( base_asset, mongo_db.tracked_assets.find_one({'asset': base_asset})) if base_asset not in asset_info: asset_info[base_asset] = base_asset_info quote_asset_info = asset_info.get( quote_asset, mongo_db.tracked_assets.find_one({'asset': quote_asset})) if quote_asset not in asset_info: asset_info[quote_asset] = quote_asset_info pair_data.setdefault( pair, { 'open_orders_count': 0, 'lowest_ask': None, 'highest_bid': None, 'completed_trades_count': 0, 'vol_base': 0, 'vol_quote': 0 }) #^ highest ask = open order selling base, highest bid = open order buying base #^ we also initialize completed_trades_count, vol_base, vol_quote because every pair inited here may # not have cooresponding data out of the trades_data_by_pair aggregation below pair_data[pair]['open_orders_count'] += 1 base_quantity_normalized = blockchain.normalize_quantity( o['give_quantity'] if base_asset == o['give_asset'] else o['get_quantity'], base_asset_info['divisible']) quote_quantity_normalized = blockchain.normalize_quantity( o['give_quantity'] if quote_asset == o['give_asset'] else o['get_quantity'], quote_asset_info['divisible']) order_price = get_price(base_quantity_normalized, quote_quantity_normalized) if base_asset == o['give_asset']: #selling base if pair_data[pair]['lowest_ask'] is None or order_price < pair_data[ pair]['lowest_ask']: pair_data[pair]['lowest_ask'] = order_price elif base_asset == o['get_asset']: #buying base if pair_data[pair][ 'highest_bid'] is None or order_price > pair_data[pair][ 'highest_bid']: pair_data[pair]['highest_bid'] = order_price #COMPOSE volume data (in XCP and BTC), and % change data #loop through all trade volume over the past 24h, and match that to the open orders trades_data_by_pair = mongo_db.trades.aggregate([ { "$match": { "block_time": { "$gte": start_dt, "$lte": end_dt } } }, { "$project": { "base_asset": 1, "quote_asset": 1, "base_quantity_normalized": 1, #to derive base volume "quote_quantity_normalized": 1 #to derive quote volume } }, { "$group": { "_id": { "base_asset": "$base_asset", "quote_asset": "$quote_asset" }, "vol_base": { "$sum": "$base_quantity_normalized" }, "vol_quote": { "$sum": "$quote_quantity_normalized" }, "count": { "$sum": 1 }, } } ]) trades_data_by_pair = [] if not trades_data_by_pair[ 'ok'] else trades_data_by_pair['result'] for e in trades_data_by_pair: pair = '%s/%s' % (e['_id']['base_asset'], e['_id']['quote_asset']) pair_data.setdefault(pair, { 'open_orders_count': 0, 'lowest_ask': None, 'highest_bid': None }) #^ initialize an empty pair in the event there are no open orders for that pair, but there ARE completed trades for it pair_data[pair]['completed_trades_count'] = e['count'] pair_data[pair]['vol_base'] = e['vol_base'] pair_data[pair]['vol_quote'] = e['vol_quote'] #compose price data, relative to BTC and XCP mps_xcp_btc, xcp_btc_price, btc_xcp_price = get_price_primatives() for pair, e in pair_data.iteritems(): base_asset, quote_asset = pair.split('/') _24h_vol_in_btc = None _24h_vol_in_xcp = None #derive asset price data, expressed in BTC and XCP, for the given volumes if base_asset == config.XCP: _24h_vol_in_xcp = e['vol_base'] _24h_vol_in_btc = blockchain.round_out( e['vol_base'] * xcp_btc_price) if xcp_btc_price else 0 elif base_asset == config.BTC: _24h_vol_in_xcp = blockchain.round_out( e['vol_base'] * btc_xcp_price) if btc_xcp_price else 0 _24h_vol_in_btc = e['vol_base'] else: #base is not XCP or BTC price_summary_in_xcp, price_summary_in_btc, price_in_xcp, price_in_btc, aggregated_price_in_xcp, aggregated_price_in_btc = \ get_xcp_btc_price_info(base_asset, mps_xcp_btc, xcp_btc_price, btc_xcp_price, with_last_trades=0, start_dt=start_dt, end_dt=end_dt) if price_in_xcp: _24h_vol_in_xcp = blockchain.round_out(e['vol_base'] * price_in_xcp) if price_in_btc: _24h_vol_in_btc = blockchain.round_out(e['vol_base'] * price_in_btc) if _24h_vol_in_xcp is None or _24h_vol_in_btc is None: #the base asset didn't have price data against BTC or XCP, or both...try against the quote asset instead price_summary_in_xcp, price_summary_in_btc, price_in_xcp, price_in_btc, aggregated_price_in_xcp, aggregated_price_in_btc = \ get_xcp_btc_price_info(quote_asset, mps_xcp_btc, xcp_btc_price, btc_xcp_price, with_last_trades=0, start_dt=start_dt, end_dt=end_dt) if _24h_vol_in_xcp is None and price_in_xcp: _24h_vol_in_xcp = blockchain.round_out(e['vol_quote'] * price_in_xcp) if _24h_vol_in_btc is None and price_in_btc: _24h_vol_in_btc = blockchain.round_out(e['vol_quote'] * price_in_btc) pair_data[pair]['24h_vol_in_{}'.format( config.XCP.lower())] = _24h_vol_in_xcp #might still be None pair_data[pair]['24h_vol_in_{}'.format( config.BTC.lower())] = _24h_vol_in_btc #might still be None #get % change stats -- start by getting the first trade directly before the 24h period starts prev_trade = mongo_db.trades.find({ "base_asset": base_asset, "quote_asset": quote_asset, "block_time": { '$lt': start_dt } }).sort('block_time', pymongo.DESCENDING).limit(1) latest_trade = mongo_db.trades.find({ "base_asset": base_asset, "quote_asset": quote_asset }).sort('block_time', pymongo.DESCENDING).limit(1) if not prev_trade.count(): #no previous trade before this 24hr period pair_data[pair]['24h_pct_change'] = None else: prev_trade = prev_trade[0] latest_trade = latest_trade[0] prev_trade_price = get_price( prev_trade['base_quantity_normalized'], prev_trade['quote_quantity_normalized']) latest_trade_price = get_price( latest_trade['base_quantity_normalized'], latest_trade['quote_quantity_normalized']) pair_data[pair]['24h_pct_change'] = ( (latest_trade_price - prev_trade_price) / prev_trade_price) * 100 pair_data[pair]['last_updated'] = end_dt #print "PRODUCED", pair, pair_data[pair] mongo_db.asset_pair_market_info.update( { 'base_asset': base_asset, 'quote_asset': quote_asset }, {"$set": pair_data[pair]}, upsert=True) #remove any old pairs that were not just updated mongo_db.asset_pair_market_info.remove({'last_updated': {'$lt': end_dt}}) logger.info("Recomposed 24h trade statistics for %i asset pairs: %s" % (len(pair_data), ', '.join(pair_data.keys())))
def compile_asset_pair_market_info(): """Compiles the pair-level statistics that show on the View Prices page of counterwallet, for instance""" #loop through all open orders, and compile a listing of pairs, with a count of open orders for each pair end_dt = datetime.datetime.utcnow() start_dt = end_dt - datetime.timedelta(days=1) start_block_index, end_block_index = database.get_block_indexes_for_dates(start_dt=start_dt, end_dt=end_dt) open_orders = util.call_jsonrpc_api("get_orders", { 'filters': [ {'field': 'give_remaining', 'op': '>', 'value': 0}, {'field': 'get_remaining', 'op': '>', 'value': 0}, {'field': 'fee_required_remaining', 'op': '>=', 'value': 0}, {'field': 'fee_provided_remaining', 'op': '>=', 'value': 0}, ], 'status': 'open', 'show_expired': False, }, abort_on_error=True)['result'] pair_data = {} asset_info = {} def get_price(base_quantity_normalized, quote_quantity_normalized): return float(D(quote_quantity_normalized / base_quantity_normalized )) #COMPOSE order depth, lowest ask, and highest bid column data for o in open_orders: (base_asset, quote_asset) = util.assets_to_asset_pair(o['give_asset'], o['get_asset']) pair = '%s/%s' % (base_asset, quote_asset) base_asset_info = asset_info.get(base_asset, config.mongo_db.tracked_assets.find_one({ 'asset': base_asset })) if base_asset not in asset_info: asset_info[base_asset] = base_asset_info quote_asset_info = asset_info.get(quote_asset, config.mongo_db.tracked_assets.find_one({ 'asset': quote_asset })) if quote_asset not in asset_info: asset_info[quote_asset] = quote_asset_info pair_data.setdefault(pair, {'open_orders_count': 0, 'lowest_ask': None, 'highest_bid': None, 'completed_trades_count': 0, 'vol_base': 0, 'vol_quote': 0}) #^ highest ask = open order selling base, highest bid = open order buying base #^ we also initialize completed_trades_count, vol_base, vol_quote because every pair inited here may # not have cooresponding data out of the trades_data_by_pair aggregation below pair_data[pair]['open_orders_count'] += 1 base_quantity_normalized = blockchain.normalize_quantity(o['give_quantity'] if base_asset == o['give_asset'] else o['get_quantity'], base_asset_info['divisible']) quote_quantity_normalized = blockchain.normalize_quantity(o['give_quantity'] if quote_asset == o['give_asset'] else o['get_quantity'], quote_asset_info['divisible']) order_price = get_price(base_quantity_normalized, quote_quantity_normalized) if base_asset == o['give_asset']: #selling base if pair_data[pair]['lowest_ask'] is None or order_price < pair_data[pair]['lowest_ask']: pair_data[pair]['lowest_ask'] = order_price elif base_asset == o['get_asset']: #buying base if pair_data[pair]['highest_bid'] is None or order_price > pair_data[pair]['highest_bid']: pair_data[pair]['highest_bid'] = order_price #COMPOSE volume data (in XCP and BTC), and % change data #loop through all trade volume over the past 24h, and match that to the open orders trades_data_by_pair = config.mongo_db.trades.aggregate([ {"$match": { "block_time": {"$gte": start_dt, "$lte": end_dt } } }, {"$project": { "base_asset": 1, "quote_asset": 1, "base_quantity_normalized": 1, #to derive base volume "quote_quantity_normalized": 1 #to derive quote volume }}, {"$group": { "_id": {"base_asset": "$base_asset", "quote_asset": "$quote_asset"}, "vol_base": {"$sum": "$base_quantity_normalized"}, "vol_quote": {"$sum": "$quote_quantity_normalized"}, "count": {"$sum": 1}, }} ]) for e in trades_data_by_pair: pair = '%s/%s' % (e['_id']['base_asset'], e['_id']['quote_asset']) pair_data.setdefault(pair, {'open_orders_count': 0, 'lowest_ask': None, 'highest_bid': None}) #^ initialize an empty pair in the event there are no open orders for that pair, but there ARE completed trades for it pair_data[pair]['completed_trades_count'] = e['count'] pair_data[pair]['vol_base'] = e['vol_base'] pair_data[pair]['vol_quote'] = e['vol_quote'] #compose price data, relative to BTC and XCP mps_xcp_btc, xcp_btc_price, btc_xcp_price = get_price_primatives() for pair, e in pair_data.iteritems(): base_asset, quote_asset = pair.split('/') _24h_vol_in_btc = None _24h_vol_in_xcp = None #derive asset price data, expressed in BTC and XCP, for the given volumes if base_asset == config.XCP: _24h_vol_in_xcp = e['vol_base'] _24h_vol_in_btc = blockchain.round_out(e['vol_base'] * xcp_btc_price) if xcp_btc_price else 0 elif base_asset == config.BTC: _24h_vol_in_xcp = blockchain.round_out(e['vol_base'] * btc_xcp_price) if btc_xcp_price else 0 _24h_vol_in_btc = e['vol_base'] else: #base is not XCP or BTC price_summary_in_xcp, price_summary_in_btc, price_in_xcp, price_in_btc, aggregated_price_in_xcp, aggregated_price_in_btc = \ get_xcp_btc_price_info(base_asset, mps_xcp_btc, xcp_btc_price, btc_xcp_price, with_last_trades=0, start_dt=start_dt, end_dt=end_dt) if price_in_xcp: _24h_vol_in_xcp = blockchain.round_out(e['vol_base'] * price_in_xcp) if price_in_btc: _24h_vol_in_btc = blockchain.round_out(e['vol_base'] * price_in_btc) if _24h_vol_in_xcp is None or _24h_vol_in_btc is None: #the base asset didn't have price data against BTC or XCP, or both...try against the quote asset instead price_summary_in_xcp, price_summary_in_btc, price_in_xcp, price_in_btc, aggregated_price_in_xcp, aggregated_price_in_btc = \ get_xcp_btc_price_info(quote_asset, mps_xcp_btc, xcp_btc_price, btc_xcp_price, with_last_trades=0, start_dt=start_dt, end_dt=end_dt) if _24h_vol_in_xcp is None and price_in_xcp: _24h_vol_in_xcp = blockchain.round_out(e['vol_quote'] * price_in_xcp) if _24h_vol_in_btc is None and price_in_btc: _24h_vol_in_btc = blockchain.round_out(e['vol_quote'] * price_in_btc) pair_data[pair]['24h_vol_in_{}'.format(config.XCP.lower())] = _24h_vol_in_xcp #might still be None pair_data[pair]['24h_vol_in_{}'.format(config.BTC.lower())] = _24h_vol_in_btc #might still be None #get % change stats -- start by getting the first trade directly before the 24h period starts prev_trade = config.mongo_db.trades.find({ "base_asset": base_asset, "quote_asset": quote_asset, "block_time": {'$lt': start_dt}}).sort('block_time', pymongo.DESCENDING).limit(1) latest_trade = config.mongo_db.trades.find({ "base_asset": base_asset, "quote_asset": quote_asset}).sort('block_time', pymongo.DESCENDING).limit(1) if not prev_trade.count(): #no previous trade before this 24hr period pair_data[pair]['24h_pct_change'] = None else: prev_trade = prev_trade[0] latest_trade = latest_trade[0] prev_trade_price = get_price(prev_trade['base_quantity_normalized'], prev_trade['quote_quantity_normalized']) latest_trade_price = get_price(latest_trade['base_quantity_normalized'], latest_trade['quote_quantity_normalized']) pair_data[pair]['24h_pct_change'] = ((latest_trade_price - prev_trade_price) / prev_trade_price) * 100 pair_data[pair]['last_updated'] = end_dt #print "PRODUCED", pair, pair_data[pair] config.mongo_db.asset_pair_market_info.update( {'base_asset': base_asset, 'quote_asset': quote_asset}, {"$set": pair_data[pair]}, upsert=True) #remove any old pairs that were not just updated config.mongo_db.asset_pair_market_info.remove({'last_updated': {'$lt': end_dt}}) logger.info("Recomposed 24h trade statistics for %i asset pairs: %s" % (len(pair_data), ', '.join(pair_data.keys())))
def get_market_trades(asset1, asset2, addresses=[], limit=50, supplies=None): limit = min(limit, 100) base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) if not supplies: supplies = get_assets_supply([asset1, asset2]) market_trades = [] sources = '' bindings = ['expired'] if len(addresses) > 0: placeholder = ','.join(['?' for e in range(0, len(addresses))]) sources = '''AND (tx0_address IN ({}) OR tx1_address IN ({}))'''.format(placeholder, placeholder) bindings += addresses + addresses sql = '''SELECT order_matches.*, blocks.block_time FROM order_matches INNER JOIN blocks ON order_matches.block_index=blocks.block_index WHERE status != ? {} AND forward_asset IN (?, ?) AND backward_asset IN (?, ?) ORDER BY block_index DESC LIMIT ?'''.format(sources) bindings += [asset1, asset2, asset1, asset2, limit] order_matches = util.call_jsonrpc_api('sql', {'query': sql, 'bindings': bindings})['result'] for order_match in order_matches: if order_match['tx0_address'] in addresses: trade = {} trade['match_id'] = order_match['id'] trade['source'] = order_match['tx0_address'] trade['countersource'] = order_match['tx1_address'] trade['block_index'] = order_match['block_index'] trade['block_time'] = order_match['block_time'] trade['status'] = order_match['status'] if order_match['forward_asset'] == base_asset: trade['type'] = 'SELL' trade['price'] = calculate_price(order_match['forward_quantity'], order_match['backward_quantity'], supplies[order_match['forward_asset']][1], supplies[order_match['backward_asset']][1], 'SELL') trade['amount'] = order_match['forward_quantity'] trade['total'] = order_match['backward_quantity'] else: trade['type'] = 'BUY' trade['price'] = calculate_price(order_match['backward_quantity'], order_match['forward_quantity'], supplies[order_match['backward_asset']][1], supplies[order_match['forward_asset']][1], 'BUY') trade['amount'] = order_match['backward_quantity'] trade['total'] = order_match['forward_quantity'] market_trades.append(trade) if len(addresses) == 0 or order_match['tx1_address'] in addresses: trade = {} trade['match_id'] = order_match['id'] trade['source'] = order_match['tx1_address'] trade['countersource'] = order_match['tx0_address'] trade['block_index'] = order_match['block_index'] trade['block_time'] = order_match['block_time'] trade['status'] = order_match['status'] if order_match['backward_asset'] == base_asset: trade['type'] = 'SELL' trade['price'] = calculate_price(order_match['backward_quantity'], order_match['forward_quantity'], supplies[order_match['backward_asset']][1], supplies[order_match['forward_asset']][1], 'SELL') trade['amount'] = order_match['backward_quantity'] trade['total'] = order_match['forward_quantity'] else: trade['type'] = 'BUY' trade['price'] = calculate_price(order_match['forward_quantity'], order_match['backward_quantity'], supplies[order_match['forward_asset']][1], supplies[order_match['backward_asset']][1], 'BUY') trade['amount'] = order_match['forward_quantity'] trade['total'] = order_match['backward_quantity'] market_trades.append(trade) return market_trades
def get_market_orders(asset1, asset2, addresses=[], supplies=None, min_fee_provided=0.95, max_fee_required=0.95): base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) if not supplies: supplies = get_assets_supply([asset1, asset2]) market_orders = [] buy_orders = [] sell_orders = [] sql = '''SELECT orders.*, blocks.block_time FROM orders INNER JOIN blocks ON orders.block_index=blocks.block_index WHERE status = ? ''' bindings = ['open'] if len(addresses) > 0: sql += '''AND source IN ({}) '''.format(','.join(['?' for e in range(0, len(addresses))])) bindings += addresses sql += '''AND give_remaining > 0 AND give_asset IN (?, ?) AND get_asset IN (?, ?) ORDER BY tx_index DESC''' bindings += [asset1, asset2, asset1, asset2] orders = util.call_jsonrpc_api('sql', {'query': sql, 'bindings': bindings})['result'] for order in orders: market_order = {} exclude = False if order['give_asset'] == 'BTC': try: fee_provided = order['fee_provided'] / (order['give_quantity'] / 100) market_order['fee_provided'] = format(D(order['fee_provided']) / (D(order['give_quantity']) / D(100)), '.2f') except Exception as e: fee_provided = min_fee_provided - 1 # exclude exclude = fee_provided < min_fee_provided elif order['get_asset'] == 'BTC': try: fee_required = order['fee_required'] / (order['get_quantity'] / 100) market_order['fee_required'] = format(D(order['fee_required']) / (D(order['get_quantity']) / D(100)), '.2f') except Exception as e: fee_required = max_fee_required + 1 # exclude exclude = fee_required > max_fee_required if not exclude: if order['give_asset'] == base_asset: try: price = calculate_price(order['give_quantity'], order['get_quantity'], supplies[order['give_asset']][1], supplies[order['get_asset']][1], 'SELL') except: continue market_order['type'] = 'SELL' market_order['amount'] = order['give_remaining'] market_order['total'] = D(order['give_remaining']) * D(price) if not supplies[order['give_asset']][1] and supplies[order['get_asset']][1]: market_order['total'] = int(market_order['total'] * config.UNIT) elif supplies[order['give_asset']][1] and not supplies[order['get_asset']][1]: market_order['total'] = int(market_order['total'] / config.UNIT) else: market_order['total'] = int(market_order['total']) else: try: price = calculate_price(order['get_quantity'], order['give_quantity'], supplies[order['get_asset']][1], supplies[order['give_asset']][1], 'BUY') except: continue market_order['type'] = 'BUY' market_order['total'] = order['give_remaining'] market_order['amount'] = D(order['give_remaining']) / D(price) if supplies[order['give_asset']][1] and not supplies[order['get_asset']][1]: market_order['amount'] = int(market_order['amount'] / config.UNIT) elif not supplies[order['give_asset']][1] and supplies[order['get_asset']][1]: market_order['amount'] = int(market_order['amount'] * config.UNIT) else: market_order['amount'] = int(market_order['amount']) market_order['price'] = price if len(addresses) > 0: completed = format(((D(order['give_quantity']) - D(order['give_remaining'])) / D(order['give_quantity'])) * D(100), '.2f') market_order['completion'] = "{}%".format(completed) market_order['tx_index'] = order['tx_index'] market_order['tx_hash'] = order['tx_hash'] market_order['source'] = order['source'] market_order['block_index'] = order['block_index'] market_order['block_time'] = order['block_time'] market_orders.append(market_order) else: if market_order['type'] == 'SELL': sell_orders.append(market_order) else: buy_orders.append(market_order) if len(addresses) == 0: market_orders = merge_same_price_orders(sell_orders) + merge_same_price_orders(buy_orders) return market_orders
def get_market_trades(asset1, asset2, addresses=[], limit=50, supplies=None): limit = min(limit, 100) base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) if not supplies: supplies = get_assets_supply([asset1, asset2]) market_trades = [] sources = '' bindings = ['expired'] if len(addresses) > 0: placeholder = ','.join(['?' for e in range(0,len(addresses))]) sources = '''AND (tx0_address IN ({}) OR tx1_address IN ({}))'''.format(placeholder, placeholder) bindings += addresses + addresses sql = '''SELECT order_matches.*, blocks.block_time FROM order_matches INNER JOIN blocks ON order_matches.block_index=blocks.block_index WHERE status != ? {} AND forward_asset IN (?, ?) AND backward_asset IN (?, ?) ORDER BY block_index DESC LIMIT ?'''.format(sources) bindings += [asset1, asset2, asset1, asset2, limit] order_matches = util.call_jsonrpc_api('sql', {'query': sql, 'bindings': bindings})['result'] for order_match in order_matches: if order_match['tx0_address'] in addresses: trade = {} trade['match_id'] = order_match['id'] trade['source'] = order_match['tx0_address'] trade['countersource'] = order_match['tx1_address'] trade['block_index'] = order_match['block_index'] trade['block_time'] = order_match['block_time'] trade['status'] = order_match['status'] if order_match['forward_asset'] == base_asset: trade['type'] = 'SELL' trade['price'] = calculate_price(order_match['forward_quantity'], order_match['backward_quantity'], supplies[order_match['forward_asset']][1], supplies[order_match['backward_asset']][1], 'SELL') trade['amount'] = order_match['forward_quantity'] trade['total'] = order_match['backward_quantity'] else: trade['type'] = 'BUY' trade['price'] = calculate_price(order_match['backward_quantity'], order_match['forward_quantity'], supplies[order_match['backward_asset']][1], supplies[order_match['forward_asset']][1], 'BUY') trade['amount'] = order_match['backward_quantity'] trade['total'] = order_match['forward_quantity'] market_trades.append(trade) if len(addresses)==0 or order_match['tx1_address'] in addresses: trade = {} trade['match_id'] = order_match['id'] trade['source'] = order_match['tx1_address'] trade['countersource'] = order_match['tx0_address'] trade['block_index'] = order_match['block_index'] trade['block_time'] = order_match['block_time'] trade['status'] = order_match['status'] if order_match['backward_asset'] == base_asset: trade['type'] = 'SELL' trade['price'] = calculate_price(order_match['backward_quantity'], order_match['forward_quantity'], supplies[order_match['backward_asset']][1], supplies[order_match['forward_asset']][1], 'SELL') trade['amount'] = order_match['backward_quantity'] trade['total'] = order_match['forward_quantity'] else: trade['type'] = 'BUY' trade['price'] = calculate_price(order_match['forward_quantity'], order_match['backward_quantity'], supplies[order_match['forward_asset']][1], supplies[order_match['backward_asset']][1], 'BUY') trade['amount'] = order_match['forward_quantity'] trade['total'] = order_match['backward_quantity'] market_trades.append(trade) return market_trades
def get_market_price_history(asset1, asset2, start_ts=None, end_ts=None, as_dict=False): """Return block-by-block aggregated market history data for the specified asset pair, within the specified date range. @returns List of lists (or list of dicts, if as_dict is specified). * If as_dict is False, each embedded list has 8 elements [block time (epoch in MS), open, high, low, close, volume, # trades in block, block index] * If as_dict is True, each dict in the list has the keys: block_time (epoch in MS), block_index, open, high, low, close, vol, count Aggregate on an an hourly basis """ now_ts = time.mktime(datetime.datetime.utcnow().timetuple()) if not end_ts: #default to current datetime end_ts = now_ts if not start_ts: #default to 180 days before the end date start_ts = end_ts - (180 * 24 * 60 * 60) base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) #get ticks -- open, high, low, close, volume result = config.mongo_db.trades.aggregate([ {"$match": { "base_asset": base_asset, "quote_asset": quote_asset, "block_time": { "$gte": datetime.datetime.utcfromtimestamp(start_ts) } if end_ts == now_ts else { "$gte": datetime.datetime.utcfromtimestamp(start_ts), "$lte": datetime.datetime.utcfromtimestamp(end_ts) } }}, {"$project": { "year": {"$year": "$block_time"}, "month": {"$month": "$block_time"}, "day": {"$dayOfMonth": "$block_time"}, "hour": {"$hour": "$block_time"}, "block_index": 1, "unit_price": 1, "base_quantity_normalized": 1 #to derive volume }}, {"$group": { "_id": {"year": "$year", "month": "$month", "day": "$day", "hour": "$hour"}, "open": {"$first": "$unit_price"}, "high": {"$max": "$unit_price"}, "low": {"$min": "$unit_price"}, "close": {"$last": "$unit_price"}, "vol": {"$sum": "$base_quantity_normalized"}, "count": {"$sum": 1}, }}, {"$sort": SON([("_id.year", pymongo.ASCENDING), ("_id.month", pymongo.ASCENDING), ("_id.day", pymongo.ASCENDING), ("_id.hour", pymongo.ASCENDING)])}, ]) result = list(result) if not len(result): return False midline = [((r['high'] + r['low']) / 2.0) for r in result] if as_dict: for i in xrange(len(result)): result[i]['interval_time'] = int(time.mktime(datetime.datetime( result[i]['_id']['year'], result[i]['_id']['month'], result[i]['_id']['day'], result[i]['_id']['hour']).timetuple()) * 1000) result[i]['midline'] = midline[i] del result[i]['_id'] return result else: list_result = [] for i in xrange(len(result)): list_result.append([ int(time.mktime(datetime.datetime( result[i]['_id']['year'], result[i]['_id']['month'], result[i]['_id']['day'], result[i]['_id']['hour']).timetuple()) * 1000), result[i]['open'], result[i]['high'], result[i]['low'], result[i]['close'], result[i]['vol'], result[i]['count'], midline[i] ]) return list_result
def get_market_orders(asset1, asset2, addresses=[], supplies=None, min_fee_provided=0.95, max_fee_required=0.95): base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) if not supplies: supplies = get_assets_supply([asset1, asset2]) market_orders = [] buy_orders = [] sell_orders = [] sql = '''SELECT orders.*, blocks.block_time FROM orders INNER JOIN blocks ON orders.block_index=blocks.block_index WHERE status = ? ''' bindings = ['open'] if len(addresses) > 0: sql += '''AND source IN ({}) '''.format(','.join(['?' for e in range(0, len(addresses))])) bindings += addresses sql += '''AND give_remaining > 0 AND give_asset IN (?, ?) AND get_asset IN (?, ?) ORDER BY tx_index DESC''' bindings += [asset1, asset2, asset1, asset2] orders = util.call_jsonrpc_api('sql', {'query': sql, 'bindings': bindings})['result'] for order in orders: market_order = {} exclude = False if order['give_asset'] == config.BTC: try: fee_provided = order['fee_provided'] / (order['give_quantity'] / 100) market_order['fee_provided'] = format(D(order['fee_provided']) / (D(order['give_quantity']) / D(100)), '.2f') except Exception as e: fee_provided = min_fee_provided - 1 # exclude exclude = fee_provided < min_fee_provided elif order['get_asset'] == config.BTC: try: fee_required = order['fee_required'] / (order['get_quantity'] / 100) market_order['fee_required'] = format(D(order['fee_required']) / (D(order['get_quantity']) / D(100)), '.2f') except Exception as e: fee_required = max_fee_required + 1 # exclude exclude = fee_required > max_fee_required if not exclude: if order['give_asset'] == base_asset: try: price = calculate_price(order['give_quantity'], order['get_quantity'], supplies[order['give_asset']][1], supplies[order['get_asset']][1], 'SELL') except: continue market_order['type'] = 'SELL' market_order['amount'] = order['give_remaining'] market_order['total'] = D(order['give_remaining']) * D(price) if not supplies[order['give_asset']][1] and supplies[order['get_asset']][1]: market_order['total'] = int(market_order['total'] * config.UNIT) elif supplies[order['give_asset']][1] and not supplies[order['get_asset']][1]: market_order['total'] = int(market_order['total'] / config.UNIT) else: market_order['total'] = int(market_order['total']) else: try: price = calculate_price(order['get_quantity'], order['give_quantity'], supplies[order['get_asset']][1], supplies[order['give_asset']][1], 'BUY') except: continue market_order['type'] = 'BUY' market_order['total'] = order['give_remaining'] market_order['amount'] = D(order['give_remaining']) / D(price) if supplies[order['give_asset']][1] and not supplies[order['get_asset']][1]: market_order['amount'] = int(market_order['amount'] / config.UNIT) elif not supplies[order['give_asset']][1] and supplies[order['get_asset']][1]: market_order['amount'] = int(market_order['amount'] * config.UNIT) else: market_order['amount'] = int(market_order['amount']) market_order['price'] = price if len(addresses) > 0: completed = format(((D(order['give_quantity']) - D(order['give_remaining'])) / D(order['give_quantity'])) * D(100), '.2f') market_order['completion'] = "{}%".format(completed) market_order['tx_index'] = order['tx_index'] market_order['tx_hash'] = order['tx_hash'] market_order['source'] = order['source'] market_order['block_index'] = order['block_index'] market_order['block_time'] = order['block_time'] market_orders.append(market_order) else: if market_order['type'] == 'SELL': sell_orders.append(market_order) else: buy_orders.append(market_order) if len(addresses) == 0: market_orders = merge_same_price_orders(sell_orders) + merge_same_price_orders(buy_orders) return market_orders
def parse_trade_book(msg, msg_data): # book trades if (msg['category'] == 'order_matches' and ((msg['command'] == 'update' and msg_data['status'] == 'completed') or # for a trade with BTC involved, but that is settled (completed) ('forward_asset' in msg_data and msg_data['forward_asset'] != config.BTC and msg_data['backward_asset'] != config.BTC)) ): # or for a trade without BTC on either end if msg['command'] == 'update' and msg_data['status'] == 'completed': # an order is being updated to a completed status (i.e. a BTCpay has completed) tx0_hash, tx1_hash = msg_data['order_match_id'][:64], msg_data[ 'order_match_id'][65:] # get the order_match this btcpay settles order_match = util.jsonrpc_api("get_order_matches", { 'filters': [{ 'field': 'tx0_hash', 'op': '==', 'value': tx0_hash }, { 'field': 'tx1_hash', 'op': '==', 'value': tx1_hash }] }, abort_on_error=False)['result'][0] else: assert msg_data[ 'status'] == 'completed' # should not enter a pending state for non BTC matches order_match = msg_data forward_asset_info = config.mongo_db.tracked_assets.find_one( {'asset': order_match['forward_asset']}) backward_asset_info = config.mongo_db.tracked_assets.find_one( {'asset': order_match['backward_asset']}) assert forward_asset_info and backward_asset_info base_asset, quote_asset = util.assets_to_asset_pair( order_match['forward_asset'], order_match['backward_asset']) # don't create trade records from order matches with BTC that are under the dust limit if ((order_match['forward_asset'] == config.BTC and order_match['forward_quantity'] <= config.ORDER_BTC_DUST_LIMIT_CUTOFF) or (order_match['backward_asset'] == config.BTC and order_match['backward_quantity'] <= config.ORDER_BTC_DUST_LIMIT_CUTOFF)): logger.debug("Order match %s ignored due to %s under dust limit." % (order_match['tx0_hash'] + order_match['tx1_hash'], config.BTC)) return 'ABORT_THIS_MESSAGE_PROCESSING' # take divisible trade quantities to floating point forward_quantity = blockchain.normalize_quantity( order_match['forward_quantity'], forward_asset_info['divisible']) backward_quantity = blockchain.normalize_quantity( order_match['backward_quantity'], backward_asset_info['divisible']) # compose trade trade = { 'block_index': config.state['cur_block']['block_index'], 'block_time': config.state['cur_block']['block_time_obj'], 'message_index': msg['message_index'], # secondary temporaral ordering off of when 'order_match_id': order_match['tx0_hash'] + '_' + order_match['tx1_hash'], 'order_match_tx0_index': order_match['tx0_index'], 'order_match_tx1_index': order_match['tx1_index'], 'order_match_tx0_address': order_match['tx0_address'], 'order_match_tx1_address': order_match['tx1_address'], 'base_asset': base_asset, 'quote_asset': quote_asset, 'base_quantity': order_match['forward_quantity'] if order_match['forward_asset'] == base_asset else order_match['backward_quantity'], 'quote_quantity': order_match['backward_quantity'] if order_match['forward_asset'] == base_asset else order_match['forward_quantity'], 'base_quantity_normalized': forward_quantity if order_match['forward_asset'] == base_asset else backward_quantity, 'quote_quantity_normalized': backward_quantity if order_match['forward_asset'] == base_asset else forward_quantity, } d = D(trade['quote_quantity_normalized']) / D( trade['base_quantity_normalized']) d = d.quantize(EIGHT_PLACES, rounding=decimal.ROUND_HALF_EVEN, context=decimal.Context(prec=30)) trade['unit_price'] = float(d) d = D(trade['base_quantity_normalized']) / D( trade['quote_quantity_normalized']) d = d.quantize(EIGHT_PLACES, rounding=decimal.ROUND_HALF_EVEN, context=decimal.Context(prec=30)) trade['unit_price_inverse'] = float(d) config.mongo_db.trades.insert(trade) logger.info("Procesed Trade from tx %s :: %s" % (msg['message_index'], trade))
def get_market_price_history(asset1, asset2, start_ts=None, end_ts=None, as_dict=False): """Return block-by-block aggregated market history data for the specified asset pair, within the specified date range. @returns List of lists (or list of dicts, if as_dict is specified). * If as_dict is False, each embedded list has 8 elements [block time (epoch in MS), open, high, low, close, volume, # trades in block, block index] * If as_dict is True, each dict in the list has the keys: block_time (epoch in MS), block_index, open, high, low, close, vol, count Aggregate on an an hourly basis """ now_ts = calendar.timegm(time.gmtime()) if not end_ts: # default to current datetime end_ts = now_ts if not start_ts: # default to 180 days before the end date start_ts = end_ts - (180 * 24 * 60 * 60) base_asset, quote_asset = util.assets_to_asset_pair(asset1, asset2) # get ticks -- open, high, low, close, volume result = config.mongo_db.trades.aggregate([ { "$match": { "base_asset": base_asset, "quote_asset": quote_asset, "block_time": { "$gte": datetime.datetime.utcfromtimestamp(start_ts) } if end_ts == now_ts else { "$gte": datetime.datetime.utcfromtimestamp(start_ts), "$lte": datetime.datetime.utcfromtimestamp(end_ts) } } }, { "$project": { "year": { "$year": "$block_time" }, "month": { "$month": "$block_time" }, "day": { "$dayOfMonth": "$block_time" }, "hour": { "$hour": "$block_time" }, "block_index": 1, "unit_price": 1, "base_quantity_normalized": 1 # to derive volume } }, { "$group": { "_id": { "year": "$year", "month": "$month", "day": "$day", "hour": "$hour" }, "open": { "$first": "$unit_price" }, "high": { "$max": "$unit_price" }, "low": { "$min": "$unit_price" }, "close": { "$last": "$unit_price" }, "vol": { "$sum": "$base_quantity_normalized" }, "count": { "$sum": 1 }, } }, { "$sort": SON([("_id.year", pymongo.ASCENDING), ("_id.month", pymongo.ASCENDING), ("_id.day", pymongo.ASCENDING), ("_id.hour", pymongo.ASCENDING)]) }, ]) result = list(result) if not len(result): return False midline = [((r['high'] + r['low']) / 2.0) for r in result] if as_dict: for i in range(len(result)): result[i]['interval_time'] = int( calendar.timegm( datetime.datetime( result[i]['_id']['year'], result[i]['_id']['month'], result[i]['_id']['day'], result[i]['_id']['hour']).timetuple()) * 1000) result[i]['midline'] = midline[i] del result[i]['_id'] return result else: list_result = [] for i in range(len(result)): list_result.append([ int( calendar.timegm( datetime.datetime( result[i]['_id']['year'], result[i]['_id']['month'], result[i]['_id']['day'], result[i]['_id']['hour']).timetuple()) * 1000), result[i]['open'], result[i]['high'], result[i]['low'], result[i]['close'], result[i]['vol'], result[i]['count'], midline[i] ]) return list_result