BitShares-Core  7.0.2
BitShares blockchain node software and command-line wallet software
asset.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
3  *
4  * The MIT License
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
25 #include <boost/rational.hpp>
26 #include <boost/multiprecision/cpp_int.hpp>
27 
28 #include <fc/io/raw.hpp>
29 #include <fc/uint128.hpp>
30 
31 namespace graphene { namespace protocol {
32  using fc::uint128_t;
33 
34  bool operator == ( const price& a, const price& b )
35  {
36  if( std::tie( a.base.asset_id, a.quote.asset_id ) != std::tie( b.base.asset_id, b.quote.asset_id ) )
37  return false;
38 
39  const auto amult = uint128_t( b.quote.amount.value ) * a.base.amount.value;
40  const auto bmult = uint128_t( a.quote.amount.value ) * b.base.amount.value;
41 
42  return amult == bmult;
43  }
44 
45  bool operator < ( const price& a, const price& b )
46  {
47  if( a.base.asset_id < b.base.asset_id ) return true;
48  if( a.base.asset_id > b.base.asset_id ) return false;
49  if( a.quote.asset_id < b.quote.asset_id ) return true;
50  if( a.quote.asset_id > b.quote.asset_id ) return false;
51 
52  const auto amult = uint128_t( b.quote.amount.value ) * a.base.amount.value;
53  const auto bmult = uint128_t( a.quote.amount.value ) * b.base.amount.value;
54 
55  return amult < bmult;
56  }
57 
58  asset operator * ( const asset& a, const price& b )
59  {
60  if( a.asset_id == b.base.asset_id )
61  {
62  FC_ASSERT( b.base.amount.value > 0 );
63  uint128_t result = (uint128_t(a.amount.value) * b.quote.amount.value)/b.base.amount.value;
65  return asset( static_cast<int64_t>(result), b.quote.asset_id );
66  }
67  else if( a.asset_id == b.quote.asset_id )
68  {
69  FC_ASSERT( b.quote.amount.value > 0 );
70  uint128_t result = (uint128_t(a.amount.value) * b.base.amount.value)/b.quote.amount.value;
72  return asset( static_cast<int64_t>(result), b.base.asset_id );
73  }
74  FC_THROW_EXCEPTION( fc::assert_exception, "invalid asset * price", ("asset",a)("price",b) );
75  }
76 
78  {
79  const asset& a = *this;
80  if( a.asset_id == b.base.asset_id )
81  {
82  FC_ASSERT( b.base.amount.value > 0 );
83  uint128_t result = ( ( ( uint128_t(a.amount.value) * b.quote.amount.value ) + b.base.amount.value ) - 1 )
84  / b.base.amount.value;
86  return asset( static_cast<int64_t>(result), b.quote.asset_id );
87  }
88  else if( a.asset_id == b.quote.asset_id )
89  {
90  FC_ASSERT( b.quote.amount.value > 0 );
91  uint128_t result = ( ( ( uint128_t(a.amount.value) * b.base.amount.value ) + b.quote.amount.value ) - 1 )
92  / b.quote.amount.value;
94  return asset( static_cast<int64_t>(result), b.base.asset_id );
95  }
96  FC_THROW_EXCEPTION( fc::assert_exception,
97  "invalid asset::multiply_and_round_up(price)", ("asset",a)("price",b) );
98  }
99 
100  price operator / ( const asset& base, const asset& quote )
101  { try {
102  FC_ASSERT( base.asset_id != quote.asset_id );
103  return price{base,quote};
104  } FC_CAPTURE_AND_RETHROW( (base)(quote) ) }
105 
106  price price::max( asset_id_type base, asset_id_type quote )
108 
109  price price::min( asset_id_type base, asset_id_type quote )
111 
112  price operator * ( const price& p, const ratio_type& r )
113  { try {
114  p.validate();
115 
116  FC_ASSERT( r.numerator() > 0 && r.denominator() > 0 );
117 
118  if( r.numerator() == r.denominator() ) return p;
119 
120  boost::rational<uint128_t> p128( p.base.amount.value, p.quote.amount.value );
121  boost::rational<uint128_t> r128( r.numerator(), r.denominator() );
122  auto cp = p128 * r128;
123  auto ocp = cp;
124 
125  bool shrinked = false;
126  bool using_max = false;
127  static const uint128_t max( GRAPHENE_MAX_SHARE_SUPPLY );
128  while( cp.numerator() > max || cp.denominator() > max )
129  {
130  if( 1 == cp.numerator() )
131  {
132  cp = boost::rational<uint128_t>( 1, max );
133  using_max = true;
134  break;
135  }
136  else if( 1 == cp.denominator() )
137  {
138  cp = boost::rational<uint128_t>( max, 1 );
139  using_max = true;
140  break;
141  }
142  else
143  {
144  cp = boost::rational<uint128_t>( cp.numerator() >> 1, cp.denominator() >> 1 );
145  shrinked = true;
146  }
147  }
148  if( shrinked ) // maybe not accurate enough due to rounding, do additional checks here
149  {
150  uint128_t num = ocp.numerator();
151  uint128_t den = ocp.denominator();
152  FC_ASSERT( num > 0 && den > 0, "Internal error" );
153  if( num > den )
154  {
155  num /= den;
156  num = std::min( num, max );
157  den = 1;
158  }
159  else
160  {
161  den /= num;
162  den = std::min( den, max );
163  num = 1;
164  }
165  boost::rational<uint128_t> ncp( num, den );
166  if( num == max || den == max ) // it's on the edge, we know it's accurate enough
167  cp = ncp;
168  else
169  {
170  // from the accurate ocp, now we have ncp and cp. use the one which is closer to ocp.
171  // TODO improve performance
172  auto diff1 = (ncp >= ocp) ? (ncp - ocp) : (ocp - ncp);
173  auto diff2 = (cp >= ocp) ? (cp - ocp) : (ocp - cp);
174  if( diff1 < diff2 ) cp = ncp;
175  }
176  }
177 
178  price np = asset( static_cast<int64_t>(cp.numerator()), p.base.asset_id )
179  / asset( static_cast<int64_t>(cp.denominator()), p.quote.asset_id );
180 
181  if( shrinked || using_max )
182  {
183  bool flipped = ( r.numerator() > r.denominator() ) ? ( np < p ) : ( np > p );
184  if( flipped )
185  // even with an accurate result, if p is out of valid range, return it
186  np = p;
187  }
188 
189  np.validate();
190  return np;
191  } FC_CAPTURE_AND_RETHROW( (p)(r.numerator())(r.denominator()) ) }
192 
193  price operator / ( const price& p, const ratio_type& r )
194  { try {
195  return p * ratio_type( r.denominator(), r.numerator() );
196  } FC_CAPTURE_AND_RETHROW( (p)(r.numerator())(r.denominator()) ) }
197 
216  price price::call_price( const asset& debt, const asset& collateral, uint16_t collateral_ratio)
217  { try {
218  boost::rational<uint128_t> swan(debt.amount.value,collateral.amount.value);
219  boost::rational<uint128_t> ratio( collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );
220  auto cp = swan * ratio;
221 
222  while( cp.numerator() > GRAPHENE_MAX_SHARE_SUPPLY || cp.denominator() > GRAPHENE_MAX_SHARE_SUPPLY )
223  cp = boost::rational<uint128_t>( (cp.numerator() >> 1)+1, (cp.denominator() >> 1)+1 );
224 
225  return ( asset( static_cast<int64_t>(cp.denominator()), collateral.asset_id )
226  / asset( static_cast<int64_t>(cp.numerator()), debt.asset_id ) );
227  } FC_CAPTURE_AND_RETHROW( (debt)(collateral)(collateral_ratio) ) }
228 
229  bool price::is_null() const
230  {
231  // Effectively same as "return *this == price();" but perhaps faster
232  return ( base.asset_id == asset_id_type() && quote.asset_id == asset_id_type() );
233  }
234 
235  void price::validate( bool check_upper_bound /* = false */ )const
236  { try {
237  FC_ASSERT( base.amount.value > 0, "Base amount should be positive" );
238  FC_ASSERT( quote.amount.value > 0, "Quote amount should be positive" );
239  FC_ASSERT( base.asset_id != quote.asset_id, "Base asset ID and quote asset ID should be different" );
240  if( check_upper_bound )
241  {
243  "Base amount should not be greater than ${max}",
244  ("max", GRAPHENE_MAX_SHARE_SUPPLY) );
246  "Quote amount should not be greater than ${max}",
247  ("max", GRAPHENE_MAX_SHARE_SUPPLY) );
248  }
250 
251  void price_feed::validate() const
252  { try {
253  if( !settlement_price.is_null() )
259  // Note: there was code here calling `max_short_squeeze_price();` before core-1270 hard fork,
260  // in order to make sure that it doesn't overflow,
261  // but the code doesn't actually check overflow, and it won't overflow, so the code is removed.
262 
263  // Note: not checking `maintenance_collateral_ratio >= maximum_short_squeeze_ratio` since launch
264  } FC_CAPTURE_AND_RETHROW( (*this) ) }
265 
266  bool price_feed::is_for( asset_id_type asset_id ) const
267  {
268  try
269  {
270  if( !settlement_price.is_null() )
271  return (settlement_price.base.asset_id == asset_id);
272  if( !core_exchange_rate.is_null() )
273  return (core_exchange_rate.base.asset_id == asset_id);
274  // (null, null) is valid for any feed
275  return true;
276  }
277  FC_CAPTURE_AND_RETHROW( (*this) )
278  }
279 
280  // This function is kept here due to potential different behavior in edge cases.
281  // TODO check after core-1270 hard fork to see if we can safely remove it
283  {
284  // settlement price is in debt/collateral
285  boost::rational<uint128_t> sp( settlement_price.base.amount.value, settlement_price.quote.amount.value );
286  boost::rational<uint128_t> ratio( GRAPHENE_COLLATERAL_RATIO_DENOM, maximum_short_squeeze_ratio );
287  auto cp = sp * ratio;
288 
289  while( cp.numerator() > GRAPHENE_MAX_SHARE_SUPPLY || cp.denominator() > GRAPHENE_MAX_SHARE_SUPPLY )
290  cp = boost::rational<uint128_t>( (cp.numerator() >> 1)+(cp.numerator()&1U),
291  (cp.denominator() >> 1)+(cp.denominator()&1U) );
292 
293  return ( asset( static_cast<int64_t>(cp.numerator()), settlement_price.base.asset_id )
294  / asset( static_cast<int64_t>(cp.denominator()), settlement_price.quote.asset_id ) );
295  }
296 
297 
298  // Documentation in header.
299  // Calculation: MSSP = settlement_price / MSSR
301  {
302  // settlement price is in debt/collateral
304  }
305 
306  // Documentation in header.
307  // Calculation: MCOP = settlement_price / (MSSR - MCFR); result is in debt/collateral
309  {
310  return settlement_price / margin_call_order_ratio( maybe_mcfr );
311  }
312 
313  // Calculation: MCOR = MSSR - MCFR, floor at 1.00
314  uint16_t price_feed::get_margin_call_price_numerator(const fc::optional<uint16_t>& maybe_mcfr)const
315  {
316  const uint16_t mcfr = maybe_mcfr.valid() ? *maybe_mcfr : 0;
317  uint16_t numerator = (mcfr < maximum_short_squeeze_ratio) ?
318  (maximum_short_squeeze_ratio - mcfr) : GRAPHENE_COLLATERAL_RATIO_DENOM; // won't underflow
319  if (numerator < GRAPHENE_COLLATERAL_RATIO_DENOM)
320  numerator = GRAPHENE_COLLATERAL_RATIO_DENOM; // floor at 1.00
321  return numerator;
322  }
323 
324  // Documentation in header.
325  // Calculation: MCOR = MSSR - MCFR
327  {
328  auto numerator = get_margin_call_price_numerator( maybe_mcfr );
329  return ratio_type( numerator, GRAPHENE_COLLATERAL_RATIO_DENOM );
330  }
331 
332  // Reason for this function is explained in header.
333  // Calculation: (MSSR - MCFR) / MSSR
335  {
336  auto numerator = get_margin_call_price_numerator( maybe_mcfr );
337  return ratio_type( numerator, maximum_short_squeeze_ratio );
338  // Note: This ratio, if it multiplied margin_call_order_price, would yield the
339  // max_short_squeeze_price, apart perhaps for truncation (rounding) error.
340  }
341 
343  {
344  if( settlement_price.is_null() )
345  return price();
347  }
348 
349 // compile-time table of powers of 10 using template metaprogramming
350 
351 template< size_t N >
352 struct p10
353 {
354  static constexpr int64_t v = 10 * p10<N-1>::v;
355 };
356 
357 template<>
358 struct p10<0>
359 {
360  static constexpr int64_t v = 1;
361 };
362 
364 {
365  FC_ASSERT( precision < 19 );
366  static constexpr std::array<int64_t, 19> scaled_precision_lut =
367  {
373  };
374 
375  return scaled_precision_lut[ precision ];
376 }
377 
378 } } // graphene::protocol
379 
GRAPHENE_MAX_SHARE_SUPPLY
constexpr int64_t GRAPHENE_MAX_SHARE_SUPPLY(1000000000000000LL)
graphene::protocol::operator<
bool operator<(const price &a, const price &b)
Definition: asset.cpp:45
FC_CAPTURE_AND_RETHROW
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:479
GRAPHENE_COLLATERAL_RATIO_DENOM
#define GRAPHENE_COLLATERAL_RATIO_DENOM
Definition: config.hpp:113
uint128.hpp
graphene::protocol::price_feed::core_exchange_rate
price core_exchange_rate
Price at which automatically exchanging this asset for CORE from fee pool occurs (used for paying fee...
Definition: asset.hpp:183
graphene::protocol::price
The price struct stores asset prices in the BitShares system.
Definition: asset.hpp:108
GRAPHENE_MIN_COLLATERAL_RATIO
#define GRAPHENE_MIN_COLLATERAL_RATIO
lower than this could result in divide by 0
Definition: config.hpp:114
graphene::protocol::asset::scaled_precision
static share_type scaled_precision(uint8_t precision)
Definition: asset.cpp:363
graphene::protocol::price_feed::maintenance_collateralization
price maintenance_collateralization() const
Definition: asset.cpp:342
graphene::protocol::operator/
price operator/(const asset &base, const asset &quote)
Definition: asset.cpp:100
graphene::protocol::price_feed::settlement_price
price settlement_price
Definition: asset.hpp:180
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION
#define GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION(type)
Definition: types.hpp:86
graphene::protocol::asset::asset
asset(share_type a=0, asset_id_type id=asset_id_type())
Definition: asset.hpp:33
graphene::protocol::price_feed::max_short_squeeze_price
price max_short_squeeze_price() const
Definition: asset.cpp:300
graphene::protocol::price_feed::validate
void validate() const
Definition: asset.cpp:251
graphene::protocol::price::call_price
static price call_price(const asset &debt, const asset &collateral, uint16_t collateral_ratio)
Definition: asset.cpp:216
graphene::protocol::asset::multiply_and_round_up
asset multiply_and_round_up(const price &p) const
Multiply and round up.
Definition: asset.cpp:77
fc::optional::valid
bool valid() const
Definition: optional.hpp:186
graphene::protocol::asset::asset_id
asset_id_type asset_id
Definition: asset.hpp:37
graphene::protocol::price_feed::maximum_short_squeeze_ratio
uint16_t maximum_short_squeeze_ratio
Definition: asset.hpp:189
graphene::protocol::price::is_null
bool is_null() const
Definition: asset.cpp:229
graphene::protocol::price_feed
defines market parameters for margin positions
Definition: asset.hpp:160
graphene::protocol::operator==
bool operator==(const price &a, const price &b)
Definition: asset.cpp:34
graphene::protocol::share_type
safe< int64_t > share_type
Definition: types.hpp:309
graphene::protocol::p10
Definition: asset.cpp:352
FC_ASSERT
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
graphene::protocol::price::validate
void validate(bool check_upper_bound=false) const
Check if the object is valid.
Definition: asset.cpp:235
graphene::protocol::asset::amount
share_type amount
Definition: asset.hpp:36
graphene::protocol::price_feed::maintenance_collateral_ratio
uint16_t maintenance_collateral_ratio
Definition: asset.hpp:186
graphene::protocol::price::max
price max() const
Definition: asset.hpp:124
graphene::protocol::p10::v
static constexpr int64_t v
Definition: asset.cpp:354
graphene::protocol::price::base
asset base
Definition: asset.hpp:113
graphene::protocol::price_feed::margin_call_pays_ratio
ratio_type margin_call_pays_ratio(const fc::optional< uint16_t > &margin_call_fee_ratio) const
Definition: asset.cpp:334
graphene::protocol::price::min
price min() const
Definition: asset.hpp:125
graphene::protocol::price_feed::max_short_squeeze_price_before_hf_1270
price max_short_squeeze_price_before_hf_1270() const
Definition: asset.cpp:282
GRAPHENE_MAX_COLLATERAL_RATIO
#define GRAPHENE_MAX_COLLATERAL_RATIO
higher than this is unnecessary and may exceed int16 storage
Definition: config.hpp:115
fc::optional< uint16_t >
FC_THROW_EXCEPTION
#define FC_THROW_EXCEPTION(EXCEPTION, FORMAT,...)
Definition: exception.hpp:379
graphene::protocol::asset
Definition: asset.hpp:31
graphene::protocol::price_feed::is_for
bool is_for(asset_id_type asset_id) const
Definition: asset.cpp:266
fc::safe::value
T value
Definition: safe.hpp:28
asset.hpp
graphene::protocol::price_feed::margin_call_order_ratio
ratio_type margin_call_order_ratio(const fc::optional< uint16_t > &margin_call_fee_ratio) const
Definition: asset.cpp:326
graphene::protocol::ratio_type
boost::rational< int32_t > ratio_type
Definition: types.hpp:150
graphene::protocol::price::quote
asset quote
Definition: asset.hpp:114
graphene
Definition: api.cpp:48
graphene::protocol::price_feed::margin_call_order_price
price margin_call_order_price(const fc::optional< uint16_t > &margin_call_fee_ratio) const
Definition: asset.cpp:308
graphene::protocol::operator*
asset operator*(const asset &a, const price &b)
Multiply and round down.
Definition: asset.cpp:58
raw.hpp
fc::safe
Definition: safe.hpp:26
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy