Merge branch '503-api-new-market-api' into develop

This commit is contained in:
theoreticalbts 2016-01-07 14:23:24 -05:00
commit 3c7a4525d7
3 changed files with 356 additions and 15 deletions

View file

@ -342,13 +342,12 @@ namespace graphene { namespace app {
uint32_t count = 0;
auto itr = history_idx.lower_bound( hkey );
vector<order_history_object> result;
while( itr != history_idx.end() )
while( itr != history_idx.end() && count < limit)
{
if( itr->key.base != a || itr->key.quote != b ) break;
result.push_back( *itr );
++itr;
++count;
if( count > limit ) break;
}
return result;
@ -389,7 +388,7 @@ namespace graphene { namespace app {
FC_ASSERT(_app.chain_database());
const auto& db = *_app.chain_database();
vector<bucket_object> result;
result.reserve(100);
result.reserve(200);
if( a > b ) std::swap(a,b);
@ -397,7 +396,7 @@ namespace graphene { namespace app {
const auto& by_key_idx = bidx.indices().get<by_key>();
auto itr = by_key_idx.lower_bound( bucket_key( a, b, bucket_seconds, start ) );
while( itr != by_key_idx.end() && itr->key.open <= end && result.size() < 100 )
while( itr != by_key_idx.end() && itr->key.open <= end && result.size() < 200 )
{
if( !(itr->key.base == a && itr->key.quote == b && itr->key.seconds == bucket_seconds) )
{

View file

@ -34,18 +34,22 @@
#include <cctype>
#include <cfenv>
#include <iostream>
#define GET_REQUIRED_FEES_MAX_RECURSION 4
namespace graphene { namespace app {
class database_api_impl;
class database_api_impl : public std::enable_shared_from_this<database_api_impl>
{
public:
database_api_impl( graphene::chain::database& db );
~database_api_impl();
// Objects
fc::variants get_objects(const vector<object_id_type>& ids)const;
@ -88,16 +92,20 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
// Assets
vector<optional<asset_object>> get_assets(const vector<asset_id_type>& asset_ids)const;
vector<asset_object> list_assets(const string& lower_bound_symbol, uint32_t limit)const;
vector<asset_object> list_assets(const string& lower_bound_symbol, uint32_t limit)const;
vector<optional<asset_object>> lookup_asset_symbols(const vector<string>& symbols_or_ids)const;
// Markets / feeds
vector<limit_order_object> get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const;
vector<call_order_object> get_call_orders(asset_id_type a, uint32_t limit)const;
vector<force_settlement_object> get_settle_orders(asset_id_type a, uint32_t limit)const;
vector<call_order_object> get_margin_positions( const account_id_type& id )const;
vector<limit_order_object> get_limit_orders(asset_id_type a, asset_id_type b, uint32_t limit)const;
vector<call_order_object> get_call_orders(asset_id_type a, uint32_t limit)const;
vector<force_settlement_object> get_settle_orders(asset_id_type a, uint32_t limit)const;
vector<call_order_object> get_margin_positions( const account_id_type& id )const;
void subscribe_to_market(std::function<void(const variant&)> callback, asset_id_type a, asset_id_type b);
void unsubscribe_from_market(asset_id_type a, asset_id_type b);
market_ticker get_ticker( const string& base, const string& quote )const;
market_volume get_24_volume( const string& base, const string& quote )const;
order_book get_order_book( const string& base, const string& quote, unsigned limit = 50 )const;
vector<market_trade> get_trade_history( const string& base, const string& quote, fc::time_point_sec start, fc::time_point_sec stop, unsigned limit = 100 )const;
// Witnesses
vector<optional<witness_object>> get_witnesses(const vector<witness_id_type>& witness_ids)const;
@ -195,7 +203,7 @@ database_api_impl::database_api_impl( graphene::chain::database& db ):_db(db)
});
_applied_block_connection = _db.applied_block.connect([this](const signed_block&){ on_applied_block(); });
_pending_trx_connection = _db.on_pending_transaction.connect([this](const signed_transaction& trx ){
_pending_trx_connection = _db.on_pending_transaction.connect([this](const signed_transaction& trx ){
if( _pending_trx_callback ) _pending_trx_callback( fc::variant(trx) );
});
}
@ -461,7 +469,7 @@ vector<vector<account_id_type>> database_api_impl::get_key_references( vector<pu
{
wdump((a)(item)(item(_db).name));
result.push_back(item);
}
}
}
}
@ -988,6 +996,254 @@ void database_api_impl::unsubscribe_from_market(asset_id_type a, asset_id_type b
_market_subscriptions.erase(std::make_pair(a,b));
}
market_ticker database_api::get_ticker( const string& base, const string& quote )const
{
return my->get_ticker( base, quote );
}
market_ticker database_api_impl::get_ticker( const string& base, const string& quote )const
{
auto assets = lookup_asset_symbols( {base, quote} );
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
auto base_id = assets[0]->id;
auto quote_id = assets[1]->id;
market_ticker result;
result.base = base;
result.quote = quote;
result.base_volume = 0;
result.quote_volume = 0;
result.percent_change = 0;
result.lowest_ask = 0;
result.highest_bid = 0;
auto price_to_real = [&]( const long a, int p ) { return double( a ) / pow( 10, p ); };
try {
if( base_id > quote_id ) std::swap(base_id, quote_id);
const auto& bidx = _db.get_index_type<bucket_index>();
const auto& by_key_idx = bidx.indices().get<by_key>();
uint32_t bucket_size = 86400;
auto now = fc::time_point_sec( fc::time_point::now() );
auto itr = by_key_idx.lower_bound( bucket_key( base_id, quote_id, bucket_size,
now - bucket_size ) );
if( itr != by_key_idx.end() && itr->key.base == base_id && itr->key.quote == quote_id && itr->key.seconds == bucket_size )
{
auto trades = get_trade_history( base, quote, now, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
if (assets[0]->id == base_id)
{
result.percent_change = ( ( price_to_real( itr->close_quote.value, assets[1]->precision ) / price_to_real( itr->close_base.value, assets[0]->precision ) )
/ ( price_to_real( itr->open_quote.value, assets[1]->precision ) / price_to_real( itr->open_base.value, assets[0]->precision ) ) - 1 ) * 100;
result.lowest_ask = (double) itr->low_quote.value / (double) itr->low_base.value;
result.highest_bid = (double) itr->high_quote.value / (double) itr->high_base.value;
result.latest = trades[0].price;
}
else
{
result.percent_change = ( ( price_to_real( itr->close_base.value, assets[1]->precision ) / price_to_real( itr->close_quote.value, assets[0]->precision ) )
/ ( price_to_real( itr->open_base.value, assets[1]->precision ) / price_to_real( itr->open_quote.value, assets[0]->precision ) ) - 1) * 100;
result.lowest_ask = (double) itr->low_base.value / (double) itr->low_quote.value;
result.highest_bid = (double) itr->high_base.value / (double) itr->high_quote.value;
result.latest = trades[0].price;
}
for ( market_trade t: trades )
{
result.base_volume += t.amount;
result.quote_volume += t.value;
}
while (trades.size() == 100)
{
for ( market_trade t: trades )
{
result.base_volume += t.amount;
result.quote_volume += t.value;
}
trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
}
}
return result;
} FC_CAPTURE_AND_RETHROW( (base)(quote) )
}
market_volume database_api::get_24_volume( const string& base, const string& quote )const
{
return my->get_24_volume( base, quote );
}
market_volume database_api_impl::get_24_volume( const string& base, const string& quote )const
{
auto assets = lookup_asset_symbols( {base, quote} );
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
auto base_id = assets[0]->id;
auto quote_id = assets[1]->id;
market_volume result;
result.base = base;
result.quote = quote;
result.base_volume = 0;
result.quote_volume = 0;
try {
if( base_id > quote_id ) std::swap(base_id, quote_id);
uint32_t bucket_size = 86400;
auto now = fc::time_point_sec( fc::time_point::now() );
auto trades = get_trade_history( base, quote, now, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
for ( market_trade t: trades )
{
result.base_volume += t.amount;
result.quote_volume += t.value;
}
while (trades.size() == 100)
{
for ( market_trade t: trades )
{
result.base_volume += t.amount;
result.quote_volume += t.value;
}
trades = get_trade_history( base, quote, trades[99].date, fc::time_point_sec( now.sec_since_epoch() - bucket_size ), 100 );
}
return result;
} FC_CAPTURE_AND_RETHROW( (base)(quote) )
}
order_book database_api::get_order_book( const string& base, const string& quote, unsigned limit )const
{
return my->get_order_book( base, quote, limit);
}
order_book database_api_impl::get_order_book( const string& base, const string& quote, unsigned limit )const
{
FC_ASSERT( limit <= 50 );
order_book result;
result.base = base;
result.quote = quote;
auto assets = lookup_asset_symbols( {base, quote} );
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
auto base_id = assets[0]->id;
auto quote_id = assets[1]->id;
auto orders = get_limit_orders( base_id, quote_id, limit );
auto asset_to_real = [&]( const asset& a, int p ) { return double(a.amount.value)/pow( 10, p ); };
auto price_to_real = [&]( const price& p )
{
if( p.base.asset_id == base_id )
return asset_to_real( p.quote, assets[1]->precision ) / asset_to_real( p.base, assets[0]->precision );
else
return asset_to_real( p.base, assets[1]->precision ) / asset_to_real( p.quote, assets[0]->precision );
};
for( const auto& o : orders ) {
if( o.sell_price.base.asset_id == base_id )
{
result.asks.push_back( std::make_pair( price_to_real(o.sell_price),
asset_to_real(o.sell_price.base, assets[0]->precision)) );
}
else
{
result.bids.push_back( std::make_pair( price_to_real(o.sell_price),
asset_to_real(o.sell_price.quote, assets[0]->precision ) ) );
}
}
return result;
}
vector<market_trade> database_api::get_trade_history( const string& base,
const string& quote,
fc::time_point_sec start,
fc::time_point_sec stop,
unsigned limit )const
{
return my->get_trade_history( base, quote, start, stop, limit );
}
vector<market_trade> database_api_impl::get_trade_history( const string& base,
const string& quote,
fc::time_point_sec start,
fc::time_point_sec stop,
unsigned limit )const
{
FC_ASSERT( limit <= 100 );
auto assets = lookup_asset_symbols( {base, quote} );
FC_ASSERT( assets[0], "Invalid base asset symbol: ${s}", ("s",base) );
FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) );
auto base_id = assets[0]->id;
auto quote_id = assets[1]->id;
if( base_id > quote_id ) std::swap( base_id, quote_id );
const auto& history_idx = _db.get_index_type<graphene::market_history::history_index>().indices().get<by_key>();
history_key hkey;
hkey.base = base_id;
hkey.quote = quote_id;
hkey.sequence = std::numeric_limits<int64_t>::min();
auto price_to_real = [&]( const long long a, int p ) { return double( a ) / pow( 10, p ); };
if ( start.sec_since_epoch() == 0 )
start = fc::time_point_sec( fc::time_point::now() );
uint32_t count = 0;
auto itr = history_idx.lower_bound( hkey );
vector<market_trade> result;
while( itr != history_idx.end() && count < limit && !( itr->key.base != base_id || itr->key.quote != quote_id || itr->time < stop ) )
{
if( itr->time < start )
{
market_trade trade;
if( base_id == itr->op.receives.asset_id )
{
trade.amount = price_to_real( itr->op.receives.amount.value, assets[0]->precision );
trade.value = price_to_real( itr->op.pays.amount.value, assets[1]->precision );
}
else
{
trade.amount = price_to_real( itr->op.pays.amount.value, assets[0]->precision );
trade.value = price_to_real( itr->op.receives.amount.value, assets[1]->precision );
}
trade.date = itr->time;
trade.price = trade.value / trade.amount;
result.push_back( trade );
++count;
}
// Trades are tracked in each direction.
++itr;
++itr;
}
return result;
}
//////////////////////////////////////////////////////////////////////
// //
// Witnesses //
@ -1500,7 +1756,7 @@ vector<blinded_balance_object> database_api_impl::get_blinded_balances( const fl
//////////////////////////////////////////////////////////////////////
// //
// Private methods //
// //
// //
//////////////////////////////////////////////////////////////////////
void database_api_impl::broadcast_updates( const vector<variant>& updates )
@ -1577,7 +1833,7 @@ void database_api_impl::on_objects_changed(const vector<object_id_type>& ids)
if( _market_subscriptions.size() )
{
if( !_subscribe_callback )
if( !_subscribe_callback )
obj = _db.find_object( id );
if( obj )
{

View file

@ -41,6 +41,8 @@
#include <graphene/chain/witness_object.hpp>
#include <graphene/chain/confidential_evaluator.hpp>
#include <graphene/market_history/market_history_plugin.hpp>
#include <fc/api.hpp>
#include <fc/optional.hpp>
#include <fc/variant_object.hpp>
@ -57,10 +59,47 @@
namespace graphene { namespace app {
using namespace graphene::chain;
using namespace graphene::market_history;
using namespace std;
class database_api_impl;
struct order_book
{
string base;
string quote;
vector< pair<double,double> > bids;
vector< pair<double,double> > asks;
};
struct market_ticker
{
string base;
string quote;
double latest;
double lowest_ask;
double highest_bid;
double percent_change;
double base_volume;
double quote_volume;
};
struct market_volume
{
string base;
string quote;
double base_volume;
double quote_volume;
};
struct market_trade
{
fc::time_point_sec date;
double price;
double amount;
double value;
};
/**
* @brief The database_api class implements the RPC API for the chain database.
*
@ -324,7 +363,46 @@ class database_api
* @param a First asset ID
* @param b Second asset ID
*/
void unsubscribe_from_market(asset_id_type a, asset_id_type b);
void unsubscribe_from_market( asset_id_type a, asset_id_type b );
/**
* @brief Returns the ticker for the market assetA:assetB
* @param a String name of the first asset
* @param b String name of the second asset
* @return The market ticker for the past 24 hours.
*/
market_ticker get_ticker( const string& base, const string& quote )const;
/**
* @brief Returns the 24 hour volume for the market assetA:assetB
* @param a String name of the first asset
* @param b String name of the second asset
* @return The market volume over the past 24 hours
*/
market_volume get_24_volume( const string& base, const string& quote )const;
/**
* @brief Returns the order book for the market base:quote
* @param base String name of the first asset
* @param quote String name of the second asset
* @param depth of the order book. Up to depth of each asks and bids, capped at 50. Prioritizes most moderate of each
* @return Order book of the market
*/
order_book get_order_book( const string& base, const string& quote, unsigned limit = 50 )const;
/**
* @brief Returns recent trades for the market assetA:assetB
* Note: Currentlt, timezone offsets are not supported. The time must be UTC.
* @param a String name of the first asset
* @param b String name of the second asset
* @param stop Stop time as a UNIX timestamp
* @param limit Number of trasactions to retrieve, capped at 100
* @param start Start time as a UNIX timestamp
* @return Recent transactions in the market
*/
vector<market_trade> get_trade_history( const string& base, const string& quote, fc::time_point_sec start, fc::time_point_sec stop, unsigned limit = 100 )const;
///////////////
// Witnesses //
@ -475,6 +553,10 @@ class database_api
};
} }
FC_REFLECT( graphene::app::order_book, (base)(quote)(bids)(asks) );
FC_REFLECT( graphene::app::market_ticker, (base)(quote)(latest)(lowest_ask)(highest_bid)(percent_change)(base_volume)(quote_volume) );
FC_REFLECT( graphene::app::market_volume, (base)(quote)(base_volume)(quote_volume) );
FC_REFLECT( graphene::app::market_trade, (date)(price)(amount)(value) );
FC_API(graphene::app::database_api,
// Objects
@ -524,12 +606,16 @@ FC_API(graphene::app::database_api,
(lookup_asset_symbols)
// Markets / feeds
(get_order_book)
(get_limit_orders)
(get_call_orders)
(get_settle_orders)
(get_margin_positions)
(subscribe_to_market)
(unsubscribe_from_market)
(get_ticker)
(get_24_volume)
(get_trade_history)
// Witnesses
(get_witnesses)