Progress on margin call tests

This commit is contained in:
Daniel Larimer 2015-06-18 18:42:44 -04:00
parent 66ab805458
commit db43f41de8
6 changed files with 384 additions and 264 deletions

View file

@ -126,4 +126,26 @@ namespace graphene { namespace chain {
FC_ASSERT( maintenance_collateral_ratio >= maximum_short_squeeze_ratio );
} FC_CAPTURE_AND_RETHROW( (*this) ) }
price price_feed::max_short_squeeze_price()const
{
asset collateral = settlement_price.quote;
fc::uint128 tmp( collateral.amount.value );
tmp *= maximum_short_squeeze_ratio - 1000;
tmp /= 1000;
FC_ASSERT( tmp <= GRAPHENE_MAX_SHARE_SUPPLY );
collateral.amount = tmp.to_uint64();
return settlement_price.base / collateral;
}
price price_feed::maintenance_price()const
{
asset collateral = settlement_price.quote;
fc::uint128 tmp( collateral.amount.value );
tmp *= maintenance_collateral_ratio - 1000;
tmp /= 1000;
FC_ASSERT( tmp <= GRAPHENE_MAX_SHARE_SUPPLY );
collateral.amount = tmp.to_uint64();
return settlement_price.base / collateral;
}
} } // graphene::chain

View file

@ -308,6 +308,7 @@ void_result asset_update_feed_producers_evaluator::do_apply(const asset_update_f
a.update_median_feeds(db().head_block_time());
});
return void_result();
}
@ -396,6 +397,9 @@ void_result asset_publish_feeds_evaluator::do_apply(const asset_publish_feed_ope
a.update_median_feeds(d.head_block_time());
});
/// TODO: only do this if the median feed actually changed, otherwise there is no point
db().check_call_orders( base );
return void_result();
} FC_CAPTURE_AND_RETHROW((o)) }

View file

@ -388,10 +388,12 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa
const limit_order_index& limit_index = get_index_type<limit_order_index>();
const auto& limit_price_index = limit_index.indices().get<by_price>();
// looking for limit orders selling the most USD for the least CORE
auto max_price = price::max( mia.id, bitasset.options.short_backing_asset );
auto min_price = bitasset.current_feed.max_short_squeeze_price();
/*
if( require_orders )
// stop when limit orders are selling too little USD for too much CORE
//auto min_price = bitasset.current_feed.max_short_squeeze_price();
auto min_price = price::min( mia.id, bitasset.options.short_backing_asset );
idump((bitasset.current_feed.settlement_price)(bitasset.current_feed.settlement_price.to_real()));
{
for( const auto& order : limit_price_index )
wdump((order)(order.sell_price.to_real()));
@ -404,24 +406,21 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa
wdump((max_price)(max_price.to_real()));
wdump((min_price)(min_price.to_real()));
}
*/
FC_ASSERT( max_price.base.asset_id == min_price.base.asset_id );
assert( max_price.base.asset_id == min_price.base.asset_id );
// wlog( "from ${a} Debt/Col to ${b} Debt/Col ", ("a", max_price.to_real())("b",min_price.to_real()) );
// NOTE limit_price_index is sorted from greatest to least
auto limit_itr = limit_price_index.lower_bound( max_price );
auto limit_end = limit_price_index.upper_bound( min_price );
/*
if( limit_itr != limit_price_index.end() )
wdump((*limit_itr)(limit_itr->sell_price.to_real()));
if( limit_end != limit_price_index.end() )
wdump((*limit_end)(limit_end->sell_price.to_real()));
*/
if( limit_itr == limit_end )
{
//wlog( "no orders available to fill margin calls" );
wlog( "no orders available to fill margin calls" );
return false;
}

View file

@ -158,10 +158,7 @@ namespace graphene { namespace chain {
* debt * settlement_price < debt * maintenance
* debt * maintenance_price() < debt * max_short_squeeze_price()
*/
price maintenance_price()const
{
return ~price::call_price( settlement_price.base, settlement_price.quote, maintenance_collateral_ratio );
}
price maintenance_price()const;
/** When selling collateral to pay off debt, the least amount of debt to receive should be
* min_usd = max_short_squeeze_price() * collateral
@ -169,10 +166,7 @@ namespace graphene { namespace chain {
* This is provided to ensure that a black swan cannot be trigged due to poor liquidity alone, it
* must be confirmed by having the max_short_squeeze_price() move below the black swan price.
*/
price max_short_squeeze_price()const
{
return ~price::call_price( settlement_price.base, settlement_price.quote, maximum_short_squeeze_ratio );
}
price max_short_squeeze_price()const;
///@}
friend bool operator == ( const price_feed& a, const price_feed& b )

View file

@ -41,6 +41,7 @@
namespace graphene { namespace chain {
using std::cout;
using std::cerr;
database_fixture::database_fixture()
: app(), db( *app.chain_database() )
@ -745,25 +746,25 @@ void database_fixture::print_market( const string& syma, const string& symb )con
const auto& limit_idx = db.get_index_type<limit_order_index>();
const auto& price_idx = limit_idx.indices().get<by_price>();
cout << std::fixed;
cout.precision(5);
cout << std::setw(10) << std::left << "NAME" << " ";
cout << std::setw(16) << std::right << "FOR SALE" << " ";
cout << std::setw(16) << std::right << "FOR WHAT" << " ";
cout << std::setw(10) << std::right << "PRICE" << " ";
cout << std::setw(10) << std::right << "1/PRICE" << "\n";
cout << string(70, '=') << std::endl;
cerr << std::fixed;
cerr.precision(5);
cerr << std::setw(10) << std::left << "NAME" << " ";
cerr << std::setw(16) << std::right << "FOR SALE" << " ";
cerr << std::setw(16) << std::right << "FOR WHAT" << " ";
cerr << std::setw(10) << std::right << "PRICE (S/W)" << " ";
cerr << std::setw(10) << std::right << "1/PRICE (W/S)" << "\n";
cerr << string(70, '=') << std::endl;
auto cur = price_idx.begin();
while( cur != price_idx.end() )
{
cout << std::setw( 10 ) << std::left << cur->seller(db).name << " ";
cout << std::setw( 10 ) << std::right << cur->for_sale.value << " ";
cout << std::setw( 5 ) << std::left << cur->amount_for_sale().asset_id(db).symbol << " ";
cout << std::setw( 10 ) << std::right << cur->amount_to_receive().amount.value << " ";
cout << std::setw( 5 ) << std::left << cur->amount_to_receive().asset_id(db).symbol << " ";
cout << std::setw( 10 ) << std::right << cur->sell_price.to_real() << " ";
cout << std::setw( 10 ) << std::right << (~cur->sell_price).to_real() << " ";
cout << "\n";
cerr << std::setw( 10 ) << std::left << cur->seller(db).name << " ";
cerr << std::setw( 10 ) << std::right << cur->for_sale.value << " ";
cerr << std::setw( 5 ) << std::left << cur->amount_for_sale().asset_id(db).symbol << " ";
cerr << std::setw( 10 ) << std::right << cur->amount_to_receive().amount.value << " ";
cerr << std::setw( 5 ) << std::left << cur->amount_to_receive().asset_id(db).symbol << " ";
cerr << std::setw( 10 ) << std::right << cur->sell_price.to_real() << " ";
cerr << std::setw( 10 ) << std::right << (~cur->sell_price).to_real() << " ";
cerr << "\n";
++cur;
}
}
@ -793,8 +794,10 @@ void database_fixture::print_call_orders()const
cout << std::setw(10) << std::right << "TYPE" << " ";
cout << std::setw(16) << std::right << "DEBT" << " ";
cout << std::setw(16) << std::right << "COLLAT" << " ";
cout << std::setw(16) << std::right << "CALL PRICE" << " ";
cout << std::setw(16) << std::right << "~CALL PRICE" << "\n";
cout << std::setw(16) << std::right << "CALL PRICE(D/C)" << " ";
cout << std::setw(16) << std::right << "~CALL PRICE(C/D)" << " ";
cout << std::setw(16) << std::right << "SWAN(D/C)" << " ";
cout << std::setw(16) << std::right << "SWAN(C/D)" << "\n";
cout << string(70, '=');
for( const call_order_object& o : db.get_index_type<call_order_index>().indices() )
@ -805,6 +808,8 @@ void database_fixture::print_call_orders()const
cout << std::setw( 16 ) << std::right << pretty( o.get_collateral() ) << " ";
cout << std::setw( 16 ) << std::right << o.call_price.to_real() << " ";
cout << std::setw( 16 ) << std::right << (~o.call_price).to_real() << " ";
cout << std::setw( 16 ) << std::right << (o.get_debt()/o.get_collateral()).to_real() << " ";
cout << std::setw( 16 ) << std::right << (~(o.get_debt()/o.get_collateral())).to_real() << " ";
}
std::cout << "\n";
}
@ -818,7 +823,7 @@ void database_fixture::print_joint_market( const string& syma, const string& sym
cout << std::setw(10) << std::right << "TYPE" << " ";
cout << std::setw(16) << std::right << "FOR SALE" << " ";
cout << std::setw(16) << std::right << "FOR WHAT" << " ";
cout << std::setw(16) << std::right << "PRICE" << "\n";
cout << std::setw(16) << std::right << "PRICE (S/W)" << "\n";
cout << string(70, '=');
const auto& limit_idx = db.get_index_type<limit_order_index>();

View file

@ -39,10 +39,25 @@ using namespace graphene::chain;
BOOST_FIXTURE_TEST_SUITE( operation_tests, database_fixture )
BOOST_AUTO_TEST_CASE( feed_limit_logic_test )
{
try {
asset usd(100,1);
asset core(200,1);
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() );
} catch (fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE( call_order_update_test )
{
try {
BOOST_TEST_MESSAGE("creating actors dan and sam" );
ACTORS((dan)(sam));
const auto& bitusd = create_bitasset("BITUSD");
const auto& core = asset_id_type()(db);
@ -121,6 +136,315 @@ BOOST_AUTO_TEST_CASE( call_order_update_test )
}
}
/**
* This test sets up a situation where a margin call will be executed and ensures that
* it is properly filled.
*
* A margin call can happen in the following situation:
* 0. there exists a bid above the mas short squeeze price
* 1. highest bid is lower than the call price of an order
* 2. the asset is not a prediction market
* 3. there is a valid price feed
*/
BOOST_AUTO_TEST_CASE( margin_call_limit_test )
{ try {
ACTORS((buyer)(seller)(borrower)(borrower2)(feedproducer));
const auto& bitusd = create_bitasset("BITUSD");
const auto& core = asset_id_type()(db);
int64_t init_balance(1000000);
transfer(genesis_account, buyer_id, asset(init_balance));
transfer(genesis_account, borrower_id, asset(init_balance));
transfer(genesis_account, borrower2_id, asset(init_balance));
update_feed_producers( bitusd, {feedproducer.id} );
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(5000), asset(10000), default_call_price );
borrow( borrower2, bitusd.amount(5000), asset(30000), default_call_price );
elog( "selling USD at 1:1, should be ok because we have 2:1 collateral" );
create_sell_order( borrower, bitusd.amount(5000), core.amount(5000) );
elog( "buying USD at 1:1" );
create_sell_order( buyer, core.amount(5000), bitusd.amount(5000) );
BOOST_REQUIRE_EQUAL( get_balance( borrower, bitusd ), 0 );
BOOST_REQUIRE_EQUAL( get_balance( buyer, bitusd ), 4950 ); // 1% market fee
BOOST_REQUIRE_EQUAL( get_balance( borrower, core ), init_balance - 10000 + 5000 );
BOOST_REQUIRE_EQUAL( get_balance( buyer, core ), init_balance - 5000 );
ilog( "print call orders..." );
print_call_orders();
ilog( "print market..." );
print_market( "", "" );
// sell the BitUSD for 50% more which represents a 33% fall in the value of the collateral
const auto order = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1400) );
const auto order2 = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1600) );
const auto order3 = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1700) );
const auto order4 = create_sell_order( borrower2, bitusd.amount(1000), core.amount(1800) );
const auto order5 = create_sell_order( borrower2, bitusd.amount(950), core.amount(1850) );
ilog( "print call orders..." );
print_call_orders();
ilog( "print market..." );
print_market( "", "" );
FC_ASSERT( order, "order should not match because sell price is below short squeeze protection" );
wdump((bitusd.bitasset_data(db).current_feed.max_short_squeeze_price().to_real()));
wdump((bitusd.bitasset_data(db).current_feed.maintenance_price().to_real()));
wdump(((order->sell_price).to_real()));
// update the feed to indicate a 33% drop in value
BOOST_TEST_MESSAGE( "Update feed to indicate CORE fell in value by 33%" );
current_feed.settlement_price = bitusd.amount( 100 ) / core.amount(200);
publish_feed( bitusd, feedproducer, current_feed );
/*
const asset_object& bitusd = create_bitasset( "BITUSD" );
const asset_object& core = get_asset( GRAPHENE_SYMBOL );
db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){
usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1);
});
const account_object& shorter1 = create_account( "shorter1" );
const account_object& shorter2 = create_account( "shorter2" );
const account_object& buyer1 = create_account( "buyer1" );
const account_object& buyer2 = create_account( "buyer2" );
transfer( genesis_account(db), shorter1, asset( 10000 ) );
transfer( genesis_account(db), shorter2, asset( 10000 ) );
transfer( genesis_account(db), buyer1, asset( 10000 ) );
transfer( genesis_account(db), buyer2, asset( 10000 ) );
BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) );
BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) );
BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee
const auto& call_index = db.get_index_type<call_order_index>().indices().get<by_account>();
const auto call_itr = call_index.find(boost::make_tuple(shorter1.id, bitusd.id));
BOOST_REQUIRE( call_itr != call_index.end() );
const call_order_object& call = *call_itr;
BOOST_CHECK(call.get_collateral() == core.amount(2000));
BOOST_CHECK(call.get_debt() == bitusd.amount(1000));
BOOST_CHECK(call.call_price == price(core.amount(1500), bitusd.amount(1000)));
BOOST_CHECK_EQUAL(get_balance(shorter1, core), 9000);
ilog( "=================================== START===================================\n\n");
// this should cause the highest bid to below the margin call threshold
// which means it should be filled by the cover
auto unmatched = create_sell_order( buyer1, bitusd.amount(495), core.amount(750) );
if( unmatched ) edump((*unmatched));
BOOST_CHECK( !unmatched );
BOOST_CHECK(call.get_debt() == bitusd.amount(505));
BOOST_CHECK(call.get_collateral() == core.amount(1250));
auto below_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(1));
BOOST_REQUIRE(below_call_price);
auto above_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(303));
BOOST_REQUIRE(above_call_price);
auto above_id = above_call_price->id;
cancel_limit_order(*below_call_price);
BOOST_CHECK_THROW(db.get_object(above_id), fc::exception);
BOOST_CHECK(call.get_debt() == bitusd.amount(305));
BOOST_CHECK(call.get_collateral() == core.amount(947));
below_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(1));
BOOST_REQUIRE(below_call_price);
auto below_id = below_call_price->id;
above_call_price = create_sell_order(buyer1, bitusd.amount(95), core.amount(144));
BOOST_REQUIRE(above_call_price);
above_id = above_call_price->id;
auto match_below_call = create_sell_order(buyer2, core.amount(1), bitusd.amount(200));
BOOST_CHECK(!match_below_call);
BOOST_CHECK_THROW(db.get_object(above_id), fc::exception);
BOOST_CHECK_THROW(db.get_object(below_id), fc::exception);
BOOST_CHECK(call.get_debt() == bitusd.amount(210));
BOOST_CHECK(call.get_collateral() == core.amount(803));
*/
} catch( const fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE( margin_call_limit_test_protected )
{ try {
FC_ASSERT( !"TODO - Reimplement with new short semantics" );
/*
const asset_object& bitusd = create_bitasset( "BITUSD" );
const asset_object& core = get_asset( GRAPHENE_SYMBOL );
db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){
usd.current_feed.call_limit = core.amount(1) / bitusd.amount(1);
});
const account_object& shorter1 = create_account( "shorter1" );
const account_object& shorter2 = create_account( "shorter2" );
const account_object& buyer1 = create_account( "buyer1" );
const account_object& buyer2 = create_account( "buyer2" );
transfer( genesis_account(db), shorter1, asset( 10000 ) );
transfer( genesis_account(db), shorter2, asset( 10000 ) );
transfer( genesis_account(db), buyer1, asset( 10000 ) );
transfer( genesis_account(db), buyer2, asset( 10000 ) );
BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) );
BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) );
BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee
ilog( "=================================== START===================================\n\n");
// this should cause the highest bid to below the margin call threshold
// which means it should be filled by the cover
auto unmatched = create_sell_order( buyer1, bitusd.amount(990), core.amount(1500) );
if( unmatched ) edump((*unmatched));
BOOST_REQUIRE( unmatched );
*/
} catch( const fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE( dont_margin_call_limit_test )
{ try {
FC_ASSERT( !"TODO - Reimplement with new short semantics" );
/*
const asset_object& bitusd = create_bitasset( "BITUSD" );
const asset_object& core = get_asset( GRAPHENE_SYMBOL );
db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){
usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1);
});
const account_object& shorter1 = create_account( "shorter1" );
const account_object& shorter2 = create_account( "shorter2" );
const account_object& buyer1 = create_account( "buyer1" );
const account_object& buyer2 = create_account( "buyer2" );
transfer( genesis_account(db), shorter1, asset( 10000 ) );
transfer( genesis_account(db), shorter2, asset( 10000 ) );
transfer( genesis_account(db), buyer1, asset( 10000 ) );
transfer( genesis_account(db), buyer2, asset( 10000 ) );
BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) );
BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) );
BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee
// this should cause the highest bid to below the margin call threshold
// which means it should be filled by the cover
auto unmatched = create_sell_order( buyer1, bitusd.amount(990), core.amount(1100) );
if( unmatched ) edump((*unmatched));
BOOST_REQUIRE( unmatched );
*/
} catch( const fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE( margin_call_short_test )
{ try {
FC_ASSERT( !"TODO - Reimplement with new short semantics" );
/*
const asset_object& bitusd = create_bitasset( "BITUSD" );
const asset_object& core = get_asset( GRAPHENE_SYMBOL );
db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){
usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1);
});
const account_object& shorter1 = create_account( "shorter1" );
const account_object& shorter2 = create_account( "shorter2" );
const account_object& buyer1 = create_account( "buyer1" );
const account_object& buyer2 = create_account( "buyer2" );
transfer( genesis_account(db), shorter1, asset( 10000 ) );
transfer( genesis_account(db), shorter2, asset( 10000 ) );
transfer( genesis_account(db), buyer1, asset( 10000 ) );
transfer( genesis_account(db), buyer2, asset( 10000 ) );
BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) );
BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) );
BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee
ilog( "=================================== START===================================\n\n");
// this should cause the highest bid to below the margin call threshold
// which means it should be filled by the cover
auto unmatched = create_short( buyer1, bitusd.amount(990), core.amount(1500) );
if( unmatched ) edump((*unmatched));
BOOST_REQUIRE( !unmatched );
*/
} catch( const fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE( margin_call_short_test_limit_protected )
{ try {
FC_ASSERT( !"TODO - Reimplement with new short semantics" );
/*
const asset_object& bitusd = create_bitasset( "BITUSD" );
const asset_object& core = get_asset( GRAPHENE_SYMBOL );
db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){
usd.current_feed.call_limit = core.amount(3) / bitusd.amount(4);
});
const account_object& shorter1 = create_account( "shorter1" );
const account_object& shorter2 = create_account( "shorter2" );
const account_object& buyer1 = create_account( "buyer1" );
const account_object& buyer2 = create_account( "buyer2" );
transfer( genesis_account(db), shorter1, asset( 10000 ) );
transfer( genesis_account(db), shorter2, asset( 10000 ) );
transfer( genesis_account(db), buyer1, asset( 10000 ) );
transfer( genesis_account(db), buyer2, asset( 10000 ) );
BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) );
BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) );
BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee
ilog( "=================================== START===================================\n\n");
// this should cause the highest bid to below the margin call threshold
// which means it should be filled by the cover
auto unmatched = create_short( buyer1, bitusd.amount(990), core.amount(1500) );
if( unmatched ) edump((*unmatched));
BOOST_REQUIRE( unmatched );
*/
} catch( const fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE( create_account_test )
{
try {
@ -947,234 +1271,6 @@ BOOST_AUTO_TEST_CASE( trade_amount_equals_zero )
}
}
BOOST_AUTO_TEST_CASE( margin_call_limit_test )
{ try {
FC_ASSERT( !"TODO - Reimplement with new short semantics" );
/*
const asset_object& bitusd = create_bitasset( "BITUSD" );
const asset_object& core = get_asset( GRAPHENE_SYMBOL );
db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){
usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1);
});
const account_object& shorter1 = create_account( "shorter1" );
const account_object& shorter2 = create_account( "shorter2" );
const account_object& buyer1 = create_account( "buyer1" );
const account_object& buyer2 = create_account( "buyer2" );
transfer( genesis_account(db), shorter1, asset( 10000 ) );
transfer( genesis_account(db), shorter2, asset( 10000 ) );
transfer( genesis_account(db), buyer1, asset( 10000 ) );
transfer( genesis_account(db), buyer2, asset( 10000 ) );
BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) );
BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) );
BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee
const auto& call_index = db.get_index_type<call_order_index>().indices().get<by_account>();
const auto call_itr = call_index.find(boost::make_tuple(shorter1.id, bitusd.id));
BOOST_REQUIRE( call_itr != call_index.end() );
const call_order_object& call = *call_itr;
BOOST_CHECK(call.get_collateral() == core.amount(2000));
BOOST_CHECK(call.get_debt() == bitusd.amount(1000));
BOOST_CHECK(call.call_price == price(core.amount(1500), bitusd.amount(1000)));
BOOST_CHECK_EQUAL(get_balance(shorter1, core), 9000);
ilog( "=================================== START===================================\n\n");
// this should cause the highest bid to below the margin call threshold
// which means it should be filled by the cover
auto unmatched = create_sell_order( buyer1, bitusd.amount(495), core.amount(750) );
if( unmatched ) edump((*unmatched));
BOOST_CHECK( !unmatched );
BOOST_CHECK(call.get_debt() == bitusd.amount(505));
BOOST_CHECK(call.get_collateral() == core.amount(1250));
auto below_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(1));
BOOST_REQUIRE(below_call_price);
auto above_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(303));
BOOST_REQUIRE(above_call_price);
auto above_id = above_call_price->id;
cancel_limit_order(*below_call_price);
BOOST_CHECK_THROW(db.get_object(above_id), fc::exception);
BOOST_CHECK(call.get_debt() == bitusd.amount(305));
BOOST_CHECK(call.get_collateral() == core.amount(947));
below_call_price = create_sell_order(buyer1, bitusd.amount(200), core.amount(1));
BOOST_REQUIRE(below_call_price);
auto below_id = below_call_price->id;
above_call_price = create_sell_order(buyer1, bitusd.amount(95), core.amount(144));
BOOST_REQUIRE(above_call_price);
above_id = above_call_price->id;
auto match_below_call = create_sell_order(buyer2, core.amount(1), bitusd.amount(200));
BOOST_CHECK(!match_below_call);
BOOST_CHECK_THROW(db.get_object(above_id), fc::exception);
BOOST_CHECK_THROW(db.get_object(below_id), fc::exception);
BOOST_CHECK(call.get_debt() == bitusd.amount(210));
BOOST_CHECK(call.get_collateral() == core.amount(803));
*/
} catch( const fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE( margin_call_limit_test_protected )
{ try {
FC_ASSERT( !"TODO - Reimplement with new short semantics" );
/*
const asset_object& bitusd = create_bitasset( "BITUSD" );
const asset_object& core = get_asset( GRAPHENE_SYMBOL );
db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){
usd.current_feed.call_limit = core.amount(1) / bitusd.amount(1);
});
const account_object& shorter1 = create_account( "shorter1" );
const account_object& shorter2 = create_account( "shorter2" );
const account_object& buyer1 = create_account( "buyer1" );
const account_object& buyer2 = create_account( "buyer2" );
transfer( genesis_account(db), shorter1, asset( 10000 ) );
transfer( genesis_account(db), shorter2, asset( 10000 ) );
transfer( genesis_account(db), buyer1, asset( 10000 ) );
transfer( genesis_account(db), buyer2, asset( 10000 ) );
BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) );
BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) );
BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee
ilog( "=================================== START===================================\n\n");
// this should cause the highest bid to below the margin call threshold
// which means it should be filled by the cover
auto unmatched = create_sell_order( buyer1, bitusd.amount(990), core.amount(1500) );
if( unmatched ) edump((*unmatched));
BOOST_REQUIRE( unmatched );
*/
} catch( const fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE( dont_margin_call_limit_test )
{ try {
FC_ASSERT( !"TODO - Reimplement with new short semantics" );
/*
const asset_object& bitusd = create_bitasset( "BITUSD" );
const asset_object& core = get_asset( GRAPHENE_SYMBOL );
db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){
usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1);
});
const account_object& shorter1 = create_account( "shorter1" );
const account_object& shorter2 = create_account( "shorter2" );
const account_object& buyer1 = create_account( "buyer1" );
const account_object& buyer2 = create_account( "buyer2" );
transfer( genesis_account(db), shorter1, asset( 10000 ) );
transfer( genesis_account(db), shorter2, asset( 10000 ) );
transfer( genesis_account(db), buyer1, asset( 10000 ) );
transfer( genesis_account(db), buyer2, asset( 10000 ) );
BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) );
BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) );
BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee
// this should cause the highest bid to below the margin call threshold
// which means it should be filled by the cover
auto unmatched = create_sell_order( buyer1, bitusd.amount(990), core.amount(1100) );
if( unmatched ) edump((*unmatched));
BOOST_REQUIRE( unmatched );
*/
} catch( const fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE( margin_call_short_test )
{ try {
FC_ASSERT( !"TODO - Reimplement with new short semantics" );
/*
const asset_object& bitusd = create_bitasset( "BITUSD" );
const asset_object& core = get_asset( GRAPHENE_SYMBOL );
db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){
usd.current_feed.call_limit = core.amount(3) / bitusd.amount(1);
});
const account_object& shorter1 = create_account( "shorter1" );
const account_object& shorter2 = create_account( "shorter2" );
const account_object& buyer1 = create_account( "buyer1" );
const account_object& buyer2 = create_account( "buyer2" );
transfer( genesis_account(db), shorter1, asset( 10000 ) );
transfer( genesis_account(db), shorter2, asset( 10000 ) );
transfer( genesis_account(db), buyer1, asset( 10000 ) );
transfer( genesis_account(db), buyer2, asset( 10000 ) );
BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) );
BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) );
BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee
ilog( "=================================== START===================================\n\n");
// this should cause the highest bid to below the margin call threshold
// which means it should be filled by the cover
auto unmatched = create_short( buyer1, bitusd.amount(990), core.amount(1500) );
if( unmatched ) edump((*unmatched));
BOOST_REQUIRE( !unmatched );
*/
} catch( const fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
BOOST_AUTO_TEST_CASE( margin_call_short_test_limit_protected )
{ try {
FC_ASSERT( !"TODO - Reimplement with new short semantics" );
/*
const asset_object& bitusd = create_bitasset( "BITUSD" );
const asset_object& core = get_asset( GRAPHENE_SYMBOL );
db.modify( bitusd.bitasset_data(db), [&]( asset_bitasset_data_object& usd ){
usd.current_feed.call_limit = core.amount(3) / bitusd.amount(4);
});
const account_object& shorter1 = create_account( "shorter1" );
const account_object& shorter2 = create_account( "shorter2" );
const account_object& buyer1 = create_account( "buyer1" );
const account_object& buyer2 = create_account( "buyer2" );
transfer( genesis_account(db), shorter1, asset( 10000 ) );
transfer( genesis_account(db), shorter2, asset( 10000 ) );
transfer( genesis_account(db), buyer1, asset( 10000 ) );
transfer( genesis_account(db), buyer2, asset( 10000 ) );
BOOST_REQUIRE( create_sell_order( buyer1, asset(1000), bitusd.amount(1000) ) );
BOOST_REQUIRE( !create_short( shorter1, bitusd.amount(1000), asset(1000) ) );
BOOST_REQUIRE_EQUAL( get_balance(buyer1, bitusd), 990 ); // 1000 - 1% fee
ilog( "=================================== START===================================\n\n");
// this should cause the highest bid to below the margin call threshold
// which means it should be filled by the cover
auto unmatched = create_short( buyer1, bitusd.amount(990), core.amount(1500) );
if( unmatched ) edump((*unmatched));
BOOST_REQUIRE( unmatched );
*/
} catch( const fc::exception& e) {
edump((e.to_detail_string()));
throw;
}
}
/**
* Create an order that cannot be filled immediately and have the