Skip to content

Commit

Permalink
Update black swan event detection for cryptonomex#338
Browse files Browse the repository at this point in the history
  • Loading branch information
abitmore committed Apr 7, 2018
1 parent ea12aa8 commit 0b6bbca
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 17 deletions.
2 changes: 2 additions & 0 deletions libraries/chain/db_maint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,7 @@ void database::process_bids( const asset_bitasset_data_object& 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
Expand Down Expand Up @@ -796,6 +797,7 @@ void update_and_match_call_orders( database& db )
// 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
}
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)
Expand Down
2 changes: 2 additions & 0 deletions libraries/chain/db_market.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,8 @@ bool database::apply_order(const limit_order_object& new_order_object, bool allo
// so a new order in the same direction with the call order won't trigger a black swan event.
// 3. calling is one direction. if the new limit order is on the opposite direction,
// no matter if matches with the call, it won't trigger a black swan event.
// (if a match at MSSP caused a black swan event, it means the call order is already undercollateralized,
// which should trigger a black swan event earlier.)
//
// Since it won't trigger a black swan, no need to check here.

Expand Down
42 changes: 25 additions & 17 deletions libraries/chain/db_update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>();

Expand All @@ -217,41 +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 );
}

// FIXME
// possible BUG: position with lowest call_price doesn't always have least collateral
// related to https://github.com/bitshares/bitshares-core/issues/343
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 )
// globol settle at feed price if possible
globally_settle_asset(mia, settle_price );
else
globally_settle_asset(mia, ~least_collateral );
return true;
}
return false;
Expand Down

0 comments on commit 0b6bbca

Please sign in to comment.