2018-02-07 15:12:58 +00:00
# define DEFAULT_LOGGER "betting"
2017-03-22 16:03:08 +00:00
# include <graphene/chain/database.hpp>
# include <graphene/chain/account_object.hpp>
# include <graphene/chain/asset_object.hpp>
# include <graphene/chain/betting_market_object.hpp>
2017-03-24 05:33:05 +00:00
# include <graphene/chain/event_object.hpp>
2017-03-22 16:03:08 +00:00
2017-03-24 15:51:35 +00:00
# include <boost/range/iterator_range.hpp>
2018-02-07 15:12:58 +00:00
# include <boost/range/combine.hpp>
# include <boost/tuple/tuple.hpp>
2017-03-24 15:51:35 +00:00
2017-03-22 16:03:08 +00:00
namespace graphene { namespace chain {
void database : : cancel_bet ( const bet_object & bet , bool create_virtual_op )
{
2017-03-22 19:04:11 +00:00
asset amount_to_refund = bet . amount_to_bet ;
2017-03-22 16:03:08 +00:00
//TODO: update global statistics
2017-08-09 15:12:59 +00:00
adjust_balance ( bet . bettor_id , amount_to_refund ) ;
2017-03-22 16:03:08 +00:00
if ( create_virtual_op )
2017-08-01 19:40:49 +00:00
{
bet_canceled_operation bet_canceled_virtual_op ( bet . bettor_id , bet . id ,
2017-08-09 15:12:59 +00:00
bet . amount_to_bet ) ;
2017-08-01 19:40:49 +00:00
//idump((bet_canceled_virtual_op));
push_applied_operation ( std : : move ( bet_canceled_virtual_op ) ) ;
}
2017-03-22 16:03:08 +00:00
remove ( bet ) ;
}
2017-03-24 04:09:43 +00:00
void database : : cancel_all_unmatched_bets_on_betting_market ( const betting_market_object & betting_market )
{
2017-03-24 14:40:53 +00:00
const auto & bet_odds_idx = get_index_type < bet_object_index > ( ) . indices ( ) . get < by_odds > ( ) ;
2017-08-15 22:44:09 +00:00
// first, cancel all bets on the active books
2017-03-24 14:40:53 +00:00
auto book_itr = bet_odds_idx . lower_bound ( std : : make_tuple ( betting_market . id ) ) ;
auto book_end = bet_odds_idx . upper_bound ( std : : make_tuple ( betting_market . id ) ) ;
while ( book_itr ! = book_end )
{
auto old_book_itr = book_itr ;
+ + book_itr ;
cancel_bet ( * old_book_itr , true ) ;
}
2017-08-15 22:44:09 +00:00
// then, cancel any delayed bets on that market. We don't have an index for
// that, so walk through all delayed bets
book_itr = bet_odds_idx . begin ( ) ;
while ( book_itr ! = bet_odds_idx . end ( ) & &
book_itr - > end_of_delay )
{
auto old_book_itr = book_itr ;
+ + book_itr ;
if ( old_book_itr - > betting_market_id = = betting_market . id )
cancel_bet ( * old_book_itr , true ) ;
}
2017-03-24 04:09:43 +00:00
}
2018-02-07 15:12:58 +00:00
void database : : validate_betting_market_group_resolutions ( const betting_market_group_object & betting_market_group ,
const std : : map < betting_market_id_type , betting_market_resolution_type > & resolutions )
2017-03-24 05:33:05 +00:00
{
2017-03-24 15:51:35 +00:00
auto & betting_market_index = get_index_type < betting_market_object_index > ( ) . indices ( ) . get < by_betting_market_group_id > ( ) ;
2018-02-07 15:12:58 +00:00
auto betting_markets_in_group = boost : : make_iterator_range ( betting_market_index . equal_range ( betting_market_group . id ) ) ;
2017-03-24 15:51:35 +00:00
2018-02-07 15:12:58 +00:00
// we must have one resolution for each betting market
FC_ASSERT ( resolutions . size ( ) = = boost : : size ( betting_markets_in_group ) ,
" You must publish resolutions for all ${size} markets in the group, you published ${published} " , ( " size " , boost : : size ( betting_markets_in_group ) ) ( " published " , resolutions . size ( ) ) ) ;
2017-03-24 05:33:05 +00:00
2018-02-07 15:12:58 +00:00
// both are sorted by id, we can walk through both and verify that they match
unsigned number_of_wins = 0 ;
unsigned number_of_cancels = 0 ;
for ( const auto & zipped : boost : : combine ( resolutions , betting_markets_in_group ) )
{
const auto & resolution = boost : : get < 0 > ( zipped ) ;
const auto & betting_market = boost : : get < 1 > ( zipped ) ;
FC_ASSERT ( resolution . first = = betting_market . id , " Missing resolution for betting market ${id} " , ( " id " , betting_market . id ) ) ;
if ( resolution . second = = betting_market_resolution_type : : cancel )
+ + number_of_cancels ;
else if ( resolution . second = = betting_market_resolution_type : : win )
+ + number_of_wins ;
else
FC_ASSERT ( resolution . second = = betting_market_resolution_type : : not_win ) ;
}
if ( number_of_cancels ! = 0 )
FC_ASSERT ( number_of_cancels = = resolutions . size ( ) , " You must cancel all betting markets or none of the betting markets in the group " ) ;
else
FC_ASSERT ( number_of_wins = = 1 , " There must be exactly one winning market " ) ;
2017-07-04 14:55:34 +00:00
}
2017-03-24 04:09:43 +00:00
2017-07-19 08:29:51 +00:00
void database : : cancel_all_unmatched_bets_on_betting_market_group ( const betting_market_group_object & betting_market_group )
{
auto & betting_market_index = get_index_type < betting_market_object_index > ( ) . indices ( ) . get < by_betting_market_group_id > ( ) ;
auto betting_market_itr = betting_market_index . lower_bound ( betting_market_group . id ) ;
while ( betting_market_itr ! = betting_market_index . end ( ) & & betting_market_itr - > group_id = = betting_market_group . id )
{
const betting_market_object & betting_market = * betting_market_itr ;
+ + betting_market_itr ;
cancel_all_unmatched_bets_on_betting_market ( betting_market ) ;
}
}
2017-07-04 14:55:34 +00:00
void database : : resolve_betting_market_group ( const betting_market_group_object & betting_market_group ,
2018-02-07 15:12:58 +00:00
const std : : map < betting_market_id_type , betting_market_resolution_type > & resolutions )
2017-07-04 14:55:34 +00:00
{
2018-02-07 15:12:58 +00:00
auto & betting_market_index = get_index_type < betting_market_object_index > ( ) . indices ( ) . get < by_betting_market_group_id > ( ) ;
auto betting_markets_in_group = boost : : make_iterator_range ( betting_market_index . equal_range ( betting_market_group . id ) ) ;
bool group_was_canceled = resolutions . begin ( ) - > second = = betting_market_resolution_type : : cancel ;
if ( group_was_canceled )
modify ( betting_market_group , [ group_was_canceled , this ] ( betting_market_group_object & betting_market_group_obj ) {
betting_market_group_obj . on_canceled_event ( * this , false ) ; // this cancels the betting markets
} ) ;
else {
// TODO: this should be pushed into the bmg's on_graded_event
// both are sorted by id, we can walk through both and verify that they match
for ( const auto & zipped : boost : : combine ( resolutions , betting_markets_in_group ) )
{
const auto & resolution = boost : : get < 0 > ( zipped ) ;
const auto & betting_market = boost : : get < 1 > ( zipped ) ;
modify ( betting_market , [ this , & resolution ] ( betting_market_object & betting_market_obj ) {
betting_market_obj . on_graded_event ( * this , resolution . second ) ;
} ) ;
}
modify ( betting_market_group , [ group_was_canceled , this ] ( betting_market_group_object & betting_market_group_obj ) {
betting_market_group_obj . on_graded_event ( * this ) ;
} ) ;
}
}
2017-07-09 22:39:53 +00:00
2018-02-07 15:12:58 +00:00
void database : : settle_betting_market_group ( const betting_market_group_object & betting_market_group )
{
ilog ( " Settling betting market group ${id} " , ( " id " , betting_market_group . id ) ) ;
2017-07-09 22:39:53 +00:00
// we pay the rake fee to the dividend distribution account for the core asset, go ahead
// and look up that account now
fc : : optional < account_id_type > rake_account_id ;
const asset_object & core_asset_obj = asset_id_type ( 0 ) ( * this ) ;
if ( core_asset_obj . dividend_data_id )
{
const asset_dividend_data_object & core_asset_dividend_data_obj = ( * core_asset_obj . dividend_data_id ) ( * this ) ;
rake_account_id = core_asset_dividend_data_obj . dividend_distribution_account ;
}
2018-02-07 15:12:58 +00:00
// collect the resolutions of all markets in the BMG: they were previously published and
// stored in the individual betting markets
std : : map < betting_market_id_type , betting_market_resolution_type > resolutions_by_market_id ;
2017-07-09 22:39:53 +00:00
// collecting bettors and their positions
std : : map < account_id_type , std : : vector < const betting_market_position_object * > > bettor_positions_map ;
auto & betting_market_index = get_index_type < betting_market_object_index > ( ) . indices ( ) . get < by_betting_market_group_id > ( ) ;
2017-07-26 19:57:07 +00:00
// [ROL] it seems to be my mistake - wrong index used
//auto& position_index = get_index_type<betting_market_position_index>().indices().get<by_bettor_betting_market>();
auto & position_index = get_index_type < betting_market_position_index > ( ) . indices ( ) . get < by_betting_market_bettor > ( ) ;
2017-07-09 22:39:53 +00:00
auto betting_market_itr = betting_market_index . lower_bound ( betting_market_group . id ) ;
while ( betting_market_itr ! = betting_market_index . end ( ) & & betting_market_itr - > group_id = = betting_market_group . id )
{
const betting_market_object & betting_market = * betting_market_itr ;
2018-02-07 15:12:58 +00:00
FC_ASSERT ( betting_market_itr - > resolution , " Unexpected error settling betting market ${market_id}: no published resolution " ,
( " market_id " , betting_market_itr - > id ) ) ;
resolutions_by_market_id . emplace ( betting_market . id , * betting_market_itr - > resolution ) ;
2017-07-09 22:39:53 +00:00
+ + betting_market_itr ;
cancel_all_unmatched_bets_on_betting_market ( betting_market ) ;
2017-07-26 19:57:07 +00:00
auto position_itr = position_index . lower_bound ( betting_market . id ) ;
2017-07-09 22:39:53 +00:00
while ( position_itr ! = position_index . end ( ) & & position_itr - > betting_market_id = = betting_market . id )
{
const betting_market_position_object & position = * position_itr ;
+ + position_itr ;
bettor_positions_map [ position . bettor_id ] . push_back ( & position ) ;
}
}
// walking through bettors' positions and collecting winings and fees respecting asset_id
for ( const auto & bettor_positions_pair : bettor_positions_map )
{
2017-07-07 12:23:02 +00:00
uint16_t rake_fee_percentage = get_global_properties ( ) . parameters . betting_rake_fee_percentage ;
2017-07-09 22:39:53 +00:00
share_type net_profits ;
share_type payout_amounts ;
2017-07-04 14:55:34 +00:00
account_id_type bettor_id = bettor_positions_pair . first ;
const std : : vector < const betting_market_position_object * > & bettor_positions = bettor_positions_pair . second ;
for ( const betting_market_position_object * position : bettor_positions )
2017-03-23 23:35:10 +00:00
{
2017-07-09 22:39:53 +00:00
betting_market_resolution_type resolution ;
2018-02-07 15:12:58 +00:00
try
{
resolution = resolutions_by_market_id . at ( position - > betting_market_id ) ;
}
catch ( std : : out_of_range & )
2017-07-09 22:39:53 +00:00
{
2018-02-07 15:12:58 +00:00
FC_THROW_EXCEPTION ( fc : : key_not_found_exception , " Unexpected betting market ID, shouldn't happen " ) ;
2017-07-09 22:39:53 +00:00
}
2018-02-07 15:12:58 +00:00
///if (cancel)
/// resolution = betting_market_resolution_type::cancel;
///else
///{
/// // checked in evaluator, should never happen, see above
/// assert(resolutions.count(position->betting_market_id));
/// resolution = resolutions.at(position->betting_market_id);
///}
2017-07-09 22:39:53 +00:00
switch ( resolution )
{
case betting_market_resolution_type : : win :
{
share_type total_payout = position - > pay_if_payout_condition + position - > pay_if_not_canceled ;
payout_amounts + = total_payout ;
net_profits + = total_payout - position - > pay_if_canceled ;
break ;
}
case betting_market_resolution_type : : not_win :
{
share_type total_payout = position - > pay_if_not_payout_condition + position - > pay_if_not_canceled ;
payout_amounts + = total_payout ;
net_profits + = total_payout - position - > pay_if_canceled ;
break ;
}
case betting_market_resolution_type : : cancel :
2017-08-09 15:12:59 +00:00
payout_amounts + = position - > pay_if_canceled ;
2017-07-09 22:39:53 +00:00
break ;
default :
continue ;
}
remove ( * position ) ;
2017-07-04 14:55:34 +00:00
}
2017-03-24 15:51:35 +00:00
2017-07-09 22:39:53 +00:00
// pay the fees to the dividend-distribution account if net profit
share_type rake_amount ;
if ( net_profits . value > 0 & & rake_account_id )
2017-07-04 14:55:34 +00:00
{
2017-07-09 22:39:53 +00:00
rake_amount = ( ( fc : : uint128_t ( net_profits . value ) * rake_fee_percentage + GRAPHENE_100_PERCENT - 1 ) / GRAPHENE_100_PERCENT ) . to_uint64 ( ) ;
if ( rake_amount . value )
adjust_balance ( * rake_account_id , asset ( rake_amount , betting_market_group . asset_id ) ) ;
2017-07-04 14:55:34 +00:00
}
2017-07-09 22:39:53 +00:00
// pay winning - rake
adjust_balance ( bettor_id , asset ( payout_amounts - rake_amount , betting_market_group . asset_id ) ) ;
2017-07-26 19:57:07 +00:00
// [ROL]
2017-08-01 19:40:49 +00:00
//idump((payout_amounts)(net_profits.value)(rake_amount.value));
2017-07-09 22:39:53 +00:00
2017-07-04 14:55:34 +00:00
push_applied_operation ( betting_market_group_resolved_operation ( bettor_id ,
2017-07-09 22:39:53 +00:00
betting_market_group . id ,
2018-02-07 15:12:58 +00:00
resolutions_by_market_id ,
2017-07-09 22:39:53 +00:00
payout_amounts ,
2017-08-09 15:12:59 +00:00
rake_amount ) ) ;
2017-07-09 22:39:53 +00:00
}
2018-02-07 15:12:58 +00:00
// At this point, the betting market group will either be in the "graded" or "canceled" state,
// if it was graded, mark it as settled. if it's canceled, let it remain canceled.
bool was_canceled = betting_market_group . get_status ( ) = = betting_market_group_status : : canceled ;
if ( ! was_canceled )
modify ( betting_market_group , [ & ] ( betting_market_group_object & group ) {
group . on_settled_event ( * this ) ;
} ) ;
2017-07-09 22:39:53 +00:00
betting_market_itr = betting_market_index . lower_bound ( betting_market_group . id ) ;
2018-02-07 15:12:58 +00:00
while ( betting_market_itr ! = betting_market_index . end ( ) & & betting_market_itr - > group_id = = betting_market_group . id ) {
2017-07-09 22:39:53 +00:00
const betting_market_object & betting_market = * betting_market_itr ;
2018-02-07 15:12:58 +00:00
2017-07-09 22:39:53 +00:00
+ + betting_market_itr ;
2018-02-07 15:12:58 +00:00
dlog ( " removing betting market ${id} " , ( " id " , betting_market . id ) ) ;
remove ( betting_market ) ;
2017-03-23 23:35:10 +00:00
}
2018-02-07 15:12:58 +00:00
const event_object & event = betting_market_group . event_id ( * this ) ;
dlog ( " removing betting market group ${id} " , ( " id " , betting_market_group . id ) ) ;
remove ( betting_market_group ) ;
if ( event . get_status ( ) = = event_status : : canceled | |
event . get_status ( ) = = event_status : : settled ) {
dlog ( " removing event ${id} " , ( " id " , event . id ) ) ;
remove ( event ) ;
}
2017-04-04 21:02:19 +00:00
}
2017-08-09 15:12:59 +00:00
share_type adjust_betting_position ( database & db , account_id_type bettor_id , betting_market_id_type betting_market_id , bet_type back_or_lay , share_type bet_amount , share_type matched_amount )
2017-03-23 20:22:25 +00:00
{ try {
assert ( bet_amount > = 0 ) ;
share_type guaranteed_winnings_returned = 0 ;
if ( bet_amount = = 0 )
return guaranteed_winnings_returned ;
auto & index = db . get_index_type < betting_market_position_index > ( ) . indices ( ) . get < by_bettor_betting_market > ( ) ;
auto itr = index . find ( boost : : make_tuple ( bettor_id , betting_market_id ) ) ;
if ( itr = = index . end ( ) )
{
db . create < betting_market_position_object > ( [ & ] ( betting_market_position_object & position ) {
position . bettor_id = bettor_id ;
position . betting_market_id = betting_market_id ;
2017-03-24 14:40:53 +00:00
position . pay_if_payout_condition = back_or_lay = = bet_type : : back ? bet_amount + matched_amount : 0 ;
position . pay_if_not_payout_condition = back_or_lay = = bet_type : : lay ? bet_amount + matched_amount : 0 ;
2017-03-23 20:22:25 +00:00
position . pay_if_canceled = bet_amount ;
position . pay_if_not_canceled = 0 ;
// this should not be reducible
} ) ;
} else {
db . modify ( * itr , [ & ] ( betting_market_position_object & position ) {
assert ( position . bettor_id = = bettor_id ) ;
assert ( position . betting_market_id = = betting_market_id ) ;
2017-03-24 14:40:53 +00:00
position . pay_if_payout_condition + = back_or_lay = = bet_type : : back ? bet_amount + matched_amount : 0 ;
position . pay_if_not_payout_condition + = back_or_lay = = bet_type : : lay ? bet_amount + matched_amount : 0 ;
2017-03-23 20:22:25 +00:00
position . pay_if_canceled + = bet_amount ;
guaranteed_winnings_returned = position . reduce ( ) ;
} ) ;
}
return guaranteed_winnings_returned ;
} FC_CAPTURE_AND_RETHROW ( ( bettor_id ) ( betting_market_id ) ( bet_amount ) ) }
2017-03-31 15:10:37 +00:00
// called twice when a bet is matched, once for the taker, once for the maker
bool bet_was_matched ( database & db , const bet_object & bet ,
share_type amount_bet , share_type amount_matched ,
2017-08-30 21:18:58 +00:00
bet_multiplier_type actual_multiplier ,
bool refund_unmatched_portion )
2017-03-23 20:22:25 +00:00
{
// record their bet, modifying their position, and return any winnings
share_type guaranteed_winnings_returned = adjust_betting_position ( db , bet . bettor_id , bet . betting_market_id ,
2017-08-09 15:12:59 +00:00
bet . back_or_lay , amount_bet , amount_matched ) ;
2017-03-23 20:22:25 +00:00
db . adjust_balance ( bet . bettor_id , asset ( guaranteed_winnings_returned , bet . amount_to_bet . asset_id ) ) ;
// generate a virtual "match" op
asset asset_amount_bet ( amount_bet , bet . amount_to_bet . asset_id ) ;
2017-08-01 19:40:49 +00:00
2017-08-09 15:12:59 +00:00
bet_matched_operation bet_matched_virtual_op ( bet . bettor_id , bet . id ,
2017-08-01 19:40:49 +00:00
asset_amount_bet ,
actual_multiplier ,
guaranteed_winnings_returned ) ;
2017-08-09 15:12:59 +00:00
//edump((bet_matched_virtual_op));
2017-08-01 19:40:49 +00:00
db . push_applied_operation ( std : : move ( bet_matched_virtual_op ) ) ;
2017-03-23 20:22:25 +00:00
// update the bet on the books
if ( asset_amount_bet = = bet . amount_to_bet )
{
db . remove ( bet ) ;
return true ;
}
else
{
db . modify ( bet , [ & ] ( bet_object & bet_obj ) {
bet_obj . amount_to_bet - = asset_amount_bet ;
} ) ;
2017-08-30 21:18:58 +00:00
if ( refund_unmatched_portion )
{
db . cancel_bet ( bet ) ;
return true ;
}
else
return false ;
2017-03-23 20:22:25 +00:00
}
}
/**
* Matches the two orders ,
*
* @ return a bit field indicating which orders were filled ( and thus removed )
*
2017-03-31 15:10:37 +00:00
* 0 - no bet was matched ( this will never happen )
* 1 - taker_bet was filled and removed from the books
* 2 - maker_bet was filled and removed from the books
* 3 - both were filled and removed from the books
2017-03-23 20:22:25 +00:00
*/
int match_bet ( database & db , const bet_object & taker_bet , const bet_object & maker_bet )
{
assert ( taker_bet . amount_to_bet . asset_id = = maker_bet . amount_to_bet . asset_id ) ;
assert ( taker_bet . amount_to_bet . amount > 0 & & maker_bet . amount_to_bet . amount > 0 ) ;
2017-08-09 15:12:59 +00:00
assert ( taker_bet . back_or_lay = = bet_type : : back ? taker_bet . backer_multiplier < = maker_bet . backer_multiplier : taker_bet . backer_multiplier > = maker_bet . backer_multiplier ) ;
2017-03-23 20:22:25 +00:00
assert ( taker_bet . back_or_lay ! = maker_bet . back_or_lay ) ;
int result = 0 ;
2017-08-01 19:40:49 +00:00
//idump((taker_bet)(maker_bet));
// using the maker's odds, figure out how much of the maker's bet we would match, rounding down
// go ahead and get look up the ratio for the bet (a bet with odds 1.92 will have a ratio 25:23)
share_type back_odds_ratio ;
share_type lay_odds_ratio ;
std : : tie ( back_odds_ratio , lay_odds_ratio ) = maker_bet . get_ratio ( ) ;
// and make some shortcuts to get to the maker's and taker's side of the ratio
const share_type & maker_odds_ratio = maker_bet . back_or_lay = = bet_type : : back ? back_odds_ratio : lay_odds_ratio ;
const share_type & taker_odds_ratio = maker_bet . back_or_lay = = bet_type : : back ? lay_odds_ratio : back_odds_ratio ;
//idump((back_odds_ratio)(lay_odds_ratio));
//idump((maker_odds_ratio)(taker_odds_ratio));
// now figure out how much of the maker bet we'll consume. We don't yet know whether the maker or taker
// will be the limiting factor.
share_type maximum_taker_factor = taker_bet . amount_to_bet . amount / taker_odds_ratio ;
share_type maximum_maker_factor = maker_bet . amount_to_bet . amount / maker_odds_ratio ;
share_type maximum_factor = std : : min ( maximum_taker_factor , maximum_maker_factor ) ;
share_type maker_amount_to_match = maximum_factor * maker_odds_ratio ;
share_type taker_amount_to_match = maximum_factor * taker_odds_ratio ;
//idump((maker_amount_to_match)(taker_amount_to_match));
// TODO: analyze whether maximum_maker_amount_to_match can ever be zero here
assert ( maker_amount_to_match ! = 0 ) ;
if ( maker_amount_to_match = = 0 )
return 0 ;
# ifndef NDEBUG
assert ( taker_amount_to_match < = taker_bet . amount_to_bet . amount ) ;
assert ( taker_amount_to_match / taker_odds_ratio * taker_odds_ratio = = taker_amount_to_match ) ;
2017-03-23 20:22:25 +00:00
{
2017-08-01 19:40:49 +00:00
// verify we're getting the odds we expect
fc : : uint128_t payout_128 = maker_amount_to_match . value ;
payout_128 + = taker_amount_to_match . value ;
payout_128 * = GRAPHENE_BETTING_ODDS_PRECISION ;
payout_128 / = maker_bet . back_or_lay = = bet_type : : back ? maker_amount_to_match . value : taker_amount_to_match . value ;
assert ( payout_128 . to_uint64 ( ) = = maker_bet . backer_multiplier ) ;
2017-03-23 20:22:25 +00:00
}
2017-08-01 19:40:49 +00:00
# endif
2017-03-23 20:22:25 +00:00
2017-08-01 19:40:49 +00:00
//idump((taker_amount_to_match)(maker_amount_to_match));
2017-08-30 21:18:58 +00:00
// maker bets will always be an exact multiple of maker_odds_ratio, so they will either completely match or remain on the books
bool maker_bet_will_completely_match = maker_amount_to_match = = maker_bet . amount_to_bet . amount ;
// if the maker bet stays on the books, we need to make sure the taker bet is removed from the books (either it fills completely,
// or any un-filled amount is canceled)
result | = bet_was_matched ( db , taker_bet , taker_amount_to_match , maker_amount_to_match , maker_bet . backer_multiplier , ! maker_bet_will_completely_match ) ;
result | = bet_was_matched ( db , maker_bet , maker_amount_to_match , taker_amount_to_match , maker_bet . backer_multiplier , false ) < < 1 ;
2017-03-23 20:22:25 +00:00
assert ( result ! = 0 ) ;
return result ;
}
2017-08-09 15:12:59 +00:00
// called from the bet_place_evaluator
2017-03-22 19:04:11 +00:00
bool database : : place_bet ( const bet_object & new_bet_object )
{
2017-03-23 20:22:25 +00:00
const auto & bet_odds_idx = get_index_type < bet_object_index > ( ) . indices ( ) . get < by_odds > ( ) ;
bet_type bet_type_to_match = new_bet_object . back_or_lay = = bet_type : : back ? bet_type : : lay : bet_type : : back ;
auto book_itr = bet_odds_idx . lower_bound ( std : : make_tuple ( new_bet_object . betting_market_id , bet_type_to_match ) ) ;
auto book_end = bet_odds_idx . upper_bound ( std : : make_tuple ( new_bet_object . betting_market_id , bet_type_to_match , new_bet_object . backer_multiplier ) ) ;
2017-08-09 15:12:59 +00:00
// ilog("");
// ilog("------------ order book ------------------");
// for (auto itr = book_itr; itr != book_end; ++itr)
// idump((*itr));
// ilog("------------ order book ------------------");
2017-03-23 20:22:25 +00:00
int orders_matched_flags = 0 ;
bool finished = false ;
while ( ! finished & & book_itr ! = book_end )
{
auto old_book_itr = book_itr ;
+ + book_itr ;
orders_matched_flags = match_bet ( * this , new_bet_object , * old_book_itr ) ;
2017-08-09 15:12:59 +00:00
// we continue if the maker bet was completely consumed AND the taker bet was not
2017-03-23 20:22:25 +00:00
finished = orders_matched_flags ! = 2 ;
}
2017-08-09 15:12:59 +00:00
if ( ! ( orders_matched_flags & 1 ) )
{
// if the new (taker) bet was not completely consumed, we need to put whatever remains
// of it on the books. But we only allow bets that can be exactly matched
// on the books, so round the amount down if necessary
share_type minimum_matchable_amount = new_bet_object . get_minimum_matchable_amount ( ) ;
share_type scale_factor = new_bet_object . amount_to_bet . amount / minimum_matchable_amount ;
share_type rounded_bet_amount = scale_factor * minimum_matchable_amount ;
//idump((new_bet_object.amount_to_bet.amount)(rounded_bet_amount)(minimum_matchable_amount)(scale_factor));
if ( rounded_bet_amount = = share_type ( ) )
{
// the remainder of the bet was too small to match, cancel the bet
cancel_bet ( new_bet_object , true ) ;
return true ;
}
else if ( rounded_bet_amount ! = new_bet_object . amount_to_bet . amount )
{
asset stake_returned = new_bet_object . amount_to_bet ;
stake_returned . amount - = rounded_bet_amount ;
modify ( new_bet_object , [ & rounded_bet_amount ] ( bet_object & modified_bet_object ) {
modified_bet_object . amount_to_bet . amount = rounded_bet_amount ;
} ) ;
adjust_balance ( new_bet_object . bettor_id , stake_returned ) ;
// TODO: update global statistics
bet_adjusted_operation bet_adjusted_op ( new_bet_object . bettor_id , new_bet_object . id ,
stake_returned ) ;
// idump((bet_adjusted_op)(new_bet_object));
push_applied_operation ( std : : move ( bet_adjusted_op ) ) ;
return false ;
}
else
return false ;
}
else
return true ;
2017-03-22 19:04:11 +00:00
}
2017-03-22 16:03:08 +00:00
} }