BitShares-Core  4.0.0
BitShares blockchain implementation and command-line interface software
market_object.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018 Abit More, 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 
26 #include <boost/multiprecision/cpp_int.hpp>
27 
28 #include <functional>
29 
30 #include <fc/io/raw.hpp>
31 
32 using namespace graphene::chain;
33 
34 /*
35 target_CR = max( target_CR, MCR )
36 
37 target_CR = new_collateral / ( new_debt / feed_price )
38  = ( collateral - max_amount_to_sell ) * feed_price
39  / ( debt - amount_to_get )
40  = ( collateral - max_amount_to_sell ) * feed_price
41  / ( debt - round_down(max_amount_to_sell * match_price ) )
42  = ( collateral - max_amount_to_sell ) * feed_price
43  / ( debt - (max_amount_to_sell * match_price - x) )
44 
45 Note: x is the fraction, 0 <= x < 1
46 
47 =>
48 
49 max_amount_to_sell = ( (debt + x) * target_CR - collateral * feed_price )
50  / (target_CR * match_price - feed_price)
51  = ( (debt + x) * tCR / DENOM - collateral * fp_debt_amt / fp_coll_amt )
52  / ( (tCR / DENOM) * (mp_debt_amt / mp_coll_amt) - fp_debt_amt / fp_coll_amt )
53  = ( (debt + x) * tCR * fp_coll_amt * mp_coll_amt - collateral * fp_debt_amt * DENOM * mp_coll_amt)
54  / ( tCR * mp_debt_amt * fp_coll_amt - fp_debt_amt * DENOM * mp_coll_amt )
55 
56 max_debt_to_cover = max_amount_to_sell * match_price
57  = max_amount_to_sell * mp_debt_amt / mp_coll_amt
58  = ( (debt + x) * tCR * fp_coll_amt * mp_debt_amt - collateral * fp_debt_amt * DENOM * mp_debt_amt)
59  / (tCR * mp_debt_amt * fp_coll_amt - fp_debt_amt * DENOM * mp_coll_amt)
60 */
62  price feed_price,
63  const uint16_t maintenance_collateral_ratio,
64  const optional<price>& maintenance_collateralization )const
65 { try {
66  // be defensive here, make sure feed_price is in collateral / debt format
67  if( feed_price.base.asset_id != call_price.base.asset_id )
68  feed_price = ~feed_price;
69 
71  && feed_price.quote.asset_id == call_price.quote.asset_id );
72 
73  bool after_core_hardfork_1270 = maintenance_collateralization.valid();
74 
75  // be defensive here, make sure maintenance_collateralization is in collateral / debt format
76  if( after_core_hardfork_1270 )
77  {
78  FC_ASSERT( maintenance_collateralization->base.asset_id == call_price.base.asset_id
79  && maintenance_collateralization->quote.asset_id == call_price.quote.asset_id );
80  }
81 
82  // According to the feed protection rule (https://github.com/cryptonomex/graphene/issues/436),
83  // a call order should only be called when its collateral ratio is not higher than required maintenance collateral ratio.
84  // Although this should be guaranteed by the caller of this function, we still check here to be defensive.
85  // Theoretically this check can be skipped for better performance.
86  //
87  // Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization().
88  if( ( !after_core_hardfork_1270 && call_price > feed_price )
89  || ( after_core_hardfork_1270 && collateralization() > *maintenance_collateralization ) )
90  return 0;
91 
92  if( !target_collateral_ratio.valid() ) // target cr is not set
93  return debt;
94 
95  uint16_t tcr = std::max( *target_collateral_ratio, maintenance_collateral_ratio ); // use mcr if target cr is too small
96 
97  price target_collateralization = ( after_core_hardfork_1270 ?
98  feed_price * ratio_type( tcr, GRAPHENE_COLLATERAL_RATIO_DENOM ) :
99  price() );
100 
101  // be defensive here, make sure match_price is in collateral / debt format
102  if( match_price.base.asset_id != call_price.base.asset_id )
103  match_price = ~match_price;
104 
106  && match_price.quote.asset_id == call_price.quote.asset_id );
107 
108  typedef boost::multiprecision::int256_t i256;
109  i256 mp_debt_amt = match_price.quote.amount.value;
110  i256 mp_coll_amt = match_price.base.amount.value;
111  i256 fp_debt_amt = feed_price.quote.amount.value;
112  i256 fp_coll_amt = feed_price.base.amount.value;
113 
114  // firstly we calculate without the fraction (x), the result could be a bit too small
115  i256 numerator = fp_coll_amt * mp_debt_amt * debt.value * tcr
116  - fp_debt_amt * mp_debt_amt * collateral.value * GRAPHENE_COLLATERAL_RATIO_DENOM;
117  if( numerator < 0 ) // feed protected, actually should not be true here, just check to be safe
118  return 0;
119 
120  i256 denominator = fp_coll_amt * mp_debt_amt * tcr - fp_debt_amt * mp_coll_amt * GRAPHENE_COLLATERAL_RATIO_DENOM;
121  if( denominator <= 0 ) // black swan
122  return debt;
123 
124  // note: if add 1 here, will result in 1.5x imperfection rate;
125  // however, due to rounding, the result could still be a bit too big, thus imperfect.
126  i256 to_cover_i256 = ( numerator / denominator );
127  if( to_cover_i256 >= debt.value ) // avoid possible overflow
128  return debt;
129  share_type to_cover_amt = static_cast< int64_t >( to_cover_i256 );
130 
131  // stabilize
132  // note: rounding up-down results in 3x imperfection rate in comparison to down-down-up
133  asset to_pay = asset( to_cover_amt, debt_type() ) * match_price;
134  asset to_cover = to_pay * match_price;
135  to_pay = to_cover.multiply_and_round_up( match_price );
136 
137  if( to_cover.amount >= debt || to_pay.amount >= collateral ) // to be safe
138  return debt;
139  FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );
140 
141  // Check whether the collateral ratio after filled is high enough
142  // Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization().
143  std::function<bool()> result_is_good = after_core_hardfork_1270 ?
144  std::function<bool()>( [this,&to_cover,&to_pay,target_collateralization]() -> bool
145  {
146  price new_collateralization = ( get_collateral() - to_pay ) / ( get_debt() - to_cover );
147  return ( new_collateralization > target_collateralization );
148  }) :
149  std::function<bool()>( [this,&to_cover,&to_pay,tcr,feed_price]() -> bool
150  {
151  price new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr );
152  return ( new_call_price > feed_price );
153  });
154 
155  // if the result is good, we return.
156  if( result_is_good() )
157  return to_cover.amount;
158 
159  // be here, to_cover is too small due to rounding. deal with the fraction
160  numerator += fp_coll_amt * mp_debt_amt * tcr; // plus the fraction
161  to_cover_i256 = ( numerator / denominator ) + 1;
162  if( to_cover_i256 >= debt.value ) // avoid possible overflow
163  to_cover_i256 = debt.value;
164  to_cover_amt = static_cast< int64_t >( to_cover_i256 );
165 
166  asset max_to_pay = ( ( to_cover_amt == debt.value ) ? get_collateral()
167  : asset( to_cover_amt, debt_type() ).multiply_and_round_up( match_price ) );
168  if( max_to_pay.amount > collateral )
169  max_to_pay.amount = collateral;
170 
171  asset max_to_cover = ( ( max_to_pay.amount == collateral ) ? get_debt() : ( max_to_pay * match_price ) );
172  if( max_to_cover.amount >= debt ) // to be safe
173  {
174  max_to_pay.amount = collateral;
175  max_to_cover.amount = debt;
176  }
177 
178  if( max_to_pay <= to_pay || max_to_cover <= to_cover ) // strange data. should skip binary search and go on, but doesn't help much
179  return debt;
180  FC_ASSERT( max_to_pay > to_pay && max_to_cover > to_cover );
181 
182  asset min_to_pay = to_pay;
183  asset min_to_cover = to_cover;
184 
185  // try with binary search to find a good value
186  // note: actually binary search can not always provide perfect result here,
187  // due to rounding, collateral ratio is not always increasing while to_pay or to_cover is increasing
188  bool max_is_ok = false;
189  while( true )
190  {
191  // get the mean
192  if( match_price.base.amount < match_price.quote.amount ) // step of collateral is smaller
193  {
194  to_pay.amount = ( min_to_pay.amount + max_to_pay.amount + 1 ) / 2; // should not overflow. round up here
195  if( to_pay.amount == max_to_pay.amount )
196  to_cover.amount = max_to_cover.amount;
197  else
198  {
199  to_cover = to_pay * match_price;
200  if( to_cover.amount >= max_to_cover.amount ) // can be true when max_is_ok is false
201  {
202  to_pay.amount = max_to_pay.amount;
203  to_cover.amount = max_to_cover.amount;
204  }
205  else
206  {
207  to_pay = to_cover.multiply_and_round_up( match_price ); // stabilization, no change or become smaller
208  FC_ASSERT( to_pay.amount < max_to_pay.amount );
209  }
210  }
211  }
212  else // step of debt is smaller or equal
213  {
214  to_cover.amount = ( min_to_cover.amount + max_to_cover.amount ) / 2; // should not overflow. round down here
215  if( to_cover.amount == max_to_cover.amount )
216  to_pay.amount = max_to_pay.amount;
217  else
218  {
219  to_pay = to_cover.multiply_and_round_up( match_price );
220  if( to_pay.amount >= max_to_pay.amount ) // can be true when max_is_ok is false
221  {
222  to_pay.amount = max_to_pay.amount;
223  to_cover.amount = max_to_cover.amount;
224  }
225  else
226  {
227  to_cover = to_pay * match_price; // stabilization, to_cover should have increased
228  if( to_cover.amount >= max_to_cover.amount ) // to be safe
229  {
230  to_pay.amount = max_to_pay.amount;
231  to_cover.amount = max_to_cover.amount;
232  }
233  }
234  }
235  }
236 
237  // check again to see if we've moved away from the minimums, if not, use the maximums directly
238  if( to_pay.amount <= min_to_pay.amount || to_cover.amount <= min_to_cover.amount
239  || to_pay.amount > max_to_pay.amount || to_cover.amount > max_to_cover.amount )
240  {
241  to_pay.amount = max_to_pay.amount;
242  to_cover.amount = max_to_cover.amount;
243  }
244 
245  // check the mean
246  if( to_pay.amount == max_to_pay.amount && ( max_is_ok || to_pay.amount == collateral ) )
247  return to_cover.amount;
248  FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );
249 
250  // Check whether the result is good
251  if( result_is_good() ) // good
252  {
253  if( to_pay.amount == max_to_pay.amount )
254  return to_cover.amount;
255  max_to_pay.amount = to_pay.amount;
256  max_to_cover.amount = to_cover.amount;
257  max_is_ok = true;
258  }
259  else // not good
260  {
261  if( to_pay.amount == max_to_pay.amount )
262  break;
263  min_to_pay.amount = to_pay.amount;
264  min_to_cover.amount = to_cover.amount;
265  }
266  }
267 
268  // be here, max_to_cover is too small due to rounding. search forward
269  for( uint64_t d1 = 0, d2 = 1, d3 = 1; ; d1 = d2, d2 = d3, d3 = d1 + d2 ) // 1,1,2,3,5,8,...
270  {
271  if( match_price.base.amount > match_price.quote.amount ) // step of debt is smaller
272  {
273  to_pay.amount += d2;
274  if( to_pay.amount >= collateral )
275  return debt;
276  to_cover = to_pay * match_price;
277  if( to_cover.amount >= debt )
278  return debt;
279  to_pay = to_cover.multiply_and_round_up( match_price ); // stabilization
280  if( to_pay.amount >= collateral )
281  return debt;
282  }
283  else // step of collateral is smaller or equal
284  {
285  to_cover.amount += d2;
286  if( to_cover.amount >= debt )
287  return debt;
288  to_pay = to_cover.multiply_and_round_up( match_price );
289  if( to_pay.amount >= collateral )
290  return debt;
291  to_cover = to_pay * match_price; // stabilization
292  if( to_cover.amount >= debt )
293  return debt;
294  }
295 
296  // defensive check
297  FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );
298 
299  // Check whether the result is good
300  if( result_is_good() ) // good
301  return to_cover.amount;
302  }
303 
304 } FC_CAPTURE_AND_RETHROW( (*this)(feed_price)(match_price)(maintenance_collateral_ratio) ) }
305 
308  (expiration)(seller)(for_sale)(sell_price)(deferred_fee)(deferred_paid_fee)
309  )
310 
313 
316  (owner)(balance)(settlement_date)
317  )
318 
320  (bidder)(inv_swan_price) )
321 
#define GRAPHENE_COLLATERAL_RATIO_DENOM
Definition: config.hpp:113
share_type get_max_debt_to_cover(price match_price, price feed_price, const uint16_t maintenance_collateral_ratio, const optional< price > &maintenance_collateralization=optional< price >()) const
optional< uint16_t > target_collateral_ratio
maximum CR to maintain when selling collateral on margin call
boost::rational< int32_t > ratio_type
Definition: types.hpp:133
tracks debt and call price information
#define FC_REFLECT_DERIVED_NO_TYPENAME(TYPE, INHERITS, MEMBERS)
Definition: reflect.hpp:356
bool valid() const
Definition: optional.hpp:186
price call_price
Collateral / Debt.
share_type debt
call_price.quote.asset_id, access via get_debt
The price struct stores asset prices in the BitShares system.
Definition: asset.hpp:114
asset_id_type debt_type() const
tracks bitassets scheduled for force settlement at some point in the future.
#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
#define GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION(type)
Definition: types.hpp:74
bids of collateral for debt after a black swan
share_type collateral
call_price.base.asset_id, access via get_collateral
asset_id_type asset_id
Definition: asset.hpp:39
base for all database objects
Definition: object.hpp:62
static price call_price(const asset &debt, const asset &collateral, uint16_t collateral_ratio)
Definition: asset.cpp:212
T value
Definition: safe.hpp:22
an offer to sell a amount of a asset at a specified exchange rate by a certain timeThis limit_order_o...
asset multiply_and_round_up(const price &p) const
Multiply and round up.
Definition: asset.cpp:78