-
Notifications
You must be signed in to change notification settings - Fork 649
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BSIP31-34 market engine improvements #829
Changes from 25 commits
10298ca
ceb480a
b2a86dd
adebe1e
c3d0acc
ea12aa8
0b6bbca
8005788
3f699c1
29925b1
462cf57
3e3c457
dbb2dce
145b0ad
c049b53
e5fc5d4
82765cb
8ea398d
9ab327a
a8391d8
98a2310
43d3795
22a2ecc
62b3a7b
e311769
cd7bb4b
5eb43de
d6cdc89
8dcd9ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -766,6 +766,40 @@ void database::process_bids( const asset_bitasset_data_object& bad ) | |
_cancel_bids_and_revive_mpa( to_revive, bad ); | ||
} | ||
|
||
void update_and_match_call_orders( database& db ) | ||
{ | ||
// Update call_price | ||
wlog( "Updating all call orders for hardfork core-343 at block ${n}", ("n",db.head_block_num()) ); | ||
asset_id_type current_asset; | ||
const asset_bitasset_data_object* abd = nullptr; | ||
// by_collateral index won't change after call_price updated, so it's safe to iterate | ||
for( const auto& call_obj : db.get_index_type<call_order_index>().indices().get<by_collateral>() ) | ||
{ | ||
if( current_asset != call_obj.debt_type() ) // debt type won't be asset_id_type(), abd will always get initialized | ||
{ | ||
current_asset = call_obj.debt_type(); | ||
abd = ¤t_asset(db).bitasset_data(db); | ||
} | ||
if( !abd || abd->is_prediction_market ) // nothing to do with PM's; check !abd just to be safe | ||
continue; | ||
db.modify( call_obj, [&]( call_order_object& call ) { | ||
call.call_price = price::call_price( call.get_debt(), call.get_collateral(), | ||
abd->current_feed.maintenance_collateral_ratio ); | ||
}); | ||
} | ||
// Match call orders | ||
const auto& asset_idx = db.get_index_type<asset_index>().indices().get<by_type>(); | ||
auto itr = asset_idx.lower_bound( true /** market issued */ ); | ||
while( itr != asset_idx.end() ) | ||
{ | ||
const asset_object& a = *itr; | ||
++itr; | ||
// be here, next_maintenance_time should have been updated already | ||
db.check_call_orders( a, true, false ); // allow black swan, and call orders are taker | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps skip over PM's here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whether to skip over PM's here, the only logic difference might be the Since we didn't fix #460 in this release (no BSIP for it so far), PM's will still suffer black swan events when there is a price feed change or matching logic change. Although we didn't update I'd suggest we trigger black swan events here (if there would be some) to be consistent (rather than triggering on a new but same price feed). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps keep the old logic for PM's, don't trigger black swan event when there is no matching limit order? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My opinion on #460 is here: #460 (comment). Aka not really a bug and not a quick fix to me. So my recommendation in this release is to let PMs (still) have same behavior (on black swan events) as other MPAs, that said, we changed behavior of other MPAs, which will affect PMs as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
} | ||
wlog( "Done updating all call orders for hardfork core-343 at block ${n}", ("n",db.head_block_num()) ); | ||
} | ||
|
||
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props) | ||
{ | ||
const auto& gpo = get_global_properties(); | ||
|
@@ -917,11 +951,20 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g | |
if( (dgpo.next_maintenance_time < HARDFORK_613_TIME) && (next_maintenance_time >= HARDFORK_613_TIME) ) | ||
deprecate_annual_members(*this); | ||
|
||
// To reset call_price of all call orders, then match by new rule | ||
bool to_update_and_match_call_orders = false; | ||
if( (dgpo.next_maintenance_time <= HARDFORK_CORE_343_TIME) && (next_maintenance_time > HARDFORK_CORE_343_TIME) ) | ||
to_update_and_match_call_orders = true; | ||
|
||
modify(dgpo, [next_maintenance_time](dynamic_global_property_object& d) { | ||
d.next_maintenance_time = next_maintenance_time; | ||
d.accounts_registered_this_interval = 0; | ||
}); | ||
|
||
// We need to do it after updated next_maintenance_time, to apply new rules here | ||
if( to_update_and_match_call_orders ) | ||
update_and_match_call_orders(*this); | ||
|
||
// Reset all BitAsset force settlement volumes to zero | ||
for( const auto& d : get_index_type<asset_bitasset_data_index>().indices() ) | ||
{ | ||
|
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -209,6 +209,20 @@ bool database::check_for_blackswan( const asset_object& mia, bool enable_black_s | |
const call_order_index& call_index = get_index_type<call_order_index>(); | ||
const auto& call_price_index = call_index.indices().get<by_price>(); | ||
|
||
auto call_min = price::min( bitasset.options.short_backing_asset, mia.id ); | ||
auto call_max = price::max( bitasset.options.short_backing_asset, mia.id ); | ||
auto call_itr = call_price_index.lower_bound( call_min ); | ||
auto call_end = call_price_index.upper_bound( call_max ); | ||
|
||
if( call_itr == call_end ) return false; // no call orders | ||
|
||
price highest = settle_price; | ||
|
||
auto maint_time = get_dynamic_global_properties().next_maintenance_time; | ||
if( maint_time > HARDFORK_CORE_338_TIME ) | ||
// due to #338, we won't check for black swan on incoming limit order, so need to check with MSSP here | ||
highest = bitasset.current_feed.max_short_squeeze_price(); | ||
|
||
const limit_order_index& limit_index = get_index_type<limit_order_index>(); | ||
const auto& limit_price_index = limit_index.indices().get<by_price>(); | ||
|
||
|
@@ -217,38 +231,35 @@ bool database::check_for_blackswan( const asset_object& mia, bool enable_black_s | |
// stop when limit orders are selling too little USD for too much CORE | ||
auto lowest_possible_bid = price::min( mia.id, bitasset.options.short_backing_asset ); | ||
|
||
assert( highest_possible_bid.base.asset_id == lowest_possible_bid.base.asset_id ); | ||
FC_ASSERT( highest_possible_bid.base.asset_id == lowest_possible_bid.base.asset_id ); | ||
// NOTE limit_price_index is sorted from greatest to least | ||
auto limit_itr = limit_price_index.lower_bound( highest_possible_bid ); | ||
auto limit_end = limit_price_index.upper_bound( lowest_possible_bid ); | ||
|
||
auto call_min = price::min( bitasset.options.short_backing_asset, mia.id ); | ||
auto call_max = price::max( bitasset.options.short_backing_asset, mia.id ); | ||
auto call_itr = call_price_index.lower_bound( call_min ); | ||
auto call_end = call_price_index.upper_bound( call_max ); | ||
|
||
if( call_itr == call_end ) return false; // no call orders | ||
|
||
price highest = settle_price; | ||
if( limit_itr != limit_end ) { | ||
assert( settle_price.base.asset_id == limit_itr->sell_price.base.asset_id ); | ||
highest = std::max( limit_itr->sell_price, settle_price ); | ||
FC_ASSERT( highest.base.asset_id == limit_itr->sell_price.base.asset_id ); | ||
highest = std::max( limit_itr->sell_price, highest ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that still correct after HF_CORE_338? Important for understanding: check_for_blackswan is only called from check_call_orders. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Answer to previous remark: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In short, this change is correct. |
||
} | ||
|
||
auto least_collateral = call_itr->collateralization(); | ||
if( ~least_collateral >= highest ) | ||
{ | ||
wdump( (*call_itr) ); | ||
elog( "Black Swan detected: \n" | ||
" Least collateralized call: ${lc} ${~lc}\n" | ||
// " Highest Bid: ${hb} ${~hb}\n" | ||
" Settle Price: ${sp} ${~sp}\n" | ||
" Max: ${h} ${~h}\n", | ||
" Settle Price: ${~sp} ${sp}\n" | ||
" Max: ${~h} ${h}\n", | ||
("lc",least_collateral.to_real())("~lc",(~least_collateral).to_real()) | ||
// ("hb",limit_itr->sell_price.to_real())("~hb",(~limit_itr->sell_price).to_real()) | ||
("sp",settle_price.to_real())("~sp",(~settle_price).to_real()) | ||
("h",highest.to_real())("~h",(~highest).to_real()) ); | ||
FC_ASSERT( enable_black_swan, "Black swan was detected during a margin update which is not allowed to trigger a blackswan" ); | ||
globally_settle_asset(mia, ~least_collateral ); | ||
if( maint_time > HARDFORK_CORE_338_TIME && ~least_collateral <= settle_price ) | ||
// global settle at feed price if possible | ||
globally_settle_asset(mia, settle_price ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still struggling with the legitimacy of this, but tending to think it's ok. |
||
else | ||
globally_settle_asset(mia, ~least_collateral ); | ||
return true; | ||
} | ||
return false; | ||
|
@@ -257,20 +268,25 @@ bool database::check_for_blackswan( const asset_object& mia, bool enable_black_s | |
void database::clear_expired_orders() | ||
{ try { | ||
//Cancel expired limit orders | ||
auto head_time = head_block_time(); | ||
auto maint_time = get_dynamic_global_properties().next_maintenance_time; | ||
auto& limit_index = get_index_type<limit_order_index>().indices().get<by_expiration>(); | ||
while( !limit_index.empty() && limit_index.begin()->expiration <= head_block_time() ) | ||
while( !limit_index.empty() && limit_index.begin()->expiration <= head_time ) | ||
{ | ||
const limit_order_object& order = *limit_index.begin(); | ||
auto base_asset = order.sell_price.base.asset_id; | ||
auto quote_asset = order.sell_price.quote.asset_id; | ||
cancel_limit_order( order ); | ||
// check call orders | ||
// Comments below are copied from limit_order_cancel_evaluator::do_apply(...) | ||
// Possible optimization: order can be called by cancelling a limit order | ||
// if the canceled order was at the top of the book. | ||
// Do I need to check calls in both assets? | ||
check_call_orders( base_asset( *this ) ); | ||
check_call_orders( quote_asset( *this ) ); | ||
if( maint_time <= HARDFORK_CORE_606_TIME ) | ||
{ | ||
// check call orders | ||
// Comments below are copied from limit_order_cancel_evaluator::do_apply(...) | ||
// Possible optimization: order can be called by cancelling a limit order | ||
// if the canceled order was at the top of the book. | ||
// Do I need to check calls in both assets? | ||
check_call_orders( base_asset( *this ) ); | ||
check_call_orders( quote_asset( *this ) ); | ||
} | ||
} | ||
|
||
//Process expired force settlement orders | ||
|
@@ -330,7 +346,7 @@ void database::clear_expired_orders() | |
} | ||
|
||
// Has this order not reached its settlement date? | ||
if( order.settlement_date > head_block_time() ) | ||
if( order.settlement_date > head_time ) | ||
{ | ||
if( next_asset() ) | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// bitshares-core issue #338 Fix "margin call order fills at price of matching limit_order" | ||
#ifndef HARDFORK_CORE_338_TIME | ||
#define HARDFORK_CORE_338_TIME (fc::time_point_sec( 1600000000 )) | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// bitshares-core issue #343 | ||
// Fix "Inconsistent sorting of call orders between matching against a limit order and a force settle order" | ||
#ifndef HARDFORK_CORE_343_TIME | ||
#define HARDFORK_CORE_343_TIME (fc::time_point_sec( 1600000000 )) | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// bitshares-core issue #453 Fix "Multiple limit order and call order matching issue" | ||
#ifndef HARDFORK_CORE_453_TIME | ||
#define HARDFORK_CORE_453_TIME (fc::time_point_sec( 1600000000 )) | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// bitshares-core issue #606 Fix "Undercollateralized short positions should be called regardless of asks" | ||
#ifndef HARDFORK_CORE_606_TIME | ||
#define HARDFORK_CORE_606_TIME (fc::time_point_sec( 1600000000 )) | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// bitshares-core issue #625 Fix "Potential erratic order matching issue involving margin call orders" | ||
#ifndef HARDFORK_CORE_625_TIME | ||
#define HARDFORK_CORE_625_TIME (fc::time_point_sec( 1600000000 )) | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please capture only abd
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.