BitShares-Core  4.0.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  using fc::int128_t;
34 
35  bool operator == ( const price& a, const price& b )
36  {
37  if( std::tie( a.base.asset_id, a.quote.asset_id ) != std::tie( b.base.asset_id, b.quote.asset_id ) )
38  return false;
39 
40  const auto amult = uint128_t( b.quote.amount.value ) * a.base.amount.value;
41  const auto bmult = uint128_t( a.quote.amount.value ) * b.base.amount.value;
42 
43  return amult == bmult;
44  }
45 
46  bool operator < ( const price& a, const price& b )
47  {
48  if( a.base.asset_id < b.base.asset_id ) return true;
49  if( a.base.asset_id > b.base.asset_id ) return false;
50  if( a.quote.asset_id < b.quote.asset_id ) return true;
51  if( a.quote.asset_id > b.quote.asset_id ) return false;
52 
53  const auto amult = uint128_t( b.quote.amount.value ) * a.base.amount.value;
54  const auto bmult = uint128_t( a.quote.amount.value ) * b.base.amount.value;
55 
56  return amult < bmult;
57  }
58 
59  asset operator * ( const asset& a, const price& b )
60  {
61  if( a.asset_id == b.base.asset_id )
62  {
63  FC_ASSERT( b.base.amount.value > 0 );
64  uint128_t result = (uint128_t(a.amount.value) * b.quote.amount.value)/b.base.amount.value;
66  return asset( static_cast<int64_t>(result), b.quote.asset_id );
67  }
68  else if( a.asset_id == b.quote.asset_id )
69  {
70  FC_ASSERT( b.quote.amount.value > 0 );
71  uint128_t result = (uint128_t(a.amount.value) * b.base.amount.value)/b.quote.amount.value;
73  return asset( static_cast<int64_t>(result), b.base.asset_id );
74  }
75  FC_THROW_EXCEPTION( fc::assert_exception, "invalid asset * price", ("asset",a)("price",b) );
76  }
77 
79  {
80  const asset& a = *this;
81  if( a.asset_id == b.base.asset_id )
82  {
83  FC_ASSERT( b.base.amount.value > 0 );
84  uint128_t result = (uint128_t(a.amount.value) * b.quote.amount.value + b.base.amount.value - 1)/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)/b.quote.amount.value;
93  return asset( static_cast<int64_t>(result), b.base.asset_id );
94  }
95  FC_THROW_EXCEPTION( fc::assert_exception, "invalid asset::multiply_and_round_up(price)", ("asset",a)("price",b) );
96  }
97 
98  price operator / ( const asset& base, const asset& quote )
99  { try {
100  FC_ASSERT( base.asset_id != quote.asset_id );
101  return price{base,quote};
102  } FC_CAPTURE_AND_RETHROW( (base)(quote) ) }
103 
104  price price::max( asset_id_type base, asset_id_type quote ) { return asset( share_type(GRAPHENE_MAX_SHARE_SUPPLY), base ) / asset( share_type(1), quote); }
105  price price::min( asset_id_type base, asset_id_type quote ) { return asset( 1, base ) / asset( GRAPHENE_MAX_SHARE_SUPPLY, quote); }
106 
107  price operator * ( const price& p, const ratio_type& r )
108  { try {
109  p.validate();
110 
111  FC_ASSERT( r.numerator() > 0 && r.denominator() > 0 );
112 
113  if( r.numerator() == r.denominator() ) return p;
114 
115  boost::rational<int128_t> p128( p.base.amount.value, p.quote.amount.value );
116  boost::rational<int128_t> r128( r.numerator(), r.denominator() );
117  auto cp = p128 * r128;
118  auto ocp = cp;
119 
120  bool shrinked = false;
121  bool using_max = false;
122  static const int128_t max( GRAPHENE_MAX_SHARE_SUPPLY );
123  while( cp.numerator() > max || cp.denominator() > max )
124  {
125  if( cp.numerator() == 1 )
126  {
127  cp = boost::rational<int128_t>( 1, max );
128  using_max = true;
129  break;
130  }
131  else if( cp.denominator() == 1 )
132  {
133  cp = boost::rational<int128_t>( max, 1 );
134  using_max = true;
135  break;
136  }
137  else
138  {
139  cp = boost::rational<int128_t>( cp.numerator() >> 1, cp.denominator() >> 1 );
140  shrinked = true;
141  }
142  }
143  if( shrinked ) // maybe not accurate enough due to rounding, do additional checks here
144  {
145  int128_t num = ocp.numerator();
146  int128_t den = ocp.denominator();
147  if( num > den )
148  {
149  num /= den;
150  if( num > max )
151  num = max;
152  den = 1;
153  }
154  else
155  {
156  den /= num;
157  if( den > max )
158  den = max;
159  num = 1;
160  }
161  boost::rational<int128_t> ncp( num, den );
162  if( num == max || den == max ) // it's on the edge, we know it's accurate enough
163  cp = ncp;
164  else
165  {
166  // from the accurate ocp, now we have ncp and cp. use the one which is closer to ocp.
167  // TODO improve performance
168  auto diff1 = abs( ncp - ocp );
169  auto diff2 = abs( cp - ocp );
170  if( diff1 < diff2 ) cp = ncp;
171  }
172  }
173 
174  price np = asset( static_cast<int64_t>(cp.numerator()), p.base.asset_id )
175  / asset( static_cast<int64_t>(cp.denominator()), p.quote.asset_id );
176 
177  if( shrinked || using_max )
178  {
179  if( ( r.numerator() > r.denominator() && np < p )
180  || ( r.numerator() < r.denominator() && np > p ) )
181  // even with an accurate result, if p is out of valid range, return it
182  np = p;
183  }
184 
185  np.validate();
186  return np;
187  } FC_CAPTURE_AND_RETHROW( (p)(r.numerator())(r.denominator()) ) }
188 
189  price operator / ( const price& p, const ratio_type& r )
190  { try {
191  return p * ratio_type( r.denominator(), r.numerator() );
192  } FC_CAPTURE_AND_RETHROW( (p)(r.numerator())(r.denominator()) ) }
193 
212  price price::call_price( const asset& debt, const asset& collateral, uint16_t collateral_ratio)
213  { try {
214  boost::rational<int128_t> swan(debt.amount.value,collateral.amount.value);
215  boost::rational<int128_t> ratio( collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );
216  auto cp = swan * ratio;
217 
218  while( cp.numerator() > GRAPHENE_MAX_SHARE_SUPPLY || cp.denominator() > GRAPHENE_MAX_SHARE_SUPPLY )
219  cp = boost::rational<int128_t>( (cp.numerator() >> 1)+1, (cp.denominator() >> 1)+1 );
220 
221  return ( asset( static_cast<int64_t>(cp.denominator()), collateral.asset_id )
222  / asset( static_cast<int64_t>(cp.numerator()), debt.asset_id ) );
223  } FC_CAPTURE_AND_RETHROW( (debt)(collateral)(collateral_ratio) ) }
224 
225  bool price::is_null() const
226  {
227  // Effectively same as "return *this == price();" but perhaps faster
228  return ( base.asset_id == asset_id_type() && quote.asset_id == asset_id_type() );
229  }
230 
231  void price::validate() const
232  { try {
233  FC_ASSERT( base.amount > share_type(0) );
234  FC_ASSERT( quote.amount > share_type(0) );
235  FC_ASSERT( base.asset_id != quote.asset_id );
236  } FC_CAPTURE_AND_RETHROW( (base)(quote) ) }
237 
238  void price_feed::validate() const
239  { try {
240  if( !settlement_price.is_null() )
241  settlement_price.validate();
242  FC_ASSERT( maximum_short_squeeze_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO );
243  FC_ASSERT( maximum_short_squeeze_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO );
244  FC_ASSERT( maintenance_collateral_ratio >= GRAPHENE_MIN_COLLATERAL_RATIO );
245  FC_ASSERT( maintenance_collateral_ratio <= GRAPHENE_MAX_COLLATERAL_RATIO );
246  // Note: there was code here calling `max_short_squeeze_price();` before core-1270 hard fork,
247  // in order to make sure that it doesn't overflow,
248  // but the code doesn't actually check overflow, and it won't overflow, so the code is removed.
249 
250  // Note: not checking `maintenance_collateral_ratio >= maximum_short_squeeze_ratio` since launch
251  } FC_CAPTURE_AND_RETHROW( (*this) ) }
252 
253  bool price_feed::is_for( asset_id_type asset_id ) const
254  {
255  try
256  {
257  if( !settlement_price.is_null() )
258  return (settlement_price.base.asset_id == asset_id);
259  if( !core_exchange_rate.is_null() )
260  return (core_exchange_rate.base.asset_id == asset_id);
261  // (null, null) is valid for any feed
262  return true;
263  }
264  FC_CAPTURE_AND_RETHROW( (*this) )
265  }
266 
267  // This function is kept here due to potential different behavior in edge cases.
268  // TODO check after core-1270 hard fork to see if we can safely remove it
270  {
271  // settlement price is in debt/collateral
272  boost::rational<int128_t> sp( settlement_price.base.amount.value, settlement_price.quote.amount.value );
273  boost::rational<int128_t> ratio( GRAPHENE_COLLATERAL_RATIO_DENOM, maximum_short_squeeze_ratio );
274  auto cp = sp * ratio;
275 
276  while( cp.numerator() > GRAPHENE_MAX_SHARE_SUPPLY || cp.denominator() > GRAPHENE_MAX_SHARE_SUPPLY )
277  cp = boost::rational<int128_t>( (cp.numerator() >> 1)+(cp.numerator()&1),
278  (cp.denominator() >> 1)+(cp.denominator()&1) );
279 
280  return ( asset( static_cast<int64_t>(cp.numerator()), settlement_price.base.asset_id )
281  / asset( static_cast<int64_t>(cp.denominator()), settlement_price.quote.asset_id ) );
282  }
283 
284 
285  // Documentation in header.
286  // Calculation: MSSP = settlement_price / MSSR
288  {
289  // settlement price is in debt/collateral
290  return settlement_price * ratio_type( GRAPHENE_COLLATERAL_RATIO_DENOM, maximum_short_squeeze_ratio );
291  }
292 
293  // Documentation in header.
294  // Calculation: MCOP = settlement_price / (MSSR - MCFR); result is in debt/collateral
296  {
297  const uint16_t mcfr = maybe_mcfr.valid() ? *maybe_mcfr : 0;
298  uint16_t numerator = (mcfr < maximum_short_squeeze_ratio) ?
299  (maximum_short_squeeze_ratio - mcfr) : GRAPHENE_COLLATERAL_RATIO_DENOM; // won't underflow
300  if (numerator < GRAPHENE_COLLATERAL_RATIO_DENOM)
301  numerator = GRAPHENE_COLLATERAL_RATIO_DENOM; // floor at 1.00
302  return settlement_price * ratio_type( GRAPHENE_COLLATERAL_RATIO_DENOM, numerator );
303  }
304 
305  // Reason for this function is explained in header.
306  // Calculation: (MSSR - MCFR) / MSSR
308  {
309  if (!maybe_mcfr.valid())
310  return ratio_type(1,1);
311  const uint16_t mcfr = *maybe_mcfr;
312  uint16_t numerator = (mcfr < maximum_short_squeeze_ratio) ?
313  (maximum_short_squeeze_ratio - mcfr) : GRAPHENE_COLLATERAL_RATIO_DENOM; // won't underflow
314  if (numerator < GRAPHENE_COLLATERAL_RATIO_DENOM)
315  numerator = GRAPHENE_COLLATERAL_RATIO_DENOM; // floor at 1.00
316  return ratio_type( numerator, maximum_short_squeeze_ratio );
317  // Note: This ratio, if it multiplied margin_call_order_price, would yield the
318  // max_short_squeeze_price, apart perhaps for truncation (rounding) error.
319  }
320 
322  {
323  if( settlement_price.is_null() )
324  return price();
325  return ~settlement_price * ratio_type( maintenance_collateral_ratio, GRAPHENE_COLLATERAL_RATIO_DENOM );
326  }
327 
328 // compile-time table of powers of 10 using template metaprogramming
329 
330 template< int N >
331 struct p10
332 {
333  static const int64_t v = 10 * p10<N-1>::v;
334 };
335 
336 template<>
337 struct p10<0>
338 {
339  static const int64_t v = 1;
340 };
341 
342 const int64_t scaled_precision_lut[19] =
343 {
349 };
350 
351 } } // graphene::protocol
352 
#define GRAPHENE_COLLATERAL_RATIO_DENOM
Definition: config.hpp:113
void validate() const
Definition: asset.cpp:231
asset operator*(const asset &a, const price &b)
Multiply and round down.
Definition: asset.cpp:59
boost::rational< int32_t > ratio_type
Definition: types.hpp:133
Definition: api.cpp:56
bool valid() const
Definition: optional.hpp:186
price max_short_squeeze_price() const
Definition: asset.cpp:287
ratio_type margin_call_pays_ratio(const fc::optional< uint16_t > margin_call_fee_ratio) const
Definition: asset.cpp:307
price operator/(const asset &base, const asset &quote)
Definition: asset.cpp:98
bool is_null() const
Definition: asset.cpp:225
#define GRAPHENE_MAX_SHARE_SUPPLY
Definition: config.hpp:38
bool operator<(const price &a, const price &b)
Definition: asset.cpp:46
price min() const
Definition: asset.hpp:131
price maintenance_collateralization() const
Definition: asset.cpp:321
#define GRAPHENE_MAX_COLLATERAL_RATIO
higher than this is unnecessary and may exceed int16 storage
Definition: config.hpp:115
The price struct stores asset prices in the BitShares system.
Definition: asset.hpp:114
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:478
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
const int64_t scaled_precision_lut[19]
Definition: asset.cpp:342
price margin_call_order_price(const fc::optional< uint16_t > margin_call_fee_ratio) const
Definition: asset.cpp:295
asset(share_type a=0, asset_id_type id=asset_id_type())
Definition: asset.hpp:35
#define GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION(type)
Definition: types.hpp:74
#define FC_THROW_EXCEPTION(EXCEPTION, FORMAT,...)
Definition: exception.hpp:378
price max_short_squeeze_price_before_hf_1270() const
Definition: asset.cpp:269
#define GRAPHENE_MIN_COLLATERAL_RATIO
lower than this could result in divide by 0
Definition: config.hpp:114
bool is_for(asset_id_type asset_id) const
Definition: asset.cpp:253
defines market parameters for margin positions
Definition: asset.hpp:164
safe< int64_t > share_type
Definition: types.hpp:247
asset_id_type asset_id
Definition: asset.hpp:39
price max() const
Definition: asset.hpp:130
static price call_price(const asset &debt, const asset &collateral, uint16_t collateral_ratio)
Definition: asset.cpp:212
T value
Definition: safe.hpp:22
bool operator==(const price &a, const price &b)
Definition: asset.cpp:35
asset multiply_and_round_up(const price &p) const
Multiply and round up.
Definition: asset.cpp:78