update black swan implementation
This commit is contained in:
parent
d5fb32a839
commit
be5a8c6365
5 changed files with 77 additions and 98 deletions
|
|
@ -247,6 +247,7 @@ void_result asset_update_bitasset_evaluator::do_evaluate(const asset_update_bita
|
|||
FC_ASSERT(a.is_market_issued(), "Cannot update BitAsset-specific settings on a non-BitAsset.");
|
||||
|
||||
const asset_bitasset_data_object& b = a.bitasset_data(d);
|
||||
FC_ASSERT( !b.has_settlement(), "Cannot update a bitasset after a settlement has executed" );
|
||||
if( o.new_options.short_backing_asset != b.options.short_backing_asset )
|
||||
{
|
||||
FC_ASSERT(a.dynamic_asset_data_id(d).current_supply == 0);
|
||||
|
|
@ -345,21 +346,40 @@ object_id_type asset_settle_evaluator::do_evaluate(const asset_settle_evaluator:
|
|||
const database& d = db();
|
||||
asset_to_settle = &op.amount.asset_id(d);
|
||||
FC_ASSERT(asset_to_settle->is_market_issued());
|
||||
FC_ASSERT(asset_to_settle->can_force_settle());
|
||||
const auto& bitasset = asset_to_settle->bitasset_data(d);
|
||||
FC_ASSERT(asset_to_settle->can_force_settle() || bitasset.has_settlement() );
|
||||
FC_ASSERT(d.get_balance(d.get(op.account), *asset_to_settle) >= op.amount);
|
||||
|
||||
return d.get_index_type<force_settlement_index>().get_next_id();
|
||||
}
|
||||
|
||||
object_id_type asset_settle_evaluator::do_apply(const asset_settle_evaluator::operation_type& op)
|
||||
operation_result asset_settle_evaluator::do_apply(const asset_settle_evaluator::operation_type& op)
|
||||
{
|
||||
database& d = db();
|
||||
d.adjust_balance(op.account, -op.amount);
|
||||
return d.create<force_settlement_object>([&](force_settlement_object& s) {
|
||||
s.owner = op.account;
|
||||
s.balance = op.amount;
|
||||
s.settlement_date = d.head_block_time() + asset_to_settle->bitasset_data(d).options.force_settlement_delay_sec;
|
||||
}).id;
|
||||
|
||||
const auto& bitasset = asset_to_settle->bitasset_data(d);
|
||||
if( bitasset.has_settlement() )
|
||||
{
|
||||
auto settled_amount = op.amount * bitasset.settlement_price;
|
||||
FC_ASSERT( settled_amount.amount <= bitasset.settlement_fund );
|
||||
|
||||
d.modify( bitasset, [&]( asset_bitasset_data_object& obj ){
|
||||
obj.settlement_fund -= settled_amount.amount;
|
||||
});
|
||||
|
||||
d.adjust_balance(op.account, settled_amount);
|
||||
|
||||
return settled_amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
return d.create<force_settlement_object>([&](force_settlement_object& s) {
|
||||
s.owner = op.account;
|
||||
s.balance = op.amount;
|
||||
s.settlement_date = d.head_block_time() + asset_to_settle->bitasset_data(d).options.force_settlement_delay_sec;
|
||||
}).id;
|
||||
}
|
||||
}
|
||||
|
||||
void_result asset_publish_feeds_evaluator::do_evaluate(const asset_publish_feed_operation& o)
|
||||
|
|
@ -371,6 +391,7 @@ void_result asset_publish_feeds_evaluator::do_evaluate(const asset_publish_feed_
|
|||
FC_ASSERT(base.is_market_issued());
|
||||
|
||||
const asset_bitasset_data_object& bitasset = base.bitasset_data(d);
|
||||
FC_ASSERT( !bitasset.has_settlement(), "No further feeds may be published after a settlement event" );
|
||||
FC_ASSERT(o.feed.settlement_price.quote.asset_id == bitasset.options.short_backing_asset);
|
||||
//Verify that the publisher is authoritative to publish a feed
|
||||
if( base.issuer == account_id_type() )
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope
|
|||
|
||||
_bitasset_data = &_debt_asset->bitasset_data(d);
|
||||
|
||||
/// if there is a settlement for this asset, then no further margin positions may be taken and
|
||||
/// all existing margin positions should have been closed va database::globally_settle_asset
|
||||
FC_ASSERT( !_bitasset_data->has_settlement() );
|
||||
|
||||
FC_ASSERT( o.delta_collateral.asset_id == _bitasset_data->options.short_backing_asset );
|
||||
|
||||
if( _bitasset_data->is_prediction_market )
|
||||
|
|
|
|||
|
|
@ -28,14 +28,12 @@
|
|||
namespace graphene { namespace chain {
|
||||
|
||||
/**
|
||||
for each short order, fill it at settlement price and place funds received into a total
|
||||
calculate the USD->CORE price and convert all USD balances to CORE at that price and subtract CORE from total
|
||||
- any fees accumulated by the issuer in the bitasset are forfeit / not redeemed
|
||||
- cancel all open orders with bitasset in it
|
||||
- any bitassets that use this bitasset as collateral are immediately settled at their feed price
|
||||
- convert all balances in bitasset to CORE and subtract from total
|
||||
- any prediction markets with usd as the backing get converted to CORE as the backing
|
||||
any CORE left over due to rounding errors is paid to accumulated fees
|
||||
* All margin positions are force closed at the swan price
|
||||
* Collateral received goes into a force-settlement fund
|
||||
* No new margin positions can be created for this asset
|
||||
* No more price feed updates
|
||||
* Force settlement happens without delay at the swan price, deducting from force-settlement fund
|
||||
* No more asset updates may be issued.
|
||||
*/
|
||||
void database::globally_settle_asset( const asset_object& mia, const price& settlement_price )
|
||||
{ try {
|
||||
|
|
@ -45,6 +43,8 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
|
|||
edump( (mia.symbol)(settlement_price) );
|
||||
|
||||
const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
|
||||
FC_ASSERT( !bitasset.has_settlement(), "black swan already occurred, it should not happen again" );
|
||||
|
||||
const asset_object& backing_asset = bitasset.options.short_backing_asset(*this);
|
||||
asset collateral_gathered = backing_asset.amount(0);
|
||||
|
||||
|
|
@ -54,93 +54,30 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
|
|||
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_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) );
|
||||
auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) );
|
||||
while( call_itr != call_end )
|
||||
{
|
||||
auto pays = call_itr->get_debt() * settlement_price;
|
||||
wdump( (call_itr->get_debt() ) );
|
||||
collateral_gathered += pays;
|
||||
const auto& order = *call_itr;
|
||||
++call_itr;
|
||||
FC_ASSERT( fill_order( order, pays, order.get_debt() ) );
|
||||
}
|
||||
|
||||
const limit_order_index& limit_index = get_index_type<limit_order_index>();
|
||||
const auto& limit_price_index = limit_index.indices().get<by_price>();
|
||||
|
||||
auto max_short_squeeze = bitasset.current_feed.max_short_squeeze_price();
|
||||
// cancel all orders selling the market issued asset
|
||||
auto limit_itr = limit_price_index.lower_bound(price::max(mia.id, bitasset.options.short_backing_asset));
|
||||
auto limit_end = limit_price_index.upper_bound(~max_short_squeeze);
|
||||
while( limit_itr != limit_end )
|
||||
{
|
||||
const auto& order = *limit_itr;
|
||||
ilog( "CANCEL LIMIT ORDER" );
|
||||
idump((order));
|
||||
++limit_itr;
|
||||
cancel_order( order );
|
||||
}
|
||||
|
||||
limit_itr = limit_price_index.begin();
|
||||
while( limit_itr != limit_end )
|
||||
{
|
||||
if( limit_itr->amount_for_sale().asset_id == mia.id )
|
||||
{
|
||||
const auto& order = *limit_itr;
|
||||
ilog( "CANCEL_AGAIN" );
|
||||
edump((order));
|
||||
++limit_itr;
|
||||
cancel_order( order );
|
||||
}
|
||||
}
|
||||
|
||||
limit_itr = limit_price_index.begin();
|
||||
while( limit_itr != limit_end )
|
||||
// cancel all call orders and accumulate it into collateral_gathered
|
||||
auto call_itr = call_price_index.lower_bound( price::min( bitasset.options.short_backing_asset, mia.id ) );
|
||||
auto call_end = call_price_index.upper_bound( price::max( bitasset.options.short_backing_asset, mia.id ) );
|
||||
while( call_itr != call_end )
|
||||
{
|
||||
if( limit_itr->amount_for_sale().asset_id == mia.id )
|
||||
{
|
||||
const auto& order = *limit_itr;
|
||||
edump((order));
|
||||
++limit_itr;
|
||||
cancel_order( order );
|
||||
}
|
||||
auto pays = call_itr->get_debt() * settlement_price;
|
||||
wdump( (call_itr->get_debt() ) );
|
||||
collateral_gathered += pays;
|
||||
const auto& order = *call_itr;
|
||||
++call_itr;
|
||||
FC_ASSERT( fill_order( order, pays, order.get_debt() ) );
|
||||
}
|
||||
|
||||
// settle all balances
|
||||
asset total_mia_settled = mia.amount(0);
|
||||
modify( bitasset, [&]( asset_bitasset_data_object& obj ){
|
||||
obj.settlement_price = settlement_price;
|
||||
obj.settlement_fund = collateral_gathered.amount;
|
||||
});
|
||||
|
||||
const auto& index = get_index_type<account_balance_index>().indices().get<by_asset>();
|
||||
auto range = index.equal_range(mia.get_id());
|
||||
for( auto itr = range.first; itr != range.second; ++itr )
|
||||
{
|
||||
auto mia_balance = itr->get_balance();
|
||||
if( mia_balance.amount > 0 )
|
||||
{
|
||||
adjust_balance(itr->owner, -mia_balance);
|
||||
auto settled_amount = mia_balance * settlement_price;
|
||||
idump( (mia_balance)(settled_amount)(settlement_price) );
|
||||
adjust_balance(itr->owner, settled_amount);
|
||||
total_mia_settled += mia_balance;
|
||||
collateral_gathered -= settled_amount;
|
||||
}
|
||||
}
|
||||
/// TODO: after all margin positions are closed, the current supply will be reported as 0, but
|
||||
/// that is a lie, the supply didn't change. We need to capture the current supply before
|
||||
/// filling all call orders and then restore it afterward. Then in the force settlement
|
||||
/// evaluator reduce the supply
|
||||
|
||||
// TODO: convert payments held in escrow
|
||||
|
||||
modify( mia_dyn, [&]( asset_dynamic_data_object& obj ){
|
||||
total_mia_settled.amount += obj.accumulated_fees;
|
||||
obj.accumulated_fees = 0;
|
||||
});
|
||||
|
||||
wlog( "====================== AFTER SETTLE BLACK SWAN UNCLAIMED SETTLEMENT FUNDS ==============\n" );
|
||||
wdump((collateral_gathered)(total_mia_settled)(original_mia_supply)(mia_dyn.current_supply));
|
||||
modify( bitasset.options.short_backing_asset(*this).dynamic_asset_data_id(*this), [&]( asset_dynamic_data_object& obj ){
|
||||
obj.accumulated_fees += collateral_gathered.amount;
|
||||
});
|
||||
|
||||
FC_ASSERT( total_mia_settled.amount == original_mia_supply, "", ("total_settled",total_mia_settled)("original",original_mia_supply) );
|
||||
} FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) }
|
||||
} FC_CAPTURE_AND_RETHROW( (mia)(settlement_price) ) }
|
||||
|
||||
void database::cancel_order(const force_settlement_object& order, bool create_virtual_op)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ namespace graphene { namespace chain {
|
|||
typedef asset_settle_operation operation_type;
|
||||
|
||||
object_id_type do_evaluate(const operation_type& op);
|
||||
object_id_type do_apply(const operation_type& op);
|
||||
operation_result do_apply(const operation_type& op);
|
||||
|
||||
const asset_object* asset_to_settle = nullptr;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -250,6 +250,21 @@ namespace graphene { namespace chain {
|
|||
/// Calculate the maximum force settlement volume per maintenance interval, given the current share supply
|
||||
share_type max_force_settlement_volume(share_type current_supply)const;
|
||||
|
||||
/** return true if there has been a black swan, false otherwise */
|
||||
bool has_settlement()const { return !settlement_price.is_null(); }
|
||||
|
||||
/**
|
||||
* In the event of a black swan, the swan price is saved in the settlement price,
|
||||
* and all margin positions are settled at the same price with the siezed collateral
|
||||
* being moved into the settlement fund. From this point on no further updates to
|
||||
* the asset are permitted (no feeds, etc) and forced settlement occurs using
|
||||
* the settlement price and fund.
|
||||
*/
|
||||
///@{
|
||||
price settlement_price;
|
||||
share_type settlement_fund;
|
||||
///@}
|
||||
|
||||
time_point_sec feed_expiration_time()const
|
||||
{ return current_feed_publication_time + options.feed_lifetime_sec; }
|
||||
bool feed_is_expired(time_point_sec current_time)const
|
||||
|
|
@ -292,6 +307,8 @@ FC_REFLECT_DERIVED( graphene::chain::asset_bitasset_data_object, (graphene::db::
|
|||
(options)
|
||||
(force_settled_volume)
|
||||
(is_prediction_market)
|
||||
(settlement_price)
|
||||
(settlement_fund)
|
||||
)
|
||||
|
||||
FC_REFLECT( graphene::chain::asset_object::asset_options,
|
||||
|
|
|
|||
Loading…
Reference in a new issue