Skip to content

Commit

Permalink
Change doubles in market API to string. bitshares#144
Browse files Browse the repository at this point in the history
  • Loading branch information
abitmore committed Jan 22, 2018
1 parent 02a4499 commit c55ee9b
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 85 deletions.
158 changes: 87 additions & 71 deletions libraries/app/database_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>


//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<typename T>
void subscribe_to_item( const T& i )const
{
Expand Down Expand Up @@ -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 );
Expand All @@ -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;

Expand All @@ -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 )
{
Expand All @@ -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 )
{
Expand Down Expand Up @@ -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 );
}
}
Expand Down Expand Up @@ -1334,15 +1368,6 @@ vector<market_trade> 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() );

Expand All @@ -1358,17 +1383,17 @@ vector<market_trade> 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 )
{
Expand Down Expand Up @@ -1439,15 +1464,6 @@ vector<market_trade> 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<market_trade> result;
Expand All @@ -1470,17 +1486,17 @@ vector<market_trade> 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 )
{
Expand Down
28 changes: 14 additions & 14 deletions libraries/app/include/graphene/app/database_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -84,30 +84,30 @@ 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
{
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;
};
Expand Down

0 comments on commit c55ee9b

Please sign in to comment.