BitShares-Core  6.1.0
BitShares blockchain implementation and command-line interface 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 )
107  { return asset( share_type(GRAPHENE_MAX_SHARE_SUPPLY), base ) / asset( share_type(1), quote); }
108 
109  price price::min( asset_id_type base, asset_id_type quote )
110  { return asset( share_type(1), base ) / asset( share_type(GRAPHENE_MAX_SHARE_SUPPLY), 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  {
242  FC_ASSERT( base.amount.value <= GRAPHENE_MAX_SHARE_SUPPLY,
243  "Base amount should not be greater than ${max}",
244  ("max", GRAPHENE_MAX_SHARE_SUPPLY) );
245  FC_ASSERT( quote.amount.value <= GRAPHENE_MAX_SHARE_SUPPLY,
246  "Quote amount should not be greater than ${max}",
247  ("max", GRAPHENE_MAX_SHARE_SUPPLY) );
248  }
249  } FC_CAPTURE_AND_RETHROW( (base)(quote) ) }
250 
251  void price_feed::validate() const
252  { try {
253  if( !settlement_price.is_null() )
254  settlement_price.validate();
255  FC_ASSERT( maximum_short_squeeze_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO );
256  FC_ASSERT( maximum_short_squeeze_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO );
257  FC_ASSERT( maintenance_collateral_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO );
258  FC_ASSERT( maintenance_collateral_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO );
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
303  return settlement_price * ratio_type( GRAPHENE_COLLATERAL_RATIO_DENOM, maximum_short_squeeze_ratio );
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();
346  return ~settlement_price * ratio_type( maintenance_collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );
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 
#define GRAPHENE_COLLATERAL_RATIO_DENOM
Definition: config.hpp:113
asset operator*(const asset &a, const price &b)
Multiply and round down.
Definition: asset.cpp:58
boost::rational< int32_t > ratio_type
Definition: types.hpp:150
price min() const
Definition: asset.hpp:125
Definition: api.cpp:48
ratio_type margin_call_pays_ratio(const fc::optional< uint16_t > &margin_call_fee_ratio) const
Definition: asset.cpp:334
price max_short_squeeze_price_before_hf_1270() const
Definition: asset.cpp:282
ratio_type margin_call_order_ratio(const fc::optional< uint16_t > &margin_call_fee_ratio) const
Definition: asset.cpp:326
price operator/(const asset &base, const asset &quote)
Definition: asset.cpp:100
bool operator<(const price &a, const price &b)
Definition: asset.cpp:45
asset multiply_and_round_up(const price &p) const
Multiply and round up.
Definition: asset.cpp:77
price max() const
Definition: asset.hpp:124
static share_type scaled_precision(uint8_t precision)
Definition: asset.cpp:363
#define GRAPHENE_MAX_COLLATERAL_RATIO
higher than this is unnecessary and may exceed int16 storage
Definition: config.hpp:115
price margin_call_order_price(const fc::optional< uint16_t > &margin_call_fee_ratio) const
Definition: asset.cpp:308
The price struct stores asset prices in the BitShares system.
Definition: asset.hpp:108
bool valid() const
Definition: optional.hpp:186
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:479
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
asset(share_type a=0, asset_id_type id=asset_id_type())
Definition: asset.hpp:33
#define GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION(type)
Definition: types.hpp:86
#define FC_THROW_EXCEPTION(EXCEPTION, FORMAT,...)
Definition: exception.hpp:379
price maintenance_collateralization() const
Definition: asset.cpp:342
#define GRAPHENE_MIN_COLLATERAL_RATIO
lower than this could result in divide by 0
Definition: config.hpp:114
defines market parameters for margin positions
Definition: asset.hpp:160
safe< int64_t > share_type
Definition: types.hpp:309
asset_id_type asset_id
Definition: asset.hpp:37
void validate(bool check_upper_bound=false) const
Check if the object is valid.
Definition: asset.cpp:235
constexpr int64_t GRAPHENE_MAX_SHARE_SUPPLY(1000000000000000LL)
price max_short_squeeze_price() const
Definition: asset.cpp:300
bool is_null() const
Definition: asset.cpp:229
static price call_price(const asset &debt, const asset &collateral, uint16_t collateral_ratio)
Definition: asset.cpp:216
bool is_for(asset_id_type asset_id) const
Definition: asset.cpp:266
T value
Definition: safe.hpp:22
bool operator==(const price &a, const price &b)
Definition: asset.cpp:34