def get_assets_supply(assets=[]): supplies = {} if 'ASP' in assets: supplies['ASP'] = (util.call_jsonrpc_api("get_supply", {'asset': 'ASP'})['result'], True) assets.remove('ASP') if 'GASP' in assets: supplies['GASP'] = (0, True) assets.remove('GASP') if len(assets) > 0: sql = '''SELECT asset, SUM(quantity) AS supply, divisible FROM issuances WHERE asset IN ({}) AND status = ? GROUP BY asset ORDER BY asset'''.format(','.join( ['?' for e in range(0, len(assets))])) bindings = assets + ['valid'] issuances = util.call_jsonrpc_api('sql', { 'query': sql, 'bindings': bindings })['result'] for issuance in issuances: supplies[issuance['asset']] = (issuance['supply'], issuance['divisible']) return supplies
def gettransaction_batch(txhash_list): raw_txes = util.call_jsonrpc_api("getrawtransaction_batch", { 'txhash_list': txhash_list, 'verbose': True, 'skip_missing': True }, abort_on_error=True)['result'] txes = {} for tx_hash, tx in raw_txes.items(): if tx is None: txes[tx_hash] = None continue valueOut = 0 for vout in tx['vout']: valueOut += vout['value'] txes[tx_hash] = { 'txid': tx_hash, 'version': tx['version'], 'locktime': tx['locktime'], 'confirmations': tx['confirmations'] if 'confirmations' in tx else 0, 'blocktime': tx['blocktime'] if 'blocktime' in tx else 0, 'blockhash': tx['blockhash'] if 'blockhash' in tx else 0, 'time': tx['time'] if 'time' in tx else 0, 'valueOut': valueOut, 'vin': tx['vin'], 'vout': tx['vout'] } return txes
def get_asset_info(asset, at_dt=None): asset_info = config.mongo_db.tracked_assets.find_one({'asset': asset}) if asset not in (config.XCP, config.BTC) and at_dt and asset_info['_at_block_time'] > at_dt: # get the asset info at or before the given at_dt datetime for e in reversed(asset_info['_history']): # newest to oldest if e['_at_block_time'] <= at_dt: asset_info = e break else: # asset was created AFTER at_dt asset_info = None if asset_info is None: return None assert asset_info['_at_block_time'] <= at_dt # modify some of the properties of the returned asset_info for BTC and XCP if asset == config.BTC: if at_dt: start_block_index, end_block_index = database.get_block_indexes_for_dates(end_dt=at_dt) asset_info['total_issued'] = blockchain.get_gasp_supply(normalize=False, at_block_index=end_block_index) asset_info['total_issued_normalized'] = blockchain.normalize_quantity(asset_info['total_issued']) else: asset_info['total_issued'] = blockchain.get_gasp_supply(normalize=False) asset_info['total_issued_normalized'] = blockchain.normalize_quantity(asset_info['total_issued']) elif asset == config.XCP: # BUG: this does not take end_dt (if specified) into account. however, the deviation won't be too big # as XCP doesn't deflate quickly at all, and shouldn't matter that much since there weren't any/much trades # before the end of the burn period (which is what is involved with how we use at_dt with currently) asset_info['total_issued'] = util.call_jsonrpc_api("get_supply", {'asset': 'ASP'}, abort_on_error=True)['result'] asset_info['total_issued_normalized'] = blockchain.normalize_quantity(asset_info['total_issued']) if not asset_info: raise Exception("Invalid asset: %s" % asset) return asset_info
def parse_base64_feed(base64_feed): decoded_feed = base64.b64decode(base64_feed) feed = json.loads(decoded_feed) if not isinstance(feed, dict) or 'feed' not in feed: return False errors = util.is_valid_json(feed['feed'], config.FEED_SCHEMA) if len(errors) > 0: raise Exception("Invalid json: {}".format(", ".join(errors))) # get broadcast infos params = { 'filters': { 'field': 'source', 'op': '=', 'value': feed['feed']['address'] }, 'order_by': 'tx_index', 'order_dir': 'DESC', 'limit': 1 } broadcasts = util.call_jsonrpc_api('get_broadcasts', params)['result'] if len(broadcasts) == 0: raise Exception("invalid feed address") complete_feed = {} complete_feed['fee_fraction_int'] = broadcasts[0]['fee_fraction_int'] complete_feed['source'] = broadcasts[0]['source'] complete_feed['locked'] = broadcasts[0]['locked'] complete_feed['counters'] = get_feed_counters(broadcasts[0]['source']) complete_feed['info_data'] = sanitize_json_data(feed['feed']) feed['feed'] = complete_feed return feed
def get_feed_counters(feed_address): counters = {} sql = 'SELECT COUNT(*) AS bet_count, SUM(wager_quantity) AS wager_quantity, SUM(wager_remaining) AS wager_remaining, status FROM bets ' sql += 'WHERE feed_address=? GROUP BY status ORDER BY status DESC' bindings = [feed_address] params = {'query': sql, 'bindings': bindings} counters['bets'] = util.call_jsonrpc_api('sql', params)['result'] return counters
def get_messagefeed_messages_by_index(message_indexes): msgs = util.call_jsonrpc_api("get_messages_by_index", {'message_indexes': message_indexes}, abort_on_error=True)['result'] events = [] for m in msgs: events.append(messages.decorate_message_for_feed(m)) return events
def get_assets_info(assetsList): assets = assetsList # TODO: change the parameter name at some point in the future...shouldn't be using camel case here if not isinstance(assets, list): raise Exception( "assets must be a list of asset names, even if it just contains one entry" ) assets_info = [] for asset in assets: # BTC and XCP. if asset in [config.BTC, config.XCP]: if asset == config.BTC: supply = blockchain.get_btc_supply(normalize=False) asset_longname = config.BTC_NAME else: supply = util.call_jsonrpc_api("get_supply", {'asset': config.XCP}, abort_on_error=True)['result'] asset_longname = config.XCP_NAME assets_info.append({ 'asset': asset, 'asset_longname': asset_longname, 'owner': None, 'divisible': True, 'locked': False, 'supply': supply, 'description': '', 'issuer': None }) continue # User-created asset. tracked_asset = config.mongo_db.tracked_assets.find_one( {'$or': [{ 'asset': asset }, { 'asset_longname': asset }]}, { '_id': 0, '_history': 0 }) if not tracked_asset: continue # asset not found, most likely assets_info.append({ 'asset': tracked_asset['asset'], 'asset_longname': tracked_asset['asset_longname'], 'owner': tracked_asset['owner'], 'divisible': tracked_asset['divisible'], 'locked': tracked_asset['locked'], 'supply': tracked_asset['total_issued'], 'description': tracked_asset['description'], 'issuer': tracked_asset['owner'] }) return assets_info
def get_last_n_messages(count=100): if count > 1000: raise Exception("The count is too damn high") message_indexes = list( range( max(config.state['last_message_index'] - count, 0) + 1, config.state['last_message_index'] + 1)) msgs = util.call_jsonrpc_api("get_messages_by_index", {'message_indexes': message_indexes}, abort_on_error=True)['result'] for i in range(len(msgs)): msgs[i] = messages.decorate_message_for_feed(msgs[i]) return msgs
def get_block_info(block_index, prefetch=0, min_message_index=None): global block_info_cache if block_index in block_info_cache: return block_info_cache[block_index] block_info_cache.clear() blocks = util.call_jsonrpc_api( 'get_blocks', {'block_indexes': list(range(block_index, block_index + prefetch)), 'min_message_index': min_message_index}, abort_on_error=True, use_cache=False)['result'] for block in blocks: block_info_cache[block['block_index']] = block return block_info_cache[block_index]
def proxy_to_aspired(method='', params=[]): if method == 'sql': raise Exception("Invalid method") result = util.call_jsonrpc_api(method, params) if 'error' in result: if result['error'].get('data', None): errorMsg = result['error']['data'].get( 'message', result['error']['message']) if isinstance(errorMsg, bytes): errorMsg = str(errorMsg, 'utf-8') else: errorMsg = json.dumps(result['error']) raise Exception(errorMsg if errorMsg is not None else "UNKNOWN") return result['result']
def get_unspent_txouts(source, return_confirmed=False): """returns a list of unspent outputs for a specific address @return: A list of dicts, with each entry in the dict having the following keys: """ txouts = util.call_jsonrpc_api('get_unspent_txouts', { 'address': source, 'unconfirmed': True }, abort_on_error=True)['result'] if return_confirmed: return txouts, [ output for output in txouts if output['confirmations'] > 0 ] else: return txouts
def get_bets(bet_type, feed_address, deadline, target_value=None, leverage=5040): limit = 50 bindings = [] sql = 'SELECT * FROM bets WHERE counterwager_remaining>0 AND ' sql += 'bet_type=? AND feed_address=? AND leverage=? AND deadline=? ' bindings += [bet_type, feed_address, leverage, deadline] if target_value: sql += 'AND target_value=? ' bindings.append(target_value) sql += 'ORDER BY ((counterwager_quantity+0.0)/(wager_quantity+0.0)) ASC LIMIT ?' bindings.append(limit) params = {'query': sql, 'bindings': bindings} return util.call_jsonrpc_api('sql', params)['result']
def get_feed(address_or_url=''): conditions = { '$or': [{ 'source': address_or_url }, { 'info_url': address_or_url }], 'info_status': 'valid' } result = {} feeds = config.mongo_db.feeds.find(conditions, projection={'_id': False}, limit=1) for feed in feeds: if 'targets' not in feed['info_data'] or ('type' in feed['info_data'] and feed['info_data']['type'] in ['all', 'cfd']): feed['info_data']['next_broadcast'] = util.next_interval_date( feed['info_data']['broadcast_date']) feed['info_data']['next_deadline'] = util.next_interval_date( feed['info_data']['deadline']) result = feed result['counters'] = get_feed_counters(feed['source']) if 'counters' not in result: params = { 'filters': { 'field': 'source', 'op': '=', 'value': address_or_url }, 'order_by': 'tx_index', 'order_dir': 'DESC', 'limit': 10 } broadcasts = util.call_jsonrpc_api('get_broadcasts', params)['result'] if broadcasts: return { 'broadcasts': broadcasts, 'counters': get_feed_counters(address_or_url) } return result
def get_user_bets(addresses=[], status="open"): params = { 'filters': { 'field': 'source', 'op': 'IN', 'value': addresses }, 'status': status, 'order_by': 'tx_index', 'order_dir': 'DESC', 'limit': 100 } bets = util.call_jsonrpc_api('get_bets', params)['result'] sources = {} for bet in bets: sources[bet['feed_address']] = True return { 'bets': bets, 'feeds': get_feeds_by_source_addresses(list(sources.keys())) }
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'] == 'GASP/ASP': # ASP/GASP always in first pairs_with_orders.insert(0, top_pair) else: pairs_with_orders.append(top_pair) return pairs_with_orders
def search_raw_transactions(address, unconfirmed=True): return util.call_jsonrpc_api('search_raw_transactions', { 'address': address, 'unconfirmed': unconfirmed }, abort_on_error=True)['result']
def get_normalized_balances(addresses): """ This call augments aspire's get_balances with a normalized_quantity field. It also will include any owned assets for an address, even if their balance is zero. NOTE: Does not retrieve GASP balance. Use get_address_info for that. """ if not isinstance(addresses, list): raise Exception( "addresses must be a list of addresses, even if it just contains one address" ) if not len(addresses): raise Exception("Invalid address list supplied") filters = [] for address in addresses: filters.append({'field': 'address', 'op': '==', 'value': address}) mappings = {} result = util.call_jsonrpc_api("get_balances", { 'filters': filters, 'filterop': 'or' }, abort_on_error=True)['result'] isowner = {} owned_assets = config.mongo_db.tracked_assets.find( {'$or': [{ 'owner': a } for a in addresses]}, { '_history': 0, '_id': 0 }) for o in owned_assets: isowner[o['owner'] + o['asset']] = o data = [] for d in result: if not d['quantity'] and ((d['address'] + d['asset']) not in isowner): continue # don't include balances with a zero asset value asset_info = config.mongo_db.tracked_assets.find_one( {'asset': d['asset']}) divisible = True # ASP and GASP if asset_info and 'divisible' in asset_info: divisible = asset_info['divisible'] d['normalized_quantity'] = blockchain.normalize_quantity( d['quantity'], divisible) d['owner'] = (d['address'] + d['asset']) in isowner d['asset_longname'] = asset_info['asset_longname'] mappings[d['address'] + d['asset']] = d data.append(d) # include any owned assets for each address, even if their balance is zero for key in isowner: if key not in mappings: o = isowner[key] data.append({ 'address': o['owner'], 'asset': o['asset'], 'quantity': 0, 'normalized_quantity': 0, 'owner': True, }) return data
def handle_get(): if flask.request.headers.get("Content-Type", None) == 'application/csp-report': try: data_json = flask.request.get_data().decode('utf-8') data = json.loads(data_json) assert 'csp-report' in data except Exception as e: obj_error = jsonrpc.exceptions.JSONRPCInvalidRequest( data="Invalid JSON-RPC 2.0 request format") return flask.Response(obj_error.json.encode(), 200, mimetype='application/json') tx_logger.info("***CSP SECURITY --- %s" % data_json) return flask.Response('', 200) # "ping" aspired to test cp_s = time.time() cp_result_valid = True try: cp_status = util.call_jsonrpc_api("get_running_info", abort_on_error=True, use_cache=False)['result'] except: cp_result_valid = False cp_e = time.time() # "ping" aspireblockd to test, as well cb_s = time.time() cb_result_valid = True cb_result_error_code = None payload = { "id": 0, "jsonrpc": "2.0", "method": "is_ready", "params": [], } headers = {'content-type': 'application/json', 'Connection': 'close'} try: url = "http://127.0.0.1:%s/api/" % config.RPC_PORT r = grequests.map((grequests.post(url, data=json.dumps(payload), headers=headers), ))[0] if r is None: raise Exception("result is None") except Exception as e: cb_result_valid = False cb_result_error_code = "GOT EXCEPTION: %s" % e else: if r.status_code != 200: cb_result_valid = False cb_result_error_code = "GOT STATUS %s" % r.status_code if r else 'COULD NOT CONTACT' cb_result = r.json() if 'error' in cb_result: cb_result_valid = False cb_result_error_code = "GOT ERROR: %s" % cb_result['error'] cb_e = time.time() result = { 'aspire-server': 'OK' if cp_result_valid else 'NOT OK', 'aspire-server_ver': '%s.%s.%s' % (cp_status['version_major'], cp_status['version_minor'], cp_status['version_revision']) if cp_result_valid else '?', 'aspire-server_last_block': cp_status['last_block'] if cp_result_valid else '?', 'aspire-server_last_message_index': cp_status['last_message_index'] if cp_result_valid else '?', 'aspire-server_caught_up': config.state['cp_caught_up'], 'aspire-server_check_elapsed': cp_e - cp_s, 'aspireblock': 'OK' if cb_result_valid else 'NOT OK', 'aspireblock_ver': config.VERSION, 'aspireblock_check_elapsed': cb_e - cb_s, 'aspireblock_error': cb_result_error_code, 'aspireblock_last_message_index': config.state['last_message_index'], 'aspireblock_caught_up': blockfeed.fuzzy_is_caught_up(), 'aspireblock_cur_block': { 'block_hash': config.state['cur_block'].get('block_hash', '??'), 'block_index': config.state['cur_block'].get('block_index', '??') }, 'aspireblock_last_processed_block': { 'block_hash': config.state['my_latest_block'].get('block_hash', '??'), 'block_index': config.state['my_latest_block'].get('block_index', '??') }, } response_code = 200 # error if we couldn't make a successful call to aspire-server or aspireblock's API (500) if not cp_result_valid or not cb_result_valid: response_code = 500 result['ERROR'] = "aspire-server_api_contact_error" # error 510 if the aspire-server last block is more than 1 block behind backend elif not result['aspire-server_caught_up']: response_code = 510 result['ERROR'] = "aspire-server_not_caught_up" # error 511 if the aspireblock last block is more than 1 block behind aspire-server elif not result['aspireblock_caught_up']: response_code = 511 result['ERROR'] = "aspireblock_not_caught_up" else: result['ERROR'] = None response = flask.Response(json.dumps(result), response_code, mimetype='application/json') _set_cors_headers(response) return response
def get_escrowed_balances(addresses): addresses_holder = ','.join(['?' for e in range(0, len(addresses))]) sql = '''SELECT (source || '_' || give_asset) AS source_asset, source AS address, give_asset AS asset, SUM(give_remaining) AS quantity FROM orders WHERE source IN ({}) AND status = ? AND give_asset != ? GROUP BY source_asset'''.format(addresses_holder) bindings = addresses + ['open', config.BTC] results = util.call_jsonrpc_api("sql", { 'query': sql, 'bindings': bindings }, abort_on_error=True)['result'] sql = '''SELECT (tx0_address || '_' || forward_asset) AS source_asset, tx0_address AS address, forward_asset AS asset, SUM(forward_quantity) AS quantity FROM order_matches WHERE tx0_address IN ({}) AND forward_asset != ? AND status = ? GROUP BY source_asset'''.format(addresses_holder) bindings = addresses + [config.BTC, 'pending'] results += util.call_jsonrpc_api("sql", { 'query': sql, 'bindings': bindings }, abort_on_error=True)['result'] sql = '''SELECT (tx1_address || '_' || backward_asset) AS source_asset, tx1_address AS address, backward_asset AS asset, SUM(backward_quantity) AS quantity FROM order_matches WHERE tx1_address IN ({}) AND backward_asset != ? AND status = ? GROUP BY source_asset'''.format(addresses_holder) bindings = addresses + [config.BTC, 'pending'] results += util.call_jsonrpc_api("sql", { 'query': sql, 'bindings': bindings }, abort_on_error=True)['result'] sql = '''SELECT source AS address, '{}' AS asset, SUM(wager_remaining) AS quantity FROM bets WHERE source IN ({}) AND status = ? GROUP BY address'''.format(config.XCP, addresses_holder) bindings = addresses + ['open'] results += util.call_jsonrpc_api("sql", { 'query': sql, 'bindings': bindings }, abort_on_error=True)['result'] sql = '''SELECT tx0_address AS address, '{}' AS asset, SUM(forward_quantity) AS quantity FROM bet_matches WHERE tx0_address IN ({}) AND status = ? GROUP BY address'''.format(config.XCP, addresses_holder) bindings = addresses + ['pending'] results += util.call_jsonrpc_api("sql", { 'query': sql, 'bindings': bindings }, abort_on_error=True)['result'] sql = '''SELECT tx1_address AS address, '{}' AS asset, SUM(backward_quantity) AS quantity FROM bet_matches WHERE tx1_address IN ({}) AND status = ? GROUP BY address'''.format(config.XCP, addresses_holder) bindings = addresses + ['pending'] results += util.call_jsonrpc_api("sql", { 'query': sql, 'bindings': bindings }, abort_on_error=True)['result'] escrowed_balances = {} for order in results: if order['address'] not in escrowed_balances: escrowed_balances[order['address']] = {} if order['asset'] not in escrowed_balances[order['address']]: escrowed_balances[order['address']][order['asset']] = 0 escrowed_balances[order['address']][ order['asset']] += order['quantity'] return escrowed_balances
def compile_asset_pair_market_info(): """Compiles the pair-level statistics that show on the View Prices page of aspirewallet, 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.items(): 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(list(pair_data.keys()))))
def get_pairs(quote_asset='ASP', exclude_pairs=[], max_pairs=12, from_time=None): bindings = [] sql = '''SELECT (CASE WHEN forward_asset = ? THEN backward_asset ELSE forward_asset END) AS base_asset, (CASE WHEN backward_asset = ? THEN backward_asset ELSE forward_asset END) AS quote_asset, (CASE WHEN backward_asset = ? THEN (forward_asset || '/' || backward_asset) ELSE (backward_asset || '/' || forward_asset) END) AS pair, (CASE WHEN forward_asset = ? THEN backward_quantity ELSE forward_quantity END) AS bq, (CASE WHEN backward_asset = ? THEN backward_quantity ELSE forward_quantity END) AS qq ''' if from_time: sql += ''', block_time ''' sql += '''FROM order_matches ''' bindings += [ quote_asset, quote_asset, quote_asset, quote_asset, quote_asset ] if from_time: sql += '''INNER JOIN blocks ON order_matches.block_index = blocks.block_index ''' priority_quote_assets = [] for priority_quote_asset in config.QUOTE_ASSETS: if priority_quote_asset != quote_asset: priority_quote_assets.append(priority_quote_asset) else: break if len(priority_quote_assets) > 0: asset_bindings = ','.join( ['?' for e in range(0, len(priority_quote_assets))]) sql += '''WHERE ((forward_asset = ? AND backward_asset NOT IN ({})) OR (forward_asset NOT IN ({}) AND backward_asset = ?)) '''.format( asset_bindings, asset_bindings) bindings += [ quote_asset ] + priority_quote_assets + priority_quote_assets + [quote_asset] else: sql += '''WHERE ((forward_asset = ?) OR (backward_asset = ?)) ''' bindings += [quote_asset, quote_asset] if len(exclude_pairs) > 0: sql += '''AND pair NOT IN ({}) '''.format(','.join( ['?' for e in range(0, len(exclude_pairs))])) bindings += exclude_pairs if from_time: sql += '''AND block_time > ? ''' bindings += [from_time] sql += '''AND forward_asset != backward_asset AND status = ?''' bindings += ['completed', max_pairs] sql = '''SELECT base_asset, quote_asset, pair, SUM(bq) AS base_quantity, SUM(qq) AS quote_quantity FROM ({}) GROUP BY pair ORDER BY quote_quantity DESC LIMIT ?'''.format(sql) return util.call_jsonrpc_api('sql', { 'query': sql, 'bindings': bindings })['result']
def get_pair_price(base_asset, quote_asset, max_block_time=None, supplies=None): if not supplies: supplies = get_assets_supply([base_asset, quote_asset]) sql = '''SELECT *, MAX(tx0_index, tx1_index) AS tx_index, blocks.block_time FROM order_matches INNER JOIN blocks ON order_matches.block_index = blocks.block_index WHERE forward_asset IN (?, ?) AND backward_asset IN (?, ?) ''' bindings = [base_asset, quote_asset, base_asset, quote_asset] if max_block_time: sql += '''AND block_time <= ? ''' bindings += [max_block_time] sql += '''ORDER BY tx_index DESC LIMIT 2''' order_matches = util.call_jsonrpc_api('sql', { 'query': sql, 'bindings': bindings })['result'] if len(order_matches) == 0: last_price = D(0.0) elif order_matches[0]['forward_asset'] == base_asset: last_price = calculate_price( order_matches[0]['forward_quantity'], order_matches[0]['backward_quantity'], supplies[order_matches[0]['forward_asset']][1], supplies[order_matches[0]['backward_asset']][1]) else: last_price = calculate_price( order_matches[0]['backward_quantity'], order_matches[0]['forward_quantity'], supplies[order_matches[0]['backward_asset']][1], supplies[order_matches[0]['forward_asset']][1]) trend = 0 if len(order_matches) == 2: if order_matches[1]['forward_asset'] == base_asset: before_last_price = calculate_price( order_matches[0]['forward_quantity'], order_matches[0]['backward_quantity'], supplies[order_matches[0]['forward_asset']][1], supplies[order_matches[0]['backward_asset']][1]) else: before_last_price = calculate_price( order_matches[0]['backward_quantity'], order_matches[0]['forward_quantity'], supplies[order_matches[0]['backward_asset']][1], supplies[order_matches[0]['forward_asset']][1]) if last_price < before_last_price: trend = -1 elif last_price > before_last_price: trend = 1 return D(last_price), trend
def gaspd_rpc(command, params): return util.call_jsonrpc_api(command, params=params, endpoint=config.BACKEND_URL_NOAUTH, auth=config.BACKEND_AUTH, abort_on_error=True)['result']
def get_address_history(address, start_block=None, end_block=None): address_dict = {} address_dict['balances'] = util.call_jsonrpc_api( "get_balances", { 'filters': [ { 'field': 'address', 'op': '==', 'value': address }, ], }, abort_on_error=True)['result'] address_dict['debits'] = util.call_jsonrpc_api( "get_debits", { 'filters': [{ 'field': 'address', 'op': '==', 'value': address }, { 'field': 'quantity', 'op': '>', 'value': 0 }], 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['credits'] = util.call_jsonrpc_api( "get_credits", { 'filters': [{ 'field': 'address', 'op': '==', 'value': address }, { 'field': 'quantity', 'op': '>', 'value': 0 }], 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['proofofwork'] = util.call_jsonrpc_api( "get_proofofwork", { 'filters': [{ 'field': 'address', 'op': '==', 'value': address }, { 'field': 'mined', 'op': '>', 'value': 0 }], 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['sends'] = util.call_jsonrpc_api( "get_sends", { 'filters': [{ 'field': 'source', 'op': '==', 'value': address }, { 'field': 'destination', 'op': '==', 'value': address }], 'filterop': 'or', 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] #^ with filterop == 'or', we get all sends where this address was the source OR destination address_dict['orders'] = util.call_jsonrpc_api( "get_orders", { 'filters': [ { 'field': 'source', 'op': '==', 'value': address }, ], 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['order_matches'] = util.call_jsonrpc_api( "get_order_matches", { 'filters': [ { 'field': 'tx0_address', 'op': '==', 'value': address }, { 'field': 'tx1_address', 'op': '==', 'value': address }, ], 'filterop': 'or', 'order_by': 'tx0_block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['btcpays'] = util.call_jsonrpc_api( "get_btcpays", { 'filters': [{ 'field': 'source', 'op': '==', 'value': address }, { 'field': 'destination', 'op': '==', 'value': address }], 'filterop': 'or', 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['issuances'] = util.call_jsonrpc_api( "get_issuances", { 'filters': [{ 'field': 'issuer', 'op': '==', 'value': address }, { 'field': 'source', 'op': '==', 'value': address }], 'filterop': 'or', 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['broadcasts'] = util.call_jsonrpc_api( "get_broadcasts", { 'filters': [ { 'field': 'source', 'op': '==', 'value': address }, ], 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['bets'] = util.call_jsonrpc_api( "get_bets", { 'filters': [ { 'field': 'source', 'op': '==', 'value': address }, ], 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['bet_matches'] = util.call_jsonrpc_api( "get_bet_matches", { 'filters': [ { 'field': 'tx0_address', 'op': '==', 'value': address }, { 'field': 'tx1_address', 'op': '==', 'value': address }, ], 'filterop': 'or', 'order_by': 'tx0_block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['dividends'] = util.call_jsonrpc_api( "get_dividends", { 'filters': [ { 'field': 'source', 'op': '==', 'value': address }, ], 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['cancels'] = util.call_jsonrpc_api( "get_cancels", { 'filters': [ { 'field': 'source', 'op': '==', 'value': address }, ], 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['bet_expirations'] = util.call_jsonrpc_api( "get_bet_expirations", { 'filters': [ { 'field': 'source', 'op': '==', 'value': address }, ], 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['order_expirations'] = util.call_jsonrpc_api( "get_order_expirations", { 'filters': [ { 'field': 'source', 'op': '==', 'value': address }, ], 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['bet_match_expirations'] = util.call_jsonrpc_api( "get_bet_match_expirations", { 'filters': [ { 'field': 'tx0_address', 'op': '==', 'value': address }, { 'field': 'tx1_address', 'op': '==', 'value': address }, ], 'filterop': 'or', 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] address_dict['order_match_expirations'] = util.call_jsonrpc_api( "get_order_match_expirations", { 'filters': [ { 'field': 'tx0_address', 'op': '==', 'value': address }, { 'field': 'tx1_address', 'op': '==', 'value': address }, ], 'filterop': 'or', 'order_by': 'block_index', 'order_dir': 'asc', 'start_block': start_block, 'end_block': end_block, }, abort_on_error=True)['result'] return address_dict
def convert_armory_signedtx_to_raw_hex(signed_tx_ascii): endpoint = "http://%s:%s/" % (module_config['ARMORY_UTXSVR_HOST'], ARMORY_UTXSVR_PORT_MAINNET if not config.TESTNET else ARMORY_UTXSVR_PORT_TESTNET) params = {'signed_tx_ascii': signed_tx_ascii} raw_tx_hex = util.call_jsonrpc_api("convert_signed_tx_to_raw_hex", params=params, endpoint=endpoint, abort_on_error=True)['result'] return raw_tx_hex
def create_armory_utx(unsigned_tx_hex, public_key_hex): endpoint = "http://%s:%s/" % (module_config['ARMORY_UTXSVR_HOST'], ARMORY_UTXSVR_PORT_MAINNET if not config.TESTNET else ARMORY_UTXSVR_PORT_TESTNET) params = {'unsigned_tx_hex': unsigned_tx_hex, 'public_key_hex': public_key_hex} utx_ascii = util.call_jsonrpc_api("serialize_unsigned_tx", params=params, endpoint=endpoint, abort_on_error=True)['result'] return utx_ascii
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'] == 'GASP': 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'] == 'GASP': 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_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_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