WIP: Remove call price from call_order_update_operation
This commit is contained in:
parent
c3b30b3dd7
commit
127ca9f8de
7 changed files with 60 additions and 79 deletions
|
|
@ -67,7 +67,6 @@ void_result call_order_update_evaluator::do_evaluate(const call_order_update_ope
|
|||
void_result call_order_update_evaluator::do_apply(const call_order_update_operation& o)
|
||||
{ try {
|
||||
database& d = db();
|
||||
//wdump( (_bitasset_data->current_feed) );
|
||||
|
||||
if( o.delta_debt.amount != 0 )
|
||||
{
|
||||
|
|
@ -104,11 +103,12 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
|
|||
FC_ASSERT( o.delta_debt.amount > 0 );
|
||||
|
||||
call_obj = &d.create<call_order_object>( [&](call_order_object& call ){
|
||||
call.borrower = o.funding_account;
|
||||
call.collateral = o.delta_collateral.amount;
|
||||
call.debt = o.delta_debt.amount;
|
||||
call.call_price = ~o.call_price;
|
||||
});
|
||||
call.borrower = o.funding_account;
|
||||
call.collateral = o.delta_collateral.amount;
|
||||
call.debt = o.delta_debt.amount;
|
||||
call.call_price = price::call_price(o.delta_debt, o.delta_collateral,
|
||||
_bitasset_data->current_feed.maintenance_collateral_ratio);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -117,41 +117,32 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
|
|||
d.modify( *call_obj, [&]( call_order_object& call ){
|
||||
call.collateral += o.delta_collateral.amount;
|
||||
call.debt += o.delta_debt.amount;
|
||||
call.call_price = ~o.call_price;
|
||||
call.call_price = price::call_price(call.get_debt(), call.get_collateral(),
|
||||
_bitasset_data->current_feed.maintenance_collateral_ratio);
|
||||
});
|
||||
}
|
||||
|
||||
auto debt = call_obj->get_debt();
|
||||
if( debt.amount == 0 )
|
||||
if( debt.amount == 0 )
|
||||
{
|
||||
FC_ASSERT( call_obj->collateral == 0 );
|
||||
d.remove( *call_obj );
|
||||
return void_result();
|
||||
}
|
||||
|
||||
/** then we must check for margin calls and other issues */
|
||||
FC_ASSERT(call_obj->collateral > 0 && call_obj->debt > 0);
|
||||
|
||||
// then we must check for margin calls and other issues
|
||||
if( !_bitasset_data->is_prediction_market )
|
||||
{
|
||||
auto collateral = call_obj->get_collateral();
|
||||
auto mp = _bitasset_data->current_feed.maintenance_price();
|
||||
|
||||
// edump((debt)(collateral)((debt/collateral).to_real())(mp.to_real()) );
|
||||
// edump((debt*mp));
|
||||
/// paying off the debt at the user specified call price should require
|
||||
/// less collateral than paying off the debt at the maitenance price
|
||||
auto col_at_call_price = debt * o.call_price;
|
||||
auto col_at_min_callprice = debt * mp;
|
||||
FC_ASSERT( col_at_call_price <= col_at_min_callprice, "", ("debt*o.callprice",debt*o.call_price)("debt*mp",debt*mp) );
|
||||
FC_ASSERT( col_at_call_price <= collateral );
|
||||
|
||||
//wdump( (o.call_price)(mp)(call_obj->call_price.to_real())(mp.to_real()) );
|
||||
//FC_ASSERT( call_obj->call_price <= mp );
|
||||
// Check that the order's debt per collateral is less than the system's minimum debt per collateral.
|
||||
FC_ASSERT( ~call_obj->call_price < _bitasset_data->current_feed.maintenance_price(),
|
||||
"Insufficient collateral for debt.",
|
||||
("a", call_obj->call_price)("b", _bitasset_data->current_feed.maintenance_price()));
|
||||
|
||||
auto call_order_id = call_obj->id;
|
||||
|
||||
//ilog( "checking call orders" );
|
||||
|
||||
// check to see if the order needs to be margin called now, but don't allow black swans and require there to be
|
||||
// check to see if the order needs to be margin called now, but don't allow black swans and require there to be
|
||||
// limit orders available that could be used to fill the order.
|
||||
if( d.check_call_orders( *_debt_asset, false ) )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@
|
|||
// These are NOT percentages
|
||||
#define GRAPHENE_MIN_COLLATERAL_RATIO 1001 // lower than this could result in divide by 0
|
||||
#define GRAPHENE_MAX_COLLATERAL_RATIO 32000 // higher than this is unnecessary and may exceed int16 storage
|
||||
#define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO 1500 // We require collateral of 1.5x or more
|
||||
#define GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO 1750 // If there is a squeeze you are protected up to 1.75x... but black swan could occur first
|
||||
#define GRAPHENE_DEFAULT_MAINTENANCE_COLLATERAL_RATIO 1750 // Call when collateral only pays off 175% the debt
|
||||
#define GRAPHENE_DEFAULT_MAX_SHORT_SQUEEZE_RATIO 1500 // Stop calling when collateral only pays off 150% of the debt
|
||||
#define GRAPHENE_DEFAULT_MARGIN_PERIOD_SEC (30*60*60*24)
|
||||
|
||||
#define GRAPHENE_DEFAULT_NUM_WITNESSES (101)
|
||||
|
|
|
|||
|
|
@ -845,7 +845,6 @@ namespace graphene { namespace chain {
|
|||
account_id_type funding_account; ///< pays fee, collateral, and cover
|
||||
asset delta_collateral; ///< the amount of collateral to add to the margin position
|
||||
asset delta_debt; ///< the amount of the debt to be paid off, may be negative to issue new debt
|
||||
price call_price; ///< the price at which the collateral will be sold to cover the debt
|
||||
|
||||
account_id_type fee_payer()const { return funding_account; }
|
||||
void get_required_auth(flat_set<account_id_type>& active_auth_set, flat_set<account_id_type>&)const;
|
||||
|
|
@ -1504,7 +1503,7 @@ FC_REFLECT( graphene::chain::limit_order_create_operation,
|
|||
)
|
||||
FC_REFLECT( graphene::chain::fill_order_operation, (fee)(order_id)(account_id)(pays)(receives) )
|
||||
FC_REFLECT( graphene::chain::limit_order_cancel_operation,(fee)(fee_paying_account)(order) )
|
||||
FC_REFLECT( graphene::chain::call_order_update_operation, (fee)(funding_account)(delta_collateral)(delta_debt)(call_price) )
|
||||
FC_REFLECT( graphene::chain::call_order_update_operation, (fee)(funding_account)(delta_collateral)(delta_debt) )
|
||||
|
||||
FC_REFLECT( graphene::chain::transfer_operation,
|
||||
(fee)(from)(to)(amount)(memo) )
|
||||
|
|
|
|||
|
|
@ -401,9 +401,7 @@ void call_order_update_operation::validate()const
|
|||
{ try {
|
||||
FC_ASSERT( fee.amount >= 0 );
|
||||
FC_ASSERT( delta_collateral.asset_id != delta_debt.asset_id );
|
||||
FC_ASSERT( delta_debt.asset_id == call_price.base.asset_id );
|
||||
FC_ASSERT( delta_collateral.asset_id == call_price.quote.asset_id );
|
||||
call_price.validate();
|
||||
FC_ASSERT( delta_collateral.amount != 0 || delta_debt.amount != 0 );
|
||||
} FC_CAPTURE_AND_RETHROW((*this)) }
|
||||
|
||||
share_type call_order_update_operation::calculate_fee(const fee_schedule_type& k) const
|
||||
|
|
|
|||
|
|
@ -716,22 +716,22 @@ void database_fixture::force_settle( const account_object& who, asset what )
|
|||
trx.operations.clear();
|
||||
} FC_CAPTURE_AND_RETHROW( (who)(what) ) }
|
||||
|
||||
void database_fixture::borrow( const account_object& who, asset what, asset collateral, price call_price )
|
||||
void database_fixture::borrow(const account_object& who, asset what, asset collateral)
|
||||
{ try {
|
||||
trx.set_expiration(db.head_block_time() + fc::minutes(1));
|
||||
trx.operations.clear();
|
||||
trx.operations.push_back( call_order_update_operation({ asset(), who.id, collateral, what, call_price }));;
|
||||
trx.operations.push_back(call_order_update_operation({asset(), who.id, collateral, what}));;
|
||||
for( auto& op : trx.operations ) op.visit( operation_set_fee( db.current_fee_schedule() ) );
|
||||
trx.validate();
|
||||
db.push_transaction(trx, ~0);
|
||||
trx.operations.clear();
|
||||
} FC_CAPTURE_AND_RETHROW( (who.name)(what)(collateral) ) }
|
||||
|
||||
void database_fixture::cover( const account_object& who, asset what, asset collateral, price call_price )
|
||||
void database_fixture::cover(const account_object& who, asset what, asset collateral)
|
||||
{ try {
|
||||
trx.set_expiration(db.head_block_time() + fc::minutes(1));
|
||||
trx.operations.clear();
|
||||
trx.operations.push_back( call_order_update_operation({ asset(), who.id, -collateral, -what, call_price }));
|
||||
trx.operations.push_back( call_order_update_operation({asset(), who.id, -collateral, -what}));
|
||||
for( auto& op : trx.operations ) op.visit( operation_set_fee( db.current_fee_schedule() ) );
|
||||
trx.validate();
|
||||
db.push_transaction(trx, ~0);
|
||||
|
|
|
|||
|
|
@ -151,10 +151,10 @@ struct database_fixture {
|
|||
void force_settle(const account_object& who, asset what);
|
||||
void update_feed_producers(const asset_object& mia, flat_set<account_id_type> producers);
|
||||
void publish_feed(const asset_object& mia, const account_object& by, const price_feed& f);
|
||||
void borrow(account_id_type who, asset what, asset collateral, price call_price = price())
|
||||
{ borrow(who(db), what, collateral, call_price); }
|
||||
void borrow(const account_object& who, asset what, asset collateral, price call_price);
|
||||
void cover(const account_object& who, asset what, asset collateral_freed, price call_price = price());
|
||||
void borrow(account_id_type who, asset what, asset collateral)
|
||||
{ borrow(who(db), what, collateral); }
|
||||
void borrow(const account_object& who, asset what, asset collateral);
|
||||
void cover(const account_object& who, asset what, asset collateral_freed);
|
||||
|
||||
const asset_object& get_asset( const string& symbol )const;
|
||||
const account_object& get_account( const string& name )const;
|
||||
|
|
|
|||
|
|
@ -47,8 +47,12 @@ BOOST_AUTO_TEST_CASE( feed_limit_logic_test )
|
|||
price_feed feed;
|
||||
feed.settlement_price = usd / core;
|
||||
|
||||
FC_ASSERT( usd * feed.settlement_price < usd * feed.maintenance_price() );
|
||||
FC_ASSERT( usd * feed.maintenance_price() < usd * feed.max_short_squeeze_price() );
|
||||
wdump((feed.settlement_price.to_real()));
|
||||
wdump((feed.maintenance_price().to_real()));
|
||||
wdump((feed.max_short_squeeze_price().to_real()));
|
||||
|
||||
BOOST_CHECK( usd * feed.settlement_price < usd * feed.maintenance_price() );
|
||||
BOOST_CHECK( usd * feed.maintenance_price() < usd * feed.max_short_squeeze_price() );
|
||||
|
||||
} catch (fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
|
|
@ -70,65 +74,58 @@ BOOST_AUTO_TEST_CASE( call_order_update_test )
|
|||
|
||||
FC_ASSERT( bitusd.bitasset_data(db).current_feed.settlement_price == current_feed.settlement_price );
|
||||
|
||||
auto default_call_price = ~price::call_price( bitusd.amount(5000), asset(5000), 1750);
|
||||
|
||||
BOOST_TEST_MESSAGE( "attempting to borrow using 2x collateral at 1:1 price now that there is a valid order" );
|
||||
borrow( dan, bitusd.amount(5000), asset(10000), default_call_price );
|
||||
borrow( dan, bitusd.amount(5000), asset(10000));
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 );
|
||||
|
||||
BOOST_TEST_MESSAGE( "covering 2500 usd and freeing 5000 core..." );
|
||||
cover( dan, bitusd.amount(2500), asset(5000), default_call_price );
|
||||
cover( dan, bitusd.amount(2500), asset(5000));
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 2500 );
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 + 5000 );
|
||||
|
||||
BOOST_TEST_MESSAGE( "verifying that attempting to cover the full amount without claiming the collateral fails" );
|
||||
BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(0), default_call_price ), fc::exception );
|
||||
BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(2500), core.amount(0) ), fc::exception );
|
||||
|
||||
cover( dan, bitusd.amount(2500), core.amount(5000), default_call_price );
|
||||
cover( dan, bitusd.amount(2500), core.amount(5000));
|
||||
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 0 );
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 );
|
||||
|
||||
borrow( dan, bitusd.amount(5000), asset(10000), default_call_price );
|
||||
borrow( dan, bitusd.amount(5000), asset(10000));
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 10000 );
|
||||
|
||||
|
||||
// test just increasing collateral
|
||||
BOOST_TEST_MESSAGE( "increasing collateral" );
|
||||
borrow( dan, bitusd.amount(0), asset(10000), default_call_price );
|
||||
borrow( dan, bitusd.amount(0), asset(10000));
|
||||
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 20000 );
|
||||
|
||||
// test just decreasing debt
|
||||
BOOST_TEST_MESSAGE( "decreasing debt" );
|
||||
cover( dan, bitusd.amount(1000), asset(0), default_call_price );
|
||||
cover( dan, bitusd.amount(1000), asset(0));
|
||||
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 4000 );
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 20000 );
|
||||
|
||||
BOOST_TEST_MESSAGE( "increasing debt without increasing collateral" );
|
||||
borrow( dan, bitusd.amount(1000), asset(0), default_call_price );
|
||||
borrow( dan, bitusd.amount(1000), asset(0));
|
||||
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, bitusd ), 5000 );
|
||||
BOOST_REQUIRE_EQUAL( get_balance( dan, core ), 10000000 - 20000 );
|
||||
|
||||
BOOST_TEST_MESSAGE( "increasing debt without increasing collateral again" );
|
||||
BOOST_REQUIRE_THROW( borrow( dan, bitusd.amount(80000), asset(0), default_call_price ), fc::exception );
|
||||
BOOST_REQUIRE_THROW( borrow( dan, bitusd.amount(80000), asset(0)), fc::exception );
|
||||
BOOST_TEST_MESSAGE( "attempting to claim all collateral without paying off debt" );
|
||||
BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(20000), default_call_price ), fc::exception );
|
||||
BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(20000)), fc::exception );
|
||||
BOOST_TEST_MESSAGE( "attempting reduce collateral without paying off any debt" );
|
||||
cover( dan, bitusd.amount(0), asset(1000), default_call_price );
|
||||
|
||||
BOOST_TEST_MESSAGE( "attempting change call price without changing debt/collateral ratio" );
|
||||
default_call_price = ~price::call_price( bitusd.amount(100), asset(50), 1750);
|
||||
cover( dan, bitusd.amount(0), asset(0), default_call_price );
|
||||
cover( dan, bitusd.amount(0), asset(1000));
|
||||
|
||||
BOOST_TEST_MESSAGE( "attempting change call price to be below minimum for debt/collateral ratio" );
|
||||
default_call_price = ~price::call_price( bitusd.amount(100), asset(500), 1750);
|
||||
BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(0), default_call_price ), fc::exception );
|
||||
BOOST_REQUIRE_THROW( cover( dan, bitusd.amount(0), asset(0)), fc::exception );
|
||||
|
||||
} catch (fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
|
|
@ -166,14 +163,13 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test )
|
|||
|
||||
price_feed current_feed;
|
||||
current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);
|
||||
auto default_call_price = ~price::call_price( bitusd.amount(100), asset(100), 1750);
|
||||
|
||||
// starting out with price 1:1
|
||||
publish_feed( bitusd, feedproducer, current_feed );
|
||||
|
||||
// start out with 2:1 collateral
|
||||
borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price );
|
||||
borrow( borrower2, bitusd.amount(1000), asset(4000), default_call_price );
|
||||
borrow( borrower, bitusd.amount(1000), asset(2000));
|
||||
borrow( borrower2, bitusd.amount(1000), asset(4000) );
|
||||
|
||||
BOOST_REQUIRE_EQUAL( get_balance( borrower, bitusd ), 1000 );
|
||||
BOOST_REQUIRE_EQUAL( get_balance( borrower2, bitusd ), 1000 );
|
||||
|
|
@ -194,8 +190,8 @@ BOOST_AUTO_TEST_CASE( margin_call_limit_test )
|
|||
|
||||
|
||||
BOOST_TEST_MESSAGE( "Creating a margin call that is protected by the max short squeeze price" );
|
||||
borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price );
|
||||
borrow( borrower2, bitusd.amount(1000), asset(4000), default_call_price );
|
||||
borrow( borrower, bitusd.amount(1000), asset(2000) );
|
||||
borrow( borrower2, bitusd.amount(1000), asset(4000) );
|
||||
|
||||
// this should trigger margin call without protection from the price feed.
|
||||
order = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1800) );
|
||||
|
|
@ -226,14 +222,13 @@ BOOST_AUTO_TEST_CASE( black_swan )
|
|||
|
||||
price_feed current_feed;
|
||||
current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(100);
|
||||
auto default_call_price = ~price::call_price( bitusd.amount(100), asset(100), 1750);
|
||||
|
||||
// starting out with price 1:1
|
||||
publish_feed( bitusd, feedproducer, current_feed );
|
||||
|
||||
// start out with 2:1 collateral
|
||||
borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price );
|
||||
borrow( borrower2, bitusd.amount(1000), asset(4000), default_call_price );
|
||||
borrow( borrower, bitusd.amount(1000), asset(2000) );
|
||||
borrow( borrower2, bitusd.amount(1000), asset(4000) );
|
||||
|
||||
BOOST_REQUIRE_EQUAL( get_balance( borrower, bitusd ), 1000 );
|
||||
BOOST_REQUIRE_EQUAL( get_balance( borrower2, bitusd ), 1000 );
|
||||
|
|
@ -249,9 +244,9 @@ BOOST_AUTO_TEST_CASE( black_swan )
|
|||
FC_ASSERT( bitusd.bitasset_data(db).has_settlement() );
|
||||
|
||||
force_settle( borrower, bitusd.amount(100) );
|
||||
|
||||
|
||||
BOOST_TEST_MESSAGE( "Verify that we cannot borrow after black swan" );
|
||||
BOOST_REQUIRE_THROW( borrow( borrower, bitusd.amount(1000), asset(2000), default_call_price ), fc::exception );
|
||||
BOOST_REQUIRE_THROW( borrow( borrower, bitusd.amount(1000), asset(2000) ), fc::exception );
|
||||
} catch( const fc::exception& e) {
|
||||
edump((e.to_detail_string()));
|
||||
throw;
|
||||
|
|
@ -270,23 +265,21 @@ BOOST_AUTO_TEST_CASE( prediction_market )
|
|||
transfer(genesis_account, dan_id, asset(init_balance));
|
||||
transfer(genesis_account, nathan_id, asset(init_balance));
|
||||
|
||||
auto default_call_price = ~price::call_price( pmark.amount(100), asset(100), 1750);
|
||||
|
||||
BOOST_TEST_MESSAGE( "Require throw for mismatch collateral amounts" );
|
||||
BOOST_REQUIRE_THROW( borrow( dan, pmark.amount(1000), asset(2000), default_call_price ), fc::exception );
|
||||
BOOST_REQUIRE_THROW( borrow( dan, pmark.amount(1000), asset(2000) ), fc::exception );
|
||||
|
||||
BOOST_TEST_MESSAGE( "Open position with equal collateral" );
|
||||
borrow( dan, pmark.amount(1000), asset(1000), default_call_price );
|
||||
borrow( dan, pmark.amount(1000), asset(1000) );
|
||||
|
||||
BOOST_TEST_MESSAGE( "Cover position with unequal asset should fail." );
|
||||
BOOST_REQUIRE_THROW( cover( dan, pmark.amount(500), asset(1000), default_call_price ), fc::exception );
|
||||
BOOST_REQUIRE_THROW( cover( dan, pmark.amount(500), asset(1000) ), fc::exception );
|
||||
|
||||
BOOST_TEST_MESSAGE( "Cover half of position with equal ammounts" );
|
||||
cover( dan, pmark.amount(500), asset(500), default_call_price );
|
||||
cover( dan, pmark.amount(500), asset(500) );
|
||||
|
||||
BOOST_TEST_MESSAGE( "Verify that forced settlment fails before global settlement" );
|
||||
BOOST_REQUIRE_THROW( force_settle( dan, pmark.amount(100) ), fc::exception );
|
||||
|
||||
|
||||
BOOST_TEST_MESSAGE( "Shouldn't be allowed to force settle at more than 1 collateral per debt" );
|
||||
BOOST_REQUIRE_THROW( force_global_settle( pmark, pmark.amount(100) / core.amount(105) ), fc::exception );
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue