implement and test prediction market features
This commit is contained in:
parent
186b970689
commit
14b158364f
6 changed files with 121 additions and 27 deletions
|
|
@ -348,6 +348,8 @@ object_id_type asset_settle_evaluator::do_evaluate(const asset_settle_evaluator:
|
||||||
FC_ASSERT(asset_to_settle->is_market_issued());
|
FC_ASSERT(asset_to_settle->is_market_issued());
|
||||||
const auto& bitasset = asset_to_settle->bitasset_data(d);
|
const auto& bitasset = asset_to_settle->bitasset_data(d);
|
||||||
FC_ASSERT(asset_to_settle->can_force_settle() || bitasset.has_settlement() );
|
FC_ASSERT(asset_to_settle->can_force_settle() || bitasset.has_settlement() );
|
||||||
|
if( bitasset.is_prediction_market )
|
||||||
|
FC_ASSERT( bitasset.has_settlement(), "global settlement must occur before force settling a prediction market" );
|
||||||
FC_ASSERT(d.get_balance(d.get(op.account), *asset_to_settle) >= op.amount);
|
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();
|
return d.get_index_type<force_settlement_index>().get_next_id();
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ 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)
|
void_result call_order_update_evaluator::do_apply(const call_order_update_operation& o)
|
||||||
{
|
{ try {
|
||||||
database& d = db();
|
database& d = db();
|
||||||
//wdump( (_bitasset_data->current_feed) );
|
//wdump( (_bitasset_data->current_feed) );
|
||||||
|
|
||||||
|
|
@ -97,6 +97,7 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
|
||||||
auto& call_idx = d.get_index_type<call_order_index>().indices().get<by_account>();
|
auto& call_idx = d.get_index_type<call_order_index>().indices().get<by_account>();
|
||||||
auto itr = call_idx.find( boost::make_tuple(o.funding_account, o.delta_debt.asset_id) );
|
auto itr = call_idx.find( boost::make_tuple(o.funding_account, o.delta_debt.asset_id) );
|
||||||
const call_order_object* call_obj = nullptr;
|
const call_order_object* call_obj = nullptr;
|
||||||
|
|
||||||
if( itr == call_idx.end() )
|
if( itr == call_idx.end() )
|
||||||
{
|
{
|
||||||
FC_ASSERT( o.delta_collateral.amount > 0 );
|
FC_ASSERT( o.delta_collateral.amount > 0 );
|
||||||
|
|
@ -127,34 +128,38 @@ void_result call_order_update_evaluator::do_apply(const call_order_update_operat
|
||||||
d.remove( *call_obj );
|
d.remove( *call_obj );
|
||||||
return void_result();
|
return void_result();
|
||||||
}
|
}
|
||||||
auto collateral = call_obj->get_collateral();
|
|
||||||
|
|
||||||
auto mp = _bitasset_data->current_feed.maintenance_price();
|
/** then we must check for margin calls and other issues */
|
||||||
|
if( !_bitasset_data->is_prediction_market )
|
||||||
// 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 );
|
|
||||||
|
|
||||||
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
|
|
||||||
// limit orders available that could be used to fill the order.
|
|
||||||
if( d.check_call_orders( *_debt_asset, false ) )
|
|
||||||
{
|
{
|
||||||
FC_ASSERT( !d.find_object( call_order_id ), "If updating the call order triggers a margin call, then it must completely cover the order" );
|
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 );
|
||||||
|
|
||||||
|
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
|
||||||
|
// limit orders available that could be used to fill the order.
|
||||||
|
if( d.check_call_orders( *_debt_asset, false ) )
|
||||||
|
{
|
||||||
|
FC_ASSERT( !d.find_object( call_order_id ), "If updating the call order triggers a margin call, then it must completely cover the order" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return void_result();
|
return void_result();
|
||||||
}
|
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||||
|
|
||||||
} } // graphene::chain
|
} } // graphene::chain
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,6 @@ void database::globally_settle_asset( const asset_object& mia, const price& sett
|
||||||
while( call_itr != call_end )
|
while( call_itr != call_end )
|
||||||
{
|
{
|
||||||
auto pays = call_itr->get_debt() * settlement_price;
|
auto pays = call_itr->get_debt() * settlement_price;
|
||||||
wdump( (call_itr->get_debt() ) );
|
|
||||||
collateral_gathered += pays;
|
collateral_gathered += pays;
|
||||||
const auto& order = *call_itr;
|
const auto& order = *call_itr;
|
||||||
++call_itr;
|
++call_itr;
|
||||||
|
|
@ -321,8 +320,8 @@ bool database::check_call_orders( const asset_object& mia, bool enable_black_swa
|
||||||
{ try {
|
{ try {
|
||||||
if( !mia.is_market_issued() ) return false;
|
if( !mia.is_market_issued() ) return false;
|
||||||
const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
|
const asset_bitasset_data_object& bitasset = mia.bitasset_data(*this);
|
||||||
if( bitasset.current_feed.settlement_price.is_null() ) return false;
|
|
||||||
if( bitasset.is_prediction_market ) return false;
|
if( bitasset.is_prediction_market ) return false;
|
||||||
|
if( bitasset.current_feed.settlement_price.is_null() ) return false;
|
||||||
|
|
||||||
const call_order_index& call_index = get_index_type<call_order_index>();
|
const call_order_index& call_index = get_index_type<call_order_index>();
|
||||||
const auto& call_price_index = call_index.indices().get<by_price>();
|
const auto& call_price_index = call_index.indices().get<by_price>();
|
||||||
|
|
|
||||||
|
|
@ -398,6 +398,32 @@ const asset_object& database_fixture::create_bitasset(
|
||||||
return db.get<asset_object>(ptx.operation_results[0].get<object_id_type>());
|
return db.get<asset_object>(ptx.operation_results[0].get<object_id_type>());
|
||||||
} FC_CAPTURE_AND_RETHROW( (name)(flags) ) }
|
} FC_CAPTURE_AND_RETHROW( (name)(flags) ) }
|
||||||
|
|
||||||
|
const asset_object& database_fixture::create_prediction_market(
|
||||||
|
const string& name,
|
||||||
|
account_id_type issuer /* = 1 */,
|
||||||
|
uint16_t market_fee_percent /* = 100 */ /* 1% */,
|
||||||
|
uint16_t flags /* = charge_market_fee */
|
||||||
|
)
|
||||||
|
{ try {
|
||||||
|
asset_create_operation creator;
|
||||||
|
creator.issuer = issuer;
|
||||||
|
creator.fee = asset();
|
||||||
|
creator.symbol = name;
|
||||||
|
creator.common_options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;
|
||||||
|
creator.precision = 2;
|
||||||
|
creator.common_options.market_fee_percent = market_fee_percent;
|
||||||
|
creator.common_options.issuer_permissions = flags | global_settle;
|
||||||
|
creator.common_options.flags = flags & ~global_settle;
|
||||||
|
creator.common_options.core_exchange_rate = price({asset(1,1),asset(1)});
|
||||||
|
creator.bitasset_options = asset_object::bitasset_options();
|
||||||
|
creator.is_prediction_market = true;
|
||||||
|
trx.operations.push_back(std::move(creator));
|
||||||
|
trx.validate();
|
||||||
|
processed_transaction ptx = db.push_transaction(trx, ~0);
|
||||||
|
trx.operations.clear();
|
||||||
|
return db.get<asset_object>(ptx.operation_results[0].get<object_id_type>());
|
||||||
|
} FC_CAPTURE_AND_RETHROW( (name)(flags) ) }
|
||||||
|
|
||||||
const asset_object& database_fixture::create_user_issued_asset( const string& name )
|
const asset_object& database_fixture::create_user_issued_asset( const string& name )
|
||||||
{
|
{
|
||||||
asset_create_operation creator;
|
asset_create_operation creator;
|
||||||
|
|
@ -661,6 +687,21 @@ void database_fixture::publish_feed( const asset_object& mia, const account_obj
|
||||||
trx.operations.clear();
|
trx.operations.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void database_fixture::force_global_settle( const asset_object& what, const price& p )
|
||||||
|
{ try {
|
||||||
|
trx.set_expiration(db.head_block_time() + fc::minutes(1));
|
||||||
|
trx.operations.clear();
|
||||||
|
asset_global_settle_operation sop;
|
||||||
|
sop.issuer = what.issuer;
|
||||||
|
sop.asset_to_settle = what.id;
|
||||||
|
sop.settle_price = p;
|
||||||
|
trx.operations.push_back(sop);
|
||||||
|
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( (what)(p) ) }
|
||||||
|
|
||||||
void database_fixture::force_settle( const account_object& who, asset what )
|
void database_fixture::force_settle( const account_object& who, asset what )
|
||||||
{ try {
|
{ try {
|
||||||
trx.set_expiration(db.head_block_time() + fc::minutes(1));
|
trx.set_expiration(db.head_block_time() + fc::minutes(1));
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,7 @@ struct database_fixture {
|
||||||
key_id_type key = key_id_type()
|
key_id_type key = key_id_type()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void force_global_settle( const asset_object& what, const price& p );
|
||||||
void force_settle( const account_object& who, asset what );
|
void force_settle( const account_object& who, asset what );
|
||||||
void update_feed_producers( const asset_object& mia, flat_set<account_id_type> producers );
|
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 publish_feed( const asset_object& mia, const account_object& by, const price_feed& f );
|
||||||
|
|
@ -159,6 +160,10 @@ struct database_fixture {
|
||||||
account_id_type issuer = account_id_type(1),
|
account_id_type issuer = account_id_type(1),
|
||||||
uint16_t market_fee_percent = 100 /*1%*/,
|
uint16_t market_fee_percent = 100 /*1%*/,
|
||||||
uint16_t flags = charge_market_fee);
|
uint16_t flags = charge_market_fee);
|
||||||
|
const asset_object& create_prediction_market(const string& name,
|
||||||
|
account_id_type issuer = account_id_type(1),
|
||||||
|
uint16_t market_fee_percent = 100 /*1%*/,
|
||||||
|
uint16_t flags = charge_market_fee);
|
||||||
const asset_object& create_user_issued_asset( const string& name );
|
const asset_object& create_user_issued_asset( const string& name );
|
||||||
void issue_uia( const account_object& recipient, asset amount );
|
void issue_uia( const account_object& recipient, asset amount );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -259,6 +259,48 @@ BOOST_AUTO_TEST_CASE( black_swan )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE( prediction_market )
|
||||||
|
{ try {
|
||||||
|
ACTORS((judge)(dan)(nathan));
|
||||||
|
|
||||||
|
const auto& pmark = create_prediction_market("PMARK", judge_id);
|
||||||
|
const auto& core = asset_id_type()(db);
|
||||||
|
|
||||||
|
int64_t init_balance(1000000);
|
||||||
|
transfer(genesis_account, judge_id, asset(init_balance));
|
||||||
|
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_TEST_MESSAGE( "Open position with equal collateral" );
|
||||||
|
borrow( dan, pmark.amount(1000), asset(1000), default_call_price );
|
||||||
|
|
||||||
|
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_TEST_MESSAGE( "Cover half of position with equal ammounts" );
|
||||||
|
cover( dan, pmark.amount(500), asset(500), default_call_price );
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
force_global_settle( pmark, pmark.amount(100) / core.amount(95) );
|
||||||
|
|
||||||
|
BOOST_TEST_MESSAGE( "Verify that forced settlment succeedes after global settlement" );
|
||||||
|
force_settle( dan, pmark.amount(100) );
|
||||||
|
|
||||||
|
} catch( const fc::exception& e) {
|
||||||
|
edump((e.to_detail_string()));
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE( create_account_test )
|
BOOST_AUTO_TEST_CASE( create_account_test )
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue