BitShares-Core  7.0.2
BitShares blockchain node software and command-line wallet 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
84  // maintenance collateral ratio.
85  // Although this should be guaranteed by the caller of this function, we still check here to be defensive.
86  // Theoretically this check can be skipped for better performance.
87  //
88  // Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization().
89  if( ( !after_core_hardfork_1270 && call_price > feed_price )
90  || ( after_core_hardfork_1270 && collateralization() > *maintenance_collateralization ) )
91  return 0;
92 
93  if( !target_collateral_ratio.valid() ) // target cr is not set
94  return debt;
95 
96  // use mcr if target cr is too small
97  uint16_t tcr = std::max( *target_collateral_ratio, maintenance_collateral_ratio );
98 
99  price target_collateralization = ( after_core_hardfork_1270 ?
100  feed_price * ratio_type( tcr, GRAPHENE_COLLATERAL_RATIO_DENOM ) :
101  price() );
102 
103  // be defensive here, make sure match_price is in collateral / debt format
104  if( match_price.base.asset_id != call_price.base.asset_id )
105  match_price = ~match_price;
106 
108  && match_price.quote.asset_id == call_price.quote.asset_id );
109 
110  using i256 = boost::multiprecision::int256_t;
111  i256 mp_debt_amt = match_price.quote.amount.value;
112  i256 mp_coll_amt = match_price.base.amount.value;
113  i256 fp_debt_amt = feed_price.quote.amount.value;
114  i256 fp_coll_amt = feed_price.base.amount.value;
115 
116  // firstly we calculate without the fraction (x), the result could be a bit too small
117  i256 numerator = fp_coll_amt * mp_debt_amt * debt.value * tcr
118  - fp_debt_amt * mp_debt_amt * collateral.value * GRAPHENE_COLLATERAL_RATIO_DENOM;
119  if( numerator < 0 ) // feed protected, actually should not be true here, just check to be safe
120  return 0;
121 
122  i256 denominator = fp_coll_amt * mp_debt_amt * tcr - fp_debt_amt * mp_coll_amt * GRAPHENE_COLLATERAL_RATIO_DENOM;
123  if( denominator <= 0 ) // black swan
124  return debt;
125 
126  // note: if add 1 here, will result in 1.5x imperfection rate;
127  // however, due to rounding, the result could still be a bit too big, thus imperfect.
128  i256 to_cover_i256 = ( numerator / denominator );
129  if( to_cover_i256 >= debt.value ) // avoid possible overflow
130  return debt;
131  share_type to_cover_amt = static_cast< int64_t >( to_cover_i256 );
132 
133  // stabilize
134  // note: rounding up-down results in 3x imperfection rate in comparison to down-down-up
135  asset to_pay = asset( to_cover_amt, debt_type() ) * match_price;
136  asset to_cover = to_pay * match_price;
137  to_pay = to_cover.multiply_and_round_up( match_price );
138 
139  if( to_cover.amount >= debt || to_pay.amount >= collateral ) // to be safe
140  return debt;
141  FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );
142 
143  // Check whether the collateral ratio after filled is high enough
144  // Before core-1270 hard fork, we check with call_price; afterwards, we check with collateralization().
145  std::function<bool()> result_is_good = after_core_hardfork_1270 ?
146  std::function<bool()>( [this,&to_cover,&to_pay,target_collateralization]() -> bool
147  {
148  price new_collateralization = ( get_collateral() - to_pay ) / ( get_debt() - to_cover );
149  return ( new_collateralization > target_collateralization );
150  }) :
151  std::function<bool()>( [this,&to_cover,&to_pay,tcr,feed_price]() -> bool
152  {
153  price new_call_price = price::call_price( get_debt() - to_cover, get_collateral() - to_pay, tcr );
154  return ( new_call_price > feed_price );
155  });
156 
157  // if the result is good, we return.
158  if( result_is_good() )
159  return to_cover.amount;
160 
161  // be here, to_cover is too small due to rounding. deal with the fraction
162  numerator += fp_coll_amt * mp_debt_amt * tcr; // plus the fraction
163  to_cover_i256 = ( numerator / denominator ) + 1;
164  if( to_cover_i256 >= debt.value ) // avoid possible overflow
165  to_cover_i256 = debt.value;
166  to_cover_amt = static_cast< int64_t >( to_cover_i256 );
167 
168  asset max_to_pay = ( ( to_cover_amt == debt.value ) ? get_collateral()
169  : asset( to_cover_amt, debt_type() ).multiply_and_round_up( match_price ) );
170  if( max_to_pay.amount > collateral )
171  max_to_pay.amount = collateral;
172 
173  asset max_to_cover = ( ( max_to_pay.amount == collateral ) ? get_debt() : ( max_to_pay * match_price ) );
174  if( max_to_cover.amount >= debt ) // to be safe
175  {
176  max_to_pay.amount = collateral;
177  max_to_cover.amount = debt;
178  }
179 
180  if( max_to_pay <= to_pay || max_to_cover <= to_cover ) // strange data. should skip binary search and go on,
181  // but doesn't help much
182  return debt;
183  FC_ASSERT( max_to_pay > to_pay && max_to_cover > to_cover );
184 
185  asset min_to_pay = to_pay;
186  asset min_to_cover = to_cover;
187 
188  // try with binary search to find a good value
189  // note: actually binary search can not always provide perfect result here,
190  // due to rounding, collateral ratio is not always increasing while to_pay or to_cover is increasing
191  bool max_is_ok = false;
192  while( true )
193  {
194  // get the mean
195  if( match_price.base.amount < match_price.quote.amount ) // step of collateral is smaller
196  {
197  to_pay.amount = ( min_to_pay.amount + max_to_pay.amount + 1 ) / 2; // should not overflow. round up here
198  if( to_pay.amount == max_to_pay.amount )
199  to_cover.amount = max_to_cover.amount;
200  else
201  {
202  to_cover = to_pay * match_price;
203  if( to_cover.amount >= max_to_cover.amount ) // can be true when max_is_ok is false
204  {
205  to_pay.amount = max_to_pay.amount;
206  to_cover.amount = max_to_cover.amount;
207  }
208  else
209  {
210  to_pay = to_cover.multiply_and_round_up( match_price ); // stabilization, no change or become smaller
211  FC_ASSERT( to_pay.amount < max_to_pay.amount );
212  }
213  }
214  }
215  else // step of debt is smaller or equal
216  {
217  to_cover.amount = ( min_to_cover.amount + max_to_cover.amount ) / 2; // should not overflow. round down here
218  if( to_cover.amount == max_to_cover.amount )
219  to_pay.amount = max_to_pay.amount;
220  else
221  {
222  to_pay = to_cover.multiply_and_round_up( match_price );
223  if( to_pay.amount >= max_to_pay.amount ) // can be true when max_is_ok is false
224  {
225  to_pay.amount = max_to_pay.amount;
226  to_cover.amount = max_to_cover.amount;
227  }
228  else
229  {
230  to_cover = to_pay * match_price; // stabilization, to_cover should have increased
231  if( to_cover.amount >= max_to_cover.amount ) // to be safe
232  {
233  to_pay.amount = max_to_pay.amount;
234  to_cover.amount = max_to_cover.amount;
235  }
236  }
237  }
238  }
239 
240  // check again to see if we've moved away from the minimums, if not, use the maximums directly
241  if( to_pay.amount <= min_to_pay.amount || to_cover.amount <= min_to_cover.amount
242  || to_pay.amount > max_to_pay.amount || to_cover.amount > max_to_cover.amount )
243  {
244  to_pay.amount = max_to_pay.amount;
245  to_cover.amount = max_to_cover.amount;
246  }
247 
248  // check the mean
249  if( to_pay.amount == max_to_pay.amount && ( max_is_ok || to_pay.amount == collateral ) )
250  return to_cover.amount;
251  FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );
252 
253  // Check whether the result is good
254  if( result_is_good() ) // good
255  {
256  if( to_pay.amount == max_to_pay.amount )
257  return to_cover.amount;
258  max_to_pay.amount = to_pay.amount;
259  max_to_cover.amount = to_cover.amount;
260  max_is_ok = true;
261  }
262  else // not good
263  {
264  if( to_pay.amount == max_to_pay.amount )
265  break;
266  min_to_pay.amount = to_pay.amount;
267  min_to_cover.amount = to_cover.amount;
268  }
269  }
270 
271  // be here, max_to_cover is too small due to rounding. search forward
272  for( uint64_t d1 = 0, d2 = 1, d3 = 1; ; d1 = d2, d2 = d3, d3 = d1 + d2 ) // 1,1,2,3,5,8,...
273  {
274  if( match_price.base.amount > match_price.quote.amount ) // step of debt is smaller
275  {
276  to_pay.amount += d2;
277  if( to_pay.amount >= collateral )
278  return debt;
279  to_cover = to_pay * match_price;
280  if( to_cover.amount >= debt )
281  return debt;
282  to_pay = to_cover.multiply_and_round_up( match_price ); // stabilization
283  if( to_pay.amount >= collateral )
284  return debt;
285  }
286  else // step of collateral is smaller or equal
287  {
288  to_cover.amount += d2;
289  if( to_cover.amount >= debt )
290  return debt;
291  to_pay = to_cover.multiply_and_round_up( match_price );
292  if( to_pay.amount >= collateral )
293  return debt;
294  to_cover = to_pay * match_price; // stabilization
295  if( to_cover.amount >= debt )
296  return debt;
297  }
298 
299  // defensive check
300  FC_ASSERT( to_pay.amount < collateral && to_cover.amount < debt );
301 
302  // Check whether the result is good
303  if( result_is_good() ) // good
304  return to_cover.amount;
305  }
306 
307 } FC_CAPTURE_AND_RETHROW( (*this)(feed_price)(match_price)(maintenance_collateral_ratio) ) }
308 
311  (expiration)(seller)(for_sale)(sell_price)(filled_amount)(deferred_fee)(deferred_paid_fee)
312  (is_settled_debt)(on_fill)(take_profit_order_id)
313  )
314 
316  (borrower)(collateral)(debt)(call_price)(target_collateral_ratio) )
317 
320  (owner)(balance)(settlement_date)
321  )
322 
324  (bidder)(inv_swan_price) )
325 
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
graphene::chain::call_order_object::debt
share_type debt
call_price.quote.asset_id, access via get_debt
Definition: market_object.hpp:152
graphene::protocol::price
The price struct stores asset prices in the BitShares system.
Definition: asset.hpp:108
graphene::chain::call_order_object::get_collateral
asset get_collateral() const
Definition: market_object.hpp:143
graphene::chain::call_order_object::collateralization
price collateralization() const
Definition: market_object.hpp:148
graphene::chain::call_order_object::get_debt
asset get_debt() const
Definition: market_object.hpp:144
graphene::chain::call_order_object::collateral
share_type collateral
call_price.base.asset_id, access via get_collateral
Definition: market_object.hpp:151
GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION
#define GRAPHENE_IMPLEMENT_EXTERNAL_SERIALIZATION(type)
Definition: types.hpp:86
graphene::chain::call_order_object::debt_type
asset_id_type debt_type() const
Definition: market_object.hpp:146
graphene::chain::call_order_object::target_collateral_ratio
optional< uint16_t > target_collateral_ratio
maximum CR to maintain when selling collateral on margin call
Definition: market_object.hpp:155
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
FC_REFLECT_DERIVED_NO_TYPENAME
#define FC_REFLECT_DERIVED_NO_TYPENAME(TYPE, INHERITS, MEMBERS)
Definition: reflect.hpp:357
graphene::chain::collateral_bid_object
bids of collateral for debt after a black swan
Definition: market_object.hpp:206
graphene::chain::call_order_object::get_max_debt_to_cover
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
Definition: market_object.cpp:61
graphene::chain::call_order_object::call_price
price call_price
Collateral / Debt.
Definition: market_object.hpp:153
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::asset::amount
share_type amount
Definition: asset.hpp:36
graphene::chain::force_settlement_object
tracks bitassets scheduled for force settlement at some point in the future.
Definition: market_object.hpp:187
graphene::chain::call_order_object
tracks debt and call price information
Definition: market_object.hpp:140
graphene::protocol::price::base
asset base
Definition: asset.hpp:113
fc::optional
provides stack-based nullable value similar to boost::optional
Definition: optional.hpp:20
graphene::protocol::asset
Definition: asset.hpp:31
market_object.hpp
fc::safe::value
T value
Definition: safe.hpp:28
graphene::chain::limit_order_object
an offer to sell an amount of an asset at a specified exchange rate by a certain time
Definition: market_object.hpp:45
graphene::protocol::ratio_type
boost::rational< int32_t > ratio_type
Definition: types.hpp:150
graphene::chain
Definition: util.hpp:32
graphene::protocol::price::quote
asset quote
Definition: asset.hpp:114
graphene::db::object
base for all database objects
Definition: object.hpp:61
raw.hpp
fc::safe
Definition: safe.hpp:26