diff --git a/libraries/app/database_api.cpp b/libraries/app/database_api.cpp index 96e100b6d7..abaf570b81 100644 --- a/libraries/app/database_api.cpp +++ b/libraries/app/database_api.cpp @@ -155,6 +155,11 @@ class database_api_impl : public std::enable_shared_from_this //private: + string uint128_amount_to_string( const fc::uint128& amount, const uint8_t precision )const; + string price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote )const; + string price_diff_to_string( const price& old_price, const price& new_price, + const asset_object& _base, const asset_object& _quote )const; + template void subscribe_to_item( const T& i )const { @@ -1149,6 +1154,64 @@ void database_api_impl::unsubscribe_from_market(asset_id_type a, asset_id_type b _market_subscriptions.erase(std::make_pair(a,b)); } +string database_api_impl::uint128_amount_to_string( const fc::uint128& amount, const uint8_t precision )const +{ try { + fc::uint128 scaled_precision = 1; + for( uint8_t i = 0; i < precision; ++i ) + scaled_precision *= 10; + FC_ASSERT(scaled_precision > 0); + + string result = string( amount / scaled_precision ); + auto decimals = amount % scaled_precision; + if( decimals > 0 ) + result += "." + string(scaled_precision + decimals).erase(0,1); + return result; +} FC_CAPTURE_AND_RETHROW( (amount)(precision) ) } + +string database_api_impl::price_to_string( const price& _price, const asset_object& _base, const asset_object& _quote )const +{ try { + share_type base_amount = _price.base.amount; + share_type quote_amount = _price.quote.amount; + if( _price.base.asset_id != _base.id ) + std::swap( base_amount, quote_amount ); + + // times (10**18 * 10**3) so won't overflow but always have good accuracy + fc::uint128 price128 = fc::uint128( base_amount.value ) * uint64_t(1000000000000000000ll) * uint64_t(1000) / quote_amount.value; + + return uint128_amount_to_string( price128, 21 + _base.precision - _quote.precision ); +} FC_CAPTURE_AND_RETHROW( (_price)(_base)(_quote) ) } + +string database_api_impl::price_diff_to_string( const price& old_price, const price& new_price, + const asset_object& _base, const asset_object& _quote )const +{ try { + share_type old_base_amount = old_price.base.amount; + share_type old_quote_amount = old_price.quote.amount; + if( old_price.base.asset_id != _base.id ) + std::swap( old_base_amount, old_quote_amount ); + + share_type new_base_amount = new_price.base.amount; + share_type new_quote_amount = new_price.quote.amount; + if( new_price.base.asset_id != _base.id ) + std::swap( new_base_amount, new_quote_amount ); + + // change = new/old - 1 = (new_base/new_quote)/(old_base/old_quote) - 1 + // = (new_base * old_quote) / (new_quote * old_base) - 1 + // = (new_base * old_quote - new_quote * old_base) / (new_quote * old_base) + fc::uint128 new128 = fc::uint128( new_base_amount.value ) * old_quote_amount.value; + fc::uint128 old128 = fc::uint128( old_base_amount.value ) * new_quote_amount.value; + bool non_negative = (new128 >= old128); + fc::uint128 diff128; + if( non_negative ) + diff128 = new128 - old128; + else + diff128 = old128 - new128; + string diff_str = uint128_amount_to_string( diff128 * 10000 / old128, 2 ); + if( non_negative ) + return diff_str; + else + return "-" + diff_str; +} FC_CAPTURE_AND_RETHROW( (old_price)(new_price)(_base)(_quote) ) } + market_ticker database_api::get_ticker( const string& base, const string& quote )const { return my->get_ticker( base, quote ); @@ -1161,34 +1224,20 @@ market_ticker database_api_impl::get_ticker( const string& base, const string& q FC_ASSERT( assets[1], "Invalid quote asset symbol: ${s}", ("s",quote) ); const fc::time_point_sec now = fc::time_point::now(); - const fc::time_point_sec yesterday = fc::time_point_sec( now.sec_since_epoch() - 86400 ); market_ticker result; result.time = now; result.base = base; result.quote = quote; - result.latest = 0; - result.lowest_ask = 0; - result.highest_bid = 0; - result.percent_change = 0; - result.base_volume = 0; - result.quote_volume = 0; + result.latest = "0"; + result.lowest_ask = "0"; + result.highest_bid = "0"; + result.percent_change = "0"; auto base_id = assets[0]->id; auto quote_id = assets[1]->id; if( base_id > quote_id ) std::swap( base_id, quote_id ); - // TODO: move following duplicate code out - // TODO: using pow is a bit inefficient here, optimization is possible - 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 == assets[0]->id ) - return asset_to_real( p.base, assets[0]->precision ) / asset_to_real( p.quote, assets[1]->precision ); - else - return asset_to_real( p.quote, assets[0]->precision ) / asset_to_real( p.base, assets[1]->precision ); - }; - fc::uint128 base_volume; fc::uint128 quote_volume; @@ -1197,12 +1246,12 @@ market_ticker database_api_impl::get_ticker( const string& base, const string& q if( itr != ticker_idx.end() ) { price latest_price = asset( itr->latest_base, itr->base ) / asset( itr->latest_quote, itr->quote ); - result.latest = price_to_real( latest_price ); + result.latest = price_to_string( latest_price, *assets[0], *assets[1] ); if( itr->last_day_base != 0 && itr->last_day_quote != 0 // has trade data before 24 hours && ( itr->last_day_base != itr->latest_base || itr->last_day_quote != itr->latest_quote ) ) // price changed { price last_day_price = asset( itr->last_day_base, itr->base ) / asset( itr->last_day_quote, itr->quote ); - result.percent_change = ( result.latest / price_to_real( last_day_price ) - 1 ) * 100; + result.percent_change = price_diff_to_string( last_day_price, latest_price, *assets[0], *assets[1] ); } if( assets[0]->id == itr->base ) { @@ -1216,13 +1265,8 @@ market_ticker database_api_impl::get_ticker( const string& base, const string& q } } - auto uint128_to_double = []( const fc::uint128& n ) - { - if( n.hi == 0 ) return double( n.lo ); - return double(n.hi) * (uint64_t(1)<<63) * 2 + n.lo; - }; - result.base_volume = uint128_to_double( base_volume ) / pow( 10, assets[0]->precision ); - result.quote_volume = uint128_to_double( quote_volume ) / pow( 10, assets[1]->precision ); + result.base_volume = uint128_amount_to_string( base_volume, assets[0]->precision ); + result.quote_volume = uint128_amount_to_string( quote_volume, assets[1]->precision ); if( !skip_order_book ) { @@ -1275,32 +1319,22 @@ order_book database_api_impl::get_order_book( const string& base, const string& 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.base, assets[0]->precision ) / asset_to_real( p.quote, assets[1]->precision ); - else - return asset_to_real( p.quote, assets[0]->precision ) / asset_to_real( p.base, assets[1]->precision ); - }; - for( const auto& o : orders ) { if( o.sell_price.base.asset_id == base_id ) { order ord; - ord.price = price_to_real( o.sell_price ); - ord.quote = asset_to_real( share_type( ( uint128_t( o.for_sale.value ) * o.sell_price.quote.amount.value ) / o.sell_price.base.amount.value ), assets[1]->precision ); - ord.base = asset_to_real( o.for_sale, assets[0]->precision ); + ord.price = price_to_string( o.sell_price, *assets[0], *assets[1] ); + ord.quote = assets[1]->amount_to_string( share_type( ( uint128_t( o.for_sale.value ) * o.sell_price.quote.amount.value ) / o.sell_price.base.amount.value ) ); + ord.base = assets[0]->amount_to_string( o.for_sale ); result.bids.push_back( ord ); } else { order ord; - ord.price = price_to_real( o.sell_price ); - ord.quote = asset_to_real( o.for_sale, assets[1]->precision ); - ord.base = asset_to_real( share_type( ( uint128_t( o.for_sale.value ) * o.sell_price.quote.amount.value ) / o.sell_price.base.amount.value ), assets[0]->precision ); + ord.price = price_to_string( o.sell_price, *assets[0], *assets[1] ); + ord.quote = assets[1]->amount_to_string( o.for_sale ); + ord.base = assets[0]->amount_to_string( share_type( ( uint128_t( o.for_sale.value ) * o.sell_price.quote.amount.value ) / o.sell_price.base.amount.value ) ); result.asks.push_back( ord ); } } @@ -1334,15 +1368,6 @@ vector database_api_impl::get_trade_history( const string& base, if( base_id > quote_id ) std::swap( base_id, quote_id ); - 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 == assets[0]->id ) - return asset_to_real( p.base, assets[0]->precision ) / asset_to_real( p.quote, assets[1]->precision ); - else - return asset_to_real( p.quote, assets[0]->precision ) / asset_to_real( p.base, assets[1]->precision ); - }; - if ( start.sec_since_epoch() == 0 ) start = fc::time_point_sec( fc::time_point::now() ); @@ -1358,17 +1383,17 @@ vector database_api_impl::get_trade_history( const string& base, if( assets[0]->id == itr->op.receives.asset_id ) { - trade.amount = asset_to_real( itr->op.pays, assets[1]->precision ); - trade.value = asset_to_real( itr->op.receives, assets[0]->precision ); + trade.amount = assets[1]->amount_to_string( itr->op.pays ); + trade.value = assets[0]->amount_to_string( itr->op.receives ); } else { - trade.amount = asset_to_real( itr->op.receives, assets[1]->precision ); - trade.value = asset_to_real( itr->op.pays, assets[0]->precision ); + trade.amount = assets[1]->amount_to_string( itr->op.receives ); + trade.value = assets[0]->amount_to_string( itr->op.pays ); } trade.date = itr->time; - trade.price = price_to_real( itr->op.fill_price ); + trade.price = price_to_string( itr->op.fill_price, *assets[0], *assets[1] ); if( itr->op.is_maker ) { @@ -1439,15 +1464,6 @@ vector database_api_impl::get_trade_history_by_sequence( hkey.quote = quote_id; hkey.sequence = start_seq; - 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 == assets[0]->id ) - return asset_to_real( p.base, assets[0]->precision ) / asset_to_real( p.quote, assets[1]->precision ); - else - return asset_to_real( p.quote, assets[0]->precision ) / asset_to_real( p.base, assets[1]->precision ); - }; - uint32_t count = 0; auto itr = history_idx.lower_bound( hkey ); vector result; @@ -1470,17 +1486,17 @@ vector database_api_impl::get_trade_history_by_sequence( if( assets[0]->id == itr->op.receives.asset_id ) { - trade.amount = asset_to_real( itr->op.pays, assets[1]->precision ); - trade.value = asset_to_real( itr->op.receives, assets[0]->precision ); + trade.amount = assets[1]->amount_to_string( itr->op.pays ); + trade.value = assets[0]->amount_to_string( itr->op.receives ); } else { - trade.amount = asset_to_real( itr->op.receives, assets[1]->precision ); - trade.value = asset_to_real( itr->op.pays, assets[0]->precision ); + trade.amount = assets[1]->amount_to_string( itr->op.receives ); + trade.value = assets[0]->amount_to_string( itr->op.pays ); } trade.date = itr->time; - trade.price = price_to_real( itr->op.fill_price ); + trade.price = price_to_string( itr->op.fill_price, *assets[0], *assets[1] ); if( itr->op.is_maker ) { diff --git a/libraries/app/include/graphene/app/database_api.hpp b/libraries/app/include/graphene/app/database_api.hpp index 1ab90c70c2..87b8e2e965 100644 --- a/libraries/app/include/graphene/app/database_api.hpp +++ b/libraries/app/include/graphene/app/database_api.hpp @@ -66,9 +66,9 @@ class database_api_impl; struct order { - double price; - double quote; - double base; + string price; + string quote; + string base; }; struct order_book @@ -84,12 +84,12 @@ struct market_ticker time_point_sec time; string base; string quote; - double latest; - double lowest_ask; - double highest_bid; - double percent_change; - double base_volume; - double quote_volume; + string latest; + string lowest_ask; + string highest_bid; + string percent_change; + string base_volume; + string quote_volume; }; struct market_volume @@ -97,17 +97,17 @@ struct market_volume time_point_sec time; string base; string quote; - double base_volume; - double quote_volume; + string base_volume; + string quote_volume; }; struct market_trade { int64_t sequence = 0; fc::time_point_sec date; - double price; - double amount; - double value; + string price; + string amount; + string value; account_id_type side1_account_id = GRAPHENE_NULL_ACCOUNT; account_id_type side2_account_id = GRAPHENE_NULL_ACCOUNT; };