Merge branch 'betting-merge' of https://bitbucket.org/peerplaysblockchain/peerplays-graphene into betting-merge

This commit is contained in:
Roman Olearski 2017-08-09 17:59:25 +02:00
commit 2c8b9ce17a
15 changed files with 511 additions and 243 deletions

View file

@ -236,6 +236,10 @@ struct get_impacted_account_visitor
{
_impacted.insert( op.bettor_id );
}
void operator()( const bet_adjusted_operation& op )
{
_impacted.insert( op.bettor_id );
}
void operator()( const bet_matched_operation& op )
{
_impacted.insert( op.bettor_id );

View file

@ -291,26 +291,14 @@ void_result bet_place_evaluator::do_evaluate(const bet_place_operation& op)
simulated_bet.betting_market_id = op.betting_market_id;
simulated_bet.amount_to_bet = op.amount_to_bet;
simulated_bet.backer_multiplier = op.backer_multiplier;
simulated_bet.amount_reserved_for_fees = op.amount_reserved_for_fees;
simulated_bet.back_or_lay = op.back_or_lay;
share_type required_deposit = get_required_deposit_for_bet(simulated_bet);
#endif
// verify they reserved enough to cover the percentage fee
uint16_t percentage_fee = current_params.current_fees->get<bet_place_operation>().percentage_fee;
fc::uint128_t minimum_percentage_fee_calculation = op.amount_to_bet.amount.value;
minimum_percentage_fee_calculation *= percentage_fee;
minimum_percentage_fee_calculation += GRAPHENE_100_PERCENT - 1; // round up
minimum_percentage_fee_calculation /= GRAPHENE_100_PERCENT;
share_type minimum_percentage_fee = minimum_percentage_fee_calculation.to_uint64();
FC_ASSERT(op.amount_reserved_for_fees >= minimum_percentage_fee, "insufficient fees",
("fee_provided", op.amount_reserved_for_fees)("fee_required", minimum_percentage_fee));
// do they have enough in their account to place the bet
_stake_plus_fees = op.amount_to_bet.amount + op.amount_reserved_for_fees;
FC_ASSERT( d.get_balance( *fee_paying_account, *_asset ).amount >= _stake_plus_fees, "insufficient balance",
("balance", d.get_balance(*fee_paying_account, *_asset))("stake_plus_fees", _stake_plus_fees) );
FC_ASSERT( d.get_balance( *fee_paying_account, *_asset ).amount >= op.amount_to_bet.amount, "insufficient balance",
("balance", d.get_balance(*fee_paying_account, *_asset))("amount_to_bet", op.amount_to_bet.amount) );
return void_result();
} FC_CAPTURE_AND_RETHROW( (op) ) }
@ -324,15 +312,16 @@ object_id_type bet_place_evaluator::do_apply(const bet_place_operation& op)
bet_obj.betting_market_id = op.betting_market_id;
bet_obj.amount_to_bet = op.amount_to_bet;
bet_obj.backer_multiplier = op.backer_multiplier;
bet_obj.amount_reserved_for_fees = op.amount_reserved_for_fees;
bet_obj.back_or_lay = op.back_or_lay;
});
d.adjust_balance(fee_paying_account->id, asset(-_stake_plus_fees, _betting_market_group->asset_id));
bet_id_type new_bet_id = new_bet.id; // save the bet id here, new_bet may be deleted during place_bet()
d.adjust_balance(fee_paying_account->id, -op.amount_to_bet);
bool bet_matched = d.place_bet(new_bet);
return new_bet.id;
return new_bet_id;
} FC_CAPTURE_AND_RETHROW( (op) ) }
void_result bet_cancel_evaluator::do_evaluate(const bet_cancel_operation& op)

View file

@ -11,15 +11,12 @@ namespace graphene { namespace chain {
void database::cancel_bet( const bet_object& bet, bool create_virtual_op )
{
asset amount_to_refund = bet.amount_to_bet;
amount_to_refund += bet.amount_reserved_for_fees;
//TODO: update global statistics
adjust_balance(bet.bettor_id, amount_to_refund); //return unmatched stake + fees
//TODO: do special fee accounting as required
adjust_balance(bet.bettor_id, amount_to_refund);
if (create_virtual_op)
{
bet_canceled_operation bet_canceled_virtual_op(bet.bettor_id, bet.id,
bet.amount_to_bet,
bet.amount_reserved_for_fees);
bet.amount_to_bet);
//idump((bet_canceled_virtual_op));
push_applied_operation(std::move(bet_canceled_virtual_op));
}
@ -127,7 +124,6 @@ void database::resolve_betting_market_group(const betting_market_group_object& b
uint16_t rake_fee_percentage = get_global_properties().parameters.betting_rake_fee_percentage;
share_type net_profits;
share_type payout_amounts;
share_type fees_collected;
account_id_type bettor_id = bettor_positions_pair.first;
const std::vector<const betting_market_position_object*>& bettor_positions = bettor_positions_pair.second;
@ -149,7 +145,6 @@ void database::resolve_betting_market_group(const betting_market_group_object& b
{
share_type total_payout = position->pay_if_payout_condition + position->pay_if_not_canceled;
payout_amounts += total_payout;
fees_collected += position->fees_collected;
net_profits += total_payout - position->pay_if_canceled;
break;
}
@ -157,12 +152,11 @@ void database::resolve_betting_market_group(const betting_market_group_object& b
{
share_type total_payout = position->pay_if_not_payout_condition + position->pay_if_not_canceled;
payout_amounts += total_payout;
fees_collected += position->fees_collected;
net_profits += total_payout - position->pay_if_canceled;
break;
}
case betting_market_resolution_type::cancel:
payout_amounts += position->pay_if_canceled + position->fees_collected;
payout_amounts += position->pay_if_canceled;
break;
default:
continue;
@ -179,9 +173,6 @@ void database::resolve_betting_market_group(const betting_market_group_object& b
adjust_balance(*rake_account_id, asset(rake_amount, betting_market_group.asset_id));
}
if (fees_collected.value)
adjust_balance(*rake_account_id, asset(fees_collected, betting_market_group.asset_id));
// pay winning - rake
adjust_balance(bettor_id, asset(payout_amounts - rake_amount, betting_market_group.asset_id));
// [ROL]
@ -191,7 +182,7 @@ void database::resolve_betting_market_group(const betting_market_group_object& b
betting_market_group.id,
resolutions,
payout_amounts,
fees_collected));
rake_amount));
}
betting_market_itr = betting_market_index.lower_bound(betting_market_group.id);
@ -213,27 +204,7 @@ void database::get_required_deposit_for_bet(const betting_market_object& betting
}
#endif
bool maybe_cull_small_bet( database& db, const bet_object& bet_object_to_cull )
{
/**
* There are times when this bet can't be even partially matched at its stated odds
* For example, say it's a back bet for 20 satoshis at 1.92 odds (which comes out to 25:23
* odds). It's not possible for it to match at exact odds since it's < 25
*
* Remove these bets from the books to reduce clutter.
*/
share_type minimum_matchable_amount = bet_object_to_cull.get_minimum_matchable_amount();
if (bet_object_to_cull.amount_to_bet.amount < minimum_matchable_amount)
{
dlog("culling small bet of ${amount}, smaller than the minimum ${minimum_matchable_amount} at odds ${odds}",
("amount", bet_object_to_cull.amount_to_bet.amount)("minimum_matchable_amount", minimum_matchable_amount)("odds", bet_object_to_cull.backer_multiplier));
db.cancel_bet(bet_object_to_cull);
return true;
}
return false;
}
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, share_type fees_collected)
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)
{ try {
assert(bet_amount >= 0);
@ -253,7 +224,6 @@ share_type adjust_betting_position(database& db, account_id_type bettor_id, bett
position.pay_if_not_payout_condition = back_or_lay == bet_type::lay ? bet_amount + matched_amount : 0;
position.pay_if_canceled = bet_amount;
position.pay_if_not_canceled = 0;
position.fees_collected = fees_collected;
// this should not be reducible
});
} else {
@ -263,7 +233,6 @@ share_type adjust_betting_position(database& db, account_id_type bettor_id, bett
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;
position.pay_if_canceled += bet_amount;
position.fees_collected += fees_collected;
guaranteed_winnings_returned = position.reduce();
});
@ -275,29 +244,21 @@ share_type adjust_betting_position(database& db, account_id_type bettor_id, bett
// 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,
bet_multiplier_type actual_multiplier, bool cull_if_small)
bet_multiplier_type actual_multiplier)
{
// calculate the percentage fee paid
fc::uint128_t percentage_fee_128 = bet.amount_reserved_for_fees.value;
percentage_fee_128 *= amount_bet.value;
percentage_fee_128 += bet.amount_to_bet.amount.value - 1;
percentage_fee_128 /= bet.amount_to_bet.amount.value;
share_type fee_paid = percentage_fee_128.to_uint64();
// 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,
bet.back_or_lay, amount_bet, amount_matched, fee_paid);
bet.back_or_lay, amount_bet, amount_matched);
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);
bet_matched_operation bet_matched_virtual_op(bet.bettor_id, bet.id, bet.betting_market_id,
bet_matched_operation bet_matched_virtual_op(bet.bettor_id, bet.id,
asset_amount_bet,
fee_paid,
actual_multiplier,
guaranteed_winnings_returned);
//idump((bet_matched_virtual_op));
//edump((bet_matched_virtual_op));
db.push_applied_operation(std::move(bet_matched_virtual_op));
// update the bet on the books
@ -310,12 +271,7 @@ bool bet_was_matched(database& db, const bet_object& bet,
{
db.modify(bet, [&](bet_object& bet_obj) {
bet_obj.amount_to_bet -= asset_amount_bet;
bet_obj.amount_reserved_for_fees -= fee_paid;
});
//TODO: cull_if_small is currently always true, remove the parameter if we don't find a
// need for it soon
if (cull_if_small)
return maybe_cull_small_bet(db, bet);
return false;
}
}
@ -334,7 +290,7 @@ int match_bet(database& db, const bet_object& taker_bet, const bet_object& maker
{
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);
assert(taker_bet.backer_multiplier >= maker_bet.backer_multiplier);
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);
assert(taker_bet.back_or_lay != maker_bet.back_or_lay);
int result = 0;
@ -381,37 +337,80 @@ int match_bet(database& db, const bet_object& taker_bet, const bet_object& maker
//idump((taker_amount_to_match)(maker_amount_to_match));
result |= bet_was_matched(db, taker_bet, taker_amount_to_match, maker_amount_to_match, maker_bet.backer_multiplier, true);
result |= bet_was_matched(db, maker_bet, maker_amount_to_match, taker_amount_to_match, maker_bet.backer_multiplier, true) << 1;
result |= bet_was_matched(db, taker_bet, taker_amount_to_match, maker_amount_to_match, maker_bet.backer_multiplier);
result |= bet_was_matched(db, maker_bet, maker_amount_to_match, taker_amount_to_match, maker_bet.backer_multiplier) << 1;
assert(result != 0);
return result;
}
// called from the bet_place_evaluator
bool database::place_bet(const bet_object& new_bet_object)
{
bet_id_type bet_id = new_bet_object.id;
const asset_object& bet_asset = get(new_bet_object.amount_to_bet.asset_id);
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));
// ilog("");
// ilog("------------ order book ------------------");
// for (auto itr = book_itr; itr != book_end; ++itr)
// idump((*itr));
// ilog("------------ order book ------------------");
int orders_matched_flags = 0;
bool finished = false;
while (!finished && book_itr != book_end)
{
auto old_book_itr = book_itr;
++book_itr;
// match returns 2 when only the old order was fully filled. In this case, we keep matching; otherwise, we stop.
orders_matched_flags = match_bet(*this, new_bet_object, *old_book_itr);
// we continue if the maker bet was completely consumed AND the taker bet was not
finished = orders_matched_flags != 2;
}
return (orders_matched_flags & 1) != 0;
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;
}
} }

View file

@ -210,6 +210,7 @@ struct get_impacted_account_visitor
void operator()(const bet_matched_operation &){}
void operator()(const bet_cancel_operation&){}
void operator()(const bet_canceled_operation &){}
void operator()(const bet_adjusted_operation &){}
void operator()( const tournament_create_operation& op )
{

View file

@ -94,8 +94,6 @@ class bet_object : public graphene::db::abstract_object< bet_object >
bet_multiplier_type backer_multiplier;
share_type amount_reserved_for_fees; // same asset type as amount_to_bet
bet_type back_or_lay;
static share_type get_approximate_matching_amount(share_type bet_amount, bet_multiplier_type backer_multiplier, bet_type back_or_lay, bool round_up = false);
@ -241,7 +239,10 @@ struct compare_bet_by_odds {
return true;
if (lhs_bet_type > rhs_bet_type)
return false;
return lhs_backer_multiplier < rhs_backer_multiplier;
if (lhs_bet_type == bet_type::back)
return lhs_backer_multiplier < rhs_backer_multiplier;
else
return lhs_backer_multiplier > rhs_backer_multiplier;
}
bool compare(const betting_market_id_type& lhs_betting_market_id, bet_type lhs_bet_type,
bet_multiplier_type lhs_backer_multiplier, const bet_id_type& lhs_bet_id,
@ -257,9 +258,9 @@ struct compare_bet_by_odds {
if (lhs_bet_type > rhs_bet_type)
return false;
if (lhs_backer_multiplier < rhs_backer_multiplier)
return true;
return lhs_bet_type == bet_type::back;
if (lhs_backer_multiplier > rhs_backer_multiplier)
return false;
return lhs_bet_type == bet_type::lay;
return lhs_bet_id < rhs_bet_id;
}
};
@ -381,7 +382,10 @@ struct compare_bet_by_bettor_then_odds {
return true;
if (lhs_bet_type > rhs_bet_type)
return false;
return lhs_backer_multiplier < rhs_backer_multiplier;
if (lhs_bet_type == bet_type::back)
return lhs_backer_multiplier < rhs_backer_multiplier;
else
return lhs_backer_multiplier > rhs_backer_multiplier;
}
bool compare(const betting_market_id_type& lhs_betting_market_id, const account_id_type& lhs_bettor_id,
bet_type lhs_bet_type,
@ -403,9 +407,10 @@ struct compare_bet_by_bettor_then_odds {
if (lhs_bet_type > rhs_bet_type)
return false;
if (lhs_backer_multiplier < rhs_backer_multiplier)
return true;
return lhs_bet_type == bet_type::back;
if (lhs_backer_multiplier > rhs_backer_multiplier)
return false;
return lhs_bet_type == bet_type::lay;
return lhs_bet_id < rhs_bet_id;
}
};
@ -444,6 +449,6 @@ typedef generic_index<betting_market_position_object, betting_market_position_mu
FC_REFLECT_DERIVED( graphene::chain::betting_market_rules_object, (graphene::db::object), (name)(description) )
FC_REFLECT_DERIVED( graphene::chain::betting_market_group_object, (graphene::db::object), (description)(event_id)(rules_id)(asset_id)(frozen)(total_matched_bets_amount) )
FC_REFLECT_DERIVED( graphene::chain::betting_market_object, (graphene::db::object), (group_id)(description)(payout_condition) )
FC_REFLECT_DERIVED( graphene::chain::bet_object, (graphene::db::object), (bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(amount_reserved_for_fees)(back_or_lay) )
FC_REFLECT_DERIVED( graphene::chain::bet_object, (graphene::db::object), (bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(back_or_lay) )
FC_REFLECT_DERIVED( graphene::chain::betting_market_position_object, (graphene::db::object), (bettor_id)(betting_market_id)(pay_if_payout_condition)(pay_if_not_payout_condition)(pay_if_canceled)(pay_if_not_canceled)(fees_collected) )

View file

@ -239,7 +239,6 @@ struct bet_place_operation : public base_operation
struct fee_parameters_type
{
uint64_t fee = GRAPHENE_BLOCKCHAIN_PRECISION; // fixed fee charged upon placing the bet
uint16_t percentage_fee = 2 * GRAPHENE_1_PERCENT; // charged when bet is matched
};
asset fee;
@ -257,11 +256,6 @@ struct bet_place_operation : public base_operation
// would be 2 * GRAPHENE_BETTING_ODDS_PRECISION.
bet_multiplier_type backer_multiplier;
// the amount the blockchain reserves to pay the percentage fee on matched bets.
// when this bet is (partially) matched, the blockchain will take (part of) the fee. If this bet is canceled
// the remaining amount will be returned to the bettor (same asset type as the amount_to_bet)
share_type amount_reserved_for_fees;
bet_type back_or_lay;
extensions_type extensions;
@ -278,24 +272,20 @@ struct bet_matched_operation : public base_operation
struct fee_parameters_type {};
bet_matched_operation(){}
bet_matched_operation(account_id_type bettor_id, bet_id_type bet_id, betting_market_id_type betting_market_id,
asset amount_bet, share_type fees_paid,
bet_matched_operation(account_id_type bettor_id, bet_id_type bet_id,
asset amount_bet,
bet_multiplier_type backer_multiplier,
share_type guaranteed_winnings_returned) :
bettor_id(bettor_id),
bet_id(bet_id),
betting_market_id(betting_market_id),
amount_bet(amount_bet),
fees_paid(fees_paid),
backer_multiplier(backer_multiplier),
guaranteed_winnings_returned(guaranteed_winnings_returned)
{}
account_id_type bettor_id;
bet_id_type bet_id;
betting_market_id_type betting_market_id;
asset amount_bet;
share_type fees_paid; // same asset type as amount_bet
bet_multiplier_type backer_multiplier; // the actual odds received
share_type guaranteed_winnings_returned; // same asset type as amount_bet
asset fee; // unimportant for a virtual op
@ -332,17 +322,15 @@ struct bet_canceled_operation : public base_operation
bet_canceled_operation(){}
bet_canceled_operation(account_id_type bettor_id, bet_id_type bet_id,
asset stake_returned, share_type unused_fees_returned) :
asset stake_returned) :
bettor_id(bettor_id),
bet_id(bet_id),
stake_returned(stake_returned),
unused_fees_returned(unused_fees_returned)
stake_returned(stake_returned)
{}
account_id_type bettor_id;
bet_id_type bet_id;
asset stake_returned;
share_type unused_fees_returned; // same asset type as stake_returned
asset fee; // unimportant for a virtual op
account_id_type fee_payer()const { return bettor_id; }
@ -352,6 +340,35 @@ struct bet_canceled_operation : public base_operation
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
};
/**
* virtual op generated when a bet amount is rounded down to an amount that can
* match evenly at a given odds (the blockchain does this automatically at the time
* the bet is placed on the order books). (note: there is no way a user can adjust their bet
* after placing it, aside from canceling the bet and placing a new one)
*/
struct bet_adjusted_operation : public base_operation
{
struct fee_parameters_type {};
bet_adjusted_operation(){}
bet_adjusted_operation(account_id_type bettor_id, bet_id_type bet_id,
asset stake_returned) :
bettor_id(bettor_id),
bet_id(bet_id),
stake_returned(stake_returned)
{}
account_id_type bettor_id;
bet_id_type bet_id;
asset stake_returned;
asset fee; // unimportant for a virtual op
account_id_type fee_payer()const { return bettor_id; }
void validate()const { FC_ASSERT(false, "virtual operation"); }
/// This is a virtual operation; there is no fee
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
};
} }
@ -396,13 +413,16 @@ FC_REFLECT( graphene::chain::betting_market_group_cancel_unmatched_bets_operatio
FC_REFLECT_ENUM( graphene::chain::bet_type, (back)(lay) )
FC_REFLECT( graphene::chain::bet_place_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::bet_place_operation,
(fee)(bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(amount_reserved_for_fees)(back_or_lay)(extensions) )
(fee)(bettor_id)(betting_market_id)(amount_to_bet)(backer_multiplier)(back_or_lay)(extensions) )
FC_REFLECT( graphene::chain::bet_matched_operation::fee_parameters_type, )
FC_REFLECT( graphene::chain::bet_matched_operation, (bettor_id)(bet_id)(betting_market_id)(amount_bet)(fees_paid)(backer_multiplier)(guaranteed_winnings_returned) )
FC_REFLECT( graphene::chain::bet_matched_operation, (bettor_id)(bet_id)(amount_bet)(backer_multiplier)(guaranteed_winnings_returned) )
FC_REFLECT( graphene::chain::bet_cancel_operation::fee_parameters_type, (fee) )
FC_REFLECT( graphene::chain::bet_cancel_operation, (bettor_id) (bet_to_cancel) (extensions) )
FC_REFLECT( graphene::chain::bet_canceled_operation::fee_parameters_type, )
FC_REFLECT( graphene::chain::bet_canceled_operation, (bettor_id)(bet_id)(stake_returned)(unused_fees_returned) )
FC_REFLECT( graphene::chain::bet_canceled_operation, (bettor_id)(bet_id)(stake_returned) )
FC_REFLECT( graphene::chain::bet_adjusted_operation::fee_parameters_type, )
FC_REFLECT( graphene::chain::bet_adjusted_operation, (bettor_id)(bet_id)(stake_returned) )

View file

@ -122,7 +122,8 @@ namespace graphene { namespace chain {
tournament_payout_operation, // VIRTUAL
tournament_leave_operation,
betting_market_group_update_operation,
betting_market_update_operation
betting_market_update_operation,
bet_adjusted_operation // VIRTUAL
> operation;
/// @} // operations group

View file

@ -80,13 +80,15 @@ binned_order_book bookie_api_impl::get_binned_order_book(graphene::chain::bettin
}
};
// iterate through both sides of the order book (backs at increasing odds then lays at decreasing odds)
for (auto bet_odds_iter = bet_odds_idx.lower_bound(std::make_tuple(betting_market_id));
bet_odds_iter != bet_odds_idx.end() && betting_market_id == bet_odds_iter->betting_market_id;
++bet_odds_iter)
{
if (current_bin &&
(bet_odds_iter->back_or_lay == current_bin->back_or_lay ||
bet_odds_iter->backer_multiplier > current_bin->backer_multiplier))
(bet_odds_iter->back_or_lay == current_bin->back_or_lay /* we have switched from back to lay bets */ ||
(bet_odds_iter->back_or_lay == bet_type::back ? bet_odds_iter->backer_multiplier > current_bin->backer_multiplier :
bet_odds_iter->backer_multiplier < current_bin->backer_multiplier)))
flush_current_bin();
if (!current_bin)
@ -94,9 +96,21 @@ binned_order_book bookie_api_impl::get_binned_order_book(graphene::chain::bettin
// if there is no current bin, create one appropriate for the bet we're processing
current_bin = graphene::chain::bet_object();
current_bin->backer_multiplier = (bet_odds_iter->backer_multiplier + bin_size - 1) / bin_size * bin_size;
current_bin->backer_multiplier = std::min<graphene::chain::bet_multiplier_type>(current_bin->backer_multiplier, current_params.max_bet_multiplier);
current_bin->back_or_lay = bet_odds_iter->back_or_lay == bet_type::back ? bet_type::lay : bet_type::back;
// for back bets, we want to group all bets with odds from 3.0001 to 4 into the "4" bin
// for lay bets, we want to group all bets with odds from 3 to 3.9999 into the "3" bin
if (bet_odds_iter->back_or_lay == bet_type::back)
{
current_bin->backer_multiplier = (bet_odds_iter->backer_multiplier + bin_size - 1) / bin_size * bin_size;
current_bin->backer_multiplier = std::min<graphene::chain::bet_multiplier_type>(current_bin->backer_multiplier, current_params.max_bet_multiplier);
current_bin->back_or_lay = bet_type::lay;
}
else
{
current_bin->backer_multiplier = bet_odds_iter->backer_multiplier / bin_size * bin_size;
current_bin->backer_multiplier = std::max<graphene::chain::bet_multiplier_type>(current_bin->backer_multiplier, current_params.min_bet_multiplier);
current_bin->back_or_lay = bet_type::back;
}
current_bin->amount_to_bet.amount = 0;
}

View file

@ -55,7 +55,7 @@ namespace graphene { namespace bookie {
namespace detail
{
class persistent_event_object : public graphene::db::abstract_object<event_object>
class persistent_event_object : public graphene::db::abstract_object<persistent_event_object>
{
public:
static const uint8_t space_id = bookie_objects;
@ -124,6 +124,117 @@ void events_by_competitor_index::object_modified( const object& after )
}
#endif
//////////// betting_market_group_object //////////////////
class persistent_betting_market_group_object : public graphene::db::abstract_object<persistent_betting_market_group_object>
{
public:
static const uint8_t space_id = bookie_objects;
static const uint8_t type_id = persistent_betting_market_group_object_type;
betting_market_group_object ephemeral_betting_market_group_object;
share_type total_matched_bets_amount;
betting_market_group_id_type get_betting_market_group_id() const { return ephemeral_betting_market_group_object.id; }
};
struct by_betting_market_group_id;
typedef multi_index_container<
persistent_betting_market_group_object,
indexed_by<
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
ordered_unique<tag<by_betting_market_group_id>, const_mem_fun<persistent_betting_market_group_object, betting_market_group_id_type, &persistent_betting_market_group_object::get_betting_market_group_id> > > > persistent_betting_market_group_multi_index_type;
typedef generic_index<persistent_betting_market_group_object, persistent_betting_market_group_multi_index_type> persistent_betting_market_group_index;
//////////// betting_market_object //////////////////
class persistent_betting_market_object : public graphene::db::abstract_object<persistent_betting_market_object>
{
public:
static const uint8_t space_id = bookie_objects;
static const uint8_t type_id = persistent_betting_market_object_type;
betting_market_object ephemeral_betting_market_object;
share_type total_matched_bets_amount;
betting_market_id_type get_betting_market_id() const { return ephemeral_betting_market_object.id; }
};
struct by_betting_market_id;
typedef multi_index_container<
persistent_betting_market_object,
indexed_by<
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
ordered_unique<tag<by_betting_market_id>, const_mem_fun<persistent_betting_market_object, betting_market_id_type, &persistent_betting_market_object::get_betting_market_id> > > > persistent_betting_market_multi_index_type;
typedef generic_index<persistent_betting_market_object, persistent_betting_market_multi_index_type> persistent_betting_market_index;
//////////// bet_object //////////////////
class persistent_bet_object : public graphene::db::abstract_object<persistent_bet_object>
{
public:
static const uint8_t space_id = bookie_objects;
static const uint8_t type_id = persistent_bet_object_type;
bet_object ephemeral_bet_object;
bet_id_type get_bet_id() const { return ephemeral_bet_object.id; }
};
struct by_bet_id;
typedef multi_index_container<
persistent_bet_object,
indexed_by<
ordered_unique<tag<by_id>, member<object, object_id_type, &object::id> >,
ordered_unique<tag<by_bet_id>, const_mem_fun<persistent_bet_object, bet_id_type, &persistent_bet_object::get_bet_id> > > > persistent_bet_multi_index_type;
typedef generic_index<persistent_bet_object, persistent_bet_multi_index_type> persistent_bet_index;
/* As a plugin, we get notified of new/changed objects at the end of every block processed.
* For most objects, that's fine, because we expect them to always be around until the end of
* the block. However, with bet objects, it's possible that the user places a bet and it fills
* and is removed during the same block, so need another strategy to detect them immediately after
* they are created.
* We do this by creating a secondary index on bet_object. We don't actually use it
* to index any property of the bet, we just use it to register for callbacks.
*/
class persistent_bet_object_helper : public secondary_index
{
public:
virtual ~persistent_bet_object_helper() {}
virtual void object_inserted(const object& obj) override;
//virtual void object_removed( const object& obj ) override;
//virtual void about_to_modify( const object& before ) override;
virtual void object_modified(const object& after) override;
void set_plugin_instance(bookie_plugin* instance) { _bookie_plugin = instance; }
private:
bookie_plugin* _bookie_plugin;
};
void persistent_bet_object_helper::object_inserted(const object& obj)
{
const bet_object& bet_obj = *boost::polymorphic_downcast<const bet_object*>(&obj);
_bookie_plugin->database().create<persistent_bet_object>([&](persistent_bet_object& saved_bet_obj) {
saved_bet_obj.ephemeral_bet_object = bet_obj;
});
}
void persistent_bet_object_helper::object_modified(const object& after)
{
database& db = _bookie_plugin->database();
auto& persistent_bets_by_bet_id = db.get_index_type<persistent_bet_index>().indices().get<by_bet_id>();
const bet_object& bet_obj = *boost::polymorphic_downcast<const bet_object*>(&after);
auto iter = persistent_bets_by_bet_id.find(bet_obj.id);
assert (iter != persistent_bets_by_bet_id.end());
if (iter != persistent_bets_by_bet_id.end())
db.modify(*iter, [&](persistent_bet_object& saved_bet_obj) {
saved_bet_obj.ephemeral_bet_object = bet_obj;
});
}
//////////// end bet_object ///////////////////
class bookie_plugin_impl
{
public:
@ -175,13 +286,14 @@ class bookie_plugin_impl
bookie_plugin_impl::~bookie_plugin_impl()
{
return;
}
void bookie_plugin_impl::on_objects_changed(const vector<object_id_type>& changed_object_ids)
{
graphene::chain::database& db = database();
auto& event_id_index = db.get_index_type<persistent_event_object_index>().indices().get<by_event_id>();
auto& betting_market_group_id_index = db.get_index_type<persistent_betting_market_group_index>().indices().get<by_betting_market_group_id>();
auto& betting_market_id_index = db.get_index_type<persistent_betting_market_index>().indices().get<by_betting_market_id>();
for (const object_id_type& changed_object_id : changed_object_ids)
{
@ -232,6 +344,78 @@ void bookie_plugin_impl::on_objects_changed(const vector<object_id_type>& change
});
}
}
else if (changed_object_id.space() == betting_market_group_object::space_id &&
changed_object_id.type() == betting_market_group_object::type_id)
{
betting_market_group_id_type changed_betting_market_group_id = changed_object_id;
const betting_market_group_object* new_betting_market_group_obj = nullptr;
try
{
new_betting_market_group_obj = &changed_betting_market_group_id(db);
}
catch (fc::exception& e)
{
}
// new_betting_market_group_obj should point to the now-changed event_object, or null if it was removed from the database
const persistent_betting_market_group_object* old_betting_market_group_obj = nullptr;
auto persistent_betting_market_group_iter = betting_market_group_id_index.find(changed_betting_market_group_id);
if (persistent_betting_market_group_iter != betting_market_group_id_index.end())
old_betting_market_group_obj = &*persistent_betting_market_group_iter;
// and old_betting_market_group_obj is a pointer to our saved copy, or nullptr if it is a new object
if (old_betting_market_group_obj && new_betting_market_group_obj)
{
ilog("Modifying persistent betting_market_group object ${id}", ("id", changed_betting_market_group_id));
db.modify(*old_betting_market_group_obj, [&](persistent_betting_market_group_object& saved_betting_market_group_obj) {
saved_betting_market_group_obj.ephemeral_betting_market_group_object = *new_betting_market_group_obj;
});
}
else if (new_betting_market_group_obj)
{
ilog("Creating new persistent betting_market_group object ${id}", ("id", changed_betting_market_group_id));
db.create<persistent_betting_market_group_object>([&](persistent_betting_market_group_object& saved_betting_market_group_obj) {
saved_betting_market_group_obj.ephemeral_betting_market_group_object = *new_betting_market_group_obj;
});
}
}
else if (changed_object_id.space() == betting_market_object::space_id &&
changed_object_id.type() == betting_market_object::type_id)
{
betting_market_id_type changed_betting_market_id = changed_object_id;
const betting_market_object* new_betting_market_obj = nullptr;
try
{
new_betting_market_obj = &changed_betting_market_id(db);
}
catch (fc::exception& e)
{
}
// new_betting_market_obj should point to the now-changed event_object, or null if it was removed from the database
const persistent_betting_market_object* old_betting_market_obj = nullptr;
auto persistent_betting_market_iter = betting_market_id_index.find(changed_betting_market_id);
if (persistent_betting_market_iter != betting_market_id_index.end())
old_betting_market_obj = &*persistent_betting_market_iter;
// and old_betting_market_obj is a pointer to our saved copy, or nullptr if it is a new object
if (old_betting_market_obj && new_betting_market_obj)
{
ilog("Modifying persistent betting_market object ${id}", ("id", changed_betting_market_id));
db.modify(*old_betting_market_obj, [&](persistent_betting_market_object& saved_betting_market_obj) {
saved_betting_market_obj.ephemeral_betting_market_object = *new_betting_market_obj;
});
}
else if (new_betting_market_obj)
{
ilog("Creating new persistent betting_market object ${id}", ("id", changed_betting_market_id));
db.create<persistent_betting_market_object>([&](persistent_betting_market_object& saved_betting_market_obj) {
saved_betting_market_obj.ephemeral_betting_market_object = *new_betting_market_obj;
});
}
}
}
}
@ -239,29 +423,36 @@ void bookie_plugin_impl::on_block_applied( const signed_block& )
{
graphene::chain::database& db = database();
const vector<optional<operation_history_object> >& hist = db.get_applied_operations();
for( const optional< operation_history_object >& o_op : hist )
for( const optional<operation_history_object>& o_op : hist )
{
if( !o_op.valid() )
{
continue;
}
const operation_history_object& op = *o_op;
if( op.op.which() == operation::tag< bet_matched_operation >::value )
if( op.op.which() == operation::tag<bet_matched_operation>::value )
{
const bet_matched_operation& bet_matched_op = op.op.get<bet_matched_operation>();
idump((bet_matched_op));
const asset& amount_bet = bet_matched_op.amount_bet;
// object may no longer exist
//const bet_object& bet = bet_matched_op.bet_id(db);
const betting_market_object& betting_market = bet_matched_op.betting_market_id(db);
const betting_market_group_object& betting_market_group = betting_market.group_id(db);
db.modify( betting_market_group, [&]( betting_market_group_object& obj ){
obj.total_matched_bets_amount += amount_bet.amount;
});
auto& persistent_bets_by_bet_id = db.get_index_type<persistent_bet_index>().indices().get<by_bet_id>();
auto bet_iter = persistent_bets_by_bet_id.find(bet_matched_op.bet_id);
assert(bet_iter != persistent_bets_by_bet_id.end());
if (bet_iter != persistent_bets_by_bet_id.end())
{
const bet_object& bet_obj = bet_iter->ephemeral_bet_object;
const betting_market_object& betting_market = bet_obj.betting_market_id(db); // TODO: this needs to look at the persistent version
const betting_market_group_object& betting_market_group = betting_market.group_id(db); // TODO: as does this
db.modify( betting_market_group, [&]( betting_market_group_object& obj ){
obj.total_matched_bets_amount += amount_bet.amount;
});
}
}
else if( op.op.which() == operation::tag< event_create_operation >::value )
else if( op.op.which() == operation::tag<event_create_operation>::value )
{
FC_ASSERT(op.result.which() == operation_result::tag< object_id_type >::value);
FC_ASSERT(op.result.which() == operation_result::tag<object_id_type>::value);
//object_id_type object_id = op.result.get<object_id_type>();
event_id_type object_id = op.result.get<object_id_type>();
FC_ASSERT( db.find_object(object_id), "invalid event specified" );
@ -271,7 +462,7 @@ void bookie_plugin_impl::on_block_applied( const signed_block& )
localized_event_strings[pair.first].insert(event_string(object_id, pair.second));
}
}
else if( op.op.which() == operation::tag< event_update_operation >::value )
else if( op.op.which() == operation::tag<event_update_operation>::value )
{
const event_update_operation& event_create_op = op.op.get<event_update_operation>();
if (!event_create_op.new_name.valid())
@ -345,10 +536,8 @@ std::string bookie_plugin::plugin_name()const
return "bookie";
}
void bookie_plugin::plugin_set_program_options(
boost::program_options::options_description& cli,
boost::program_options::options_description& cfg
)
void bookie_plugin::plugin_set_program_options(boost::program_options::options_description& cli,
boost::program_options::options_description& cfg)
{
//cli.add_options()
// ("track-account", boost::program_options::value<std::vector<std::string>>()->composing()->multitoken(), "Account ID to track history for (may specify multiple times)")
@ -363,8 +552,13 @@ void bookie_plugin::plugin_initialize(const boost::program_options::variables_ma
database().changed_objects.connect([&](const vector<object_id_type>& changed_object_ids, const fc::flat_set<graphene::chain::account_id_type>& impacted_accounts){ my->on_objects_changed(changed_object_ids); });
//auto event_index =
database().add_index<primary_index<detail::persistent_event_object_index> >();
//event_index->add_secondary_index<detail::events_by_competitor_index>();
//LOAD_VALUE_SET(options, "tracked-accounts", my->_tracked_accounts, graphene::chain::account_id_type);
database().add_index<primary_index<detail::persistent_betting_market_group_index> >();
database().add_index<primary_index<detail::persistent_betting_market_index> >();
database().add_index<primary_index<detail::persistent_bet_index> >();
const primary_index<bet_object_index>& bet_object_idx = database().get_index_type<primary_index<bet_object_index> >();
primary_index<bet_object_index>& nonconst_bet_object_idx = const_cast<primary_index<bet_object_index>&>(bet_object_idx);
detail::persistent_bet_object_helper* persistent_bet_object_helper_index = nonconst_bet_object_idx.add_secondary_index<detail::persistent_bet_object_helper>();
persistent_bet_object_helper_index->set_plugin_instance(this);
ilog("bookie plugin: plugin_startup() end");
}
@ -392,5 +586,8 @@ void bookie_plugin::get_events_containing_sub_string(std::vector<event_object>&
}
} }
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_event_object, (graphene::db::object), (event_object_id)(name)(season)(start_time)(event_group_id)(status)(scores) )
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_event_object, (graphene::db::object), (event_object_id)(name)(season)(start_time)(event_group_id)(status)(scores) )
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_betting_market_group_object, (graphene::db::object), (ephemeral_betting_market_group_object)(total_matched_bets_amount) )
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_betting_market_object, (graphene::db::object), (ephemeral_betting_market_object) )
FC_REFLECT_DERIVED( graphene::bookie::detail::persistent_bet_object, (graphene::db::object), (ephemeral_bet_object) )

View file

@ -47,13 +47,16 @@ enum spaces {
enum bookie_object_type
{
persistent_event_object_type,
persistent_betting_market_group_object_type,
persistent_betting_market_object_type,
persistent_bet_object_type,
BOOKIE_OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
};
namespace detail
{
class bookie_plugin_impl;
class bookie_plugin_impl;
}
class bookie_plugin : public graphene::app::plugin
@ -63,9 +66,8 @@ class bookie_plugin : public graphene::app::plugin
virtual ~bookie_plugin();
std::string plugin_name()const override;
virtual void plugin_set_program_options(
boost::program_options::options_description& cli,
boost::program_options::options_description& cfg) override;
virtual void plugin_set_program_options(boost::program_options::options_description& cli,
boost::program_options::options_description& cfg) override;
virtual void plugin_initialize(const boost::program_options::variables_map& options) override;
virtual void plugin_startup() override;

View file

@ -1686,7 +1686,6 @@ class wallet_api
bet_type back_or_lay,
asset amount_to_bet,
bet_multiplier_type backer_multiplier,
share_type amount_reserved_for_fees,
bool broadcast = false);
signed_transaction propose_resolve_betting_market_group(

View file

@ -5408,7 +5408,6 @@ signed_transaction wallet_api::place_bet(
bet_type back_or_lay,
asset amount_to_bet,
bet_multiplier_type backer_multiplier,
share_type amount_reserved_for_fees,
bool broadcast /*= false*/)
{
FC_ASSERT( !is_locked() );
@ -5419,7 +5418,6 @@ signed_transaction wallet_api::place_bet(
bet_place_op.betting_market_id = betting_market_id;
bet_place_op.amount_to_bet = amount_to_bet;
bet_place_op.backer_multiplier = backer_multiplier;
bet_place_op.amount_reserved_for_fees = amount_reserved_for_fees;
bet_place_op.back_or_lay = back_or_lay;
signed_transaction tx;

View file

@ -135,12 +135,12 @@ BOOST_AUTO_TEST_CASE(simple_bet_win)
transfer(account_id_type(), bob_id, asset(10000));
// place bets at 10:1
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION, 2);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION, 20);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION);
// reverse positions at 1:1
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 22);
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 22);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
} FC_LOG_AND_RETHROW()
}
@ -159,11 +159,11 @@ BOOST_AUTO_TEST_CASE(binned_order_books)
transfer(account_id_type(), bob_id, asset(10000));
// place back bets at decimal odds of 1.55, 1.6, 1.65, 1.66, and 1.67
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 155 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10, 2);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 165 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 166 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 167 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 155 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 165 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 166 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 167 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
const auto& bet_odds_idx = db.get_index_type<bet_object_index>().indices().get<by_odds>();
@ -187,7 +187,7 @@ BOOST_AUTO_TEST_CASE(binned_order_books)
// compute the matching lay order
share_type lay_amount = bet_object::get_approximate_matching_amount(binned_order.amount_to_bet, binned_order.backer_multiplier, bet_type::back, false /* round down */);
ilog("Alice is laying with ${lay_amount} at odds ${odds} to match the binned back amount ${back_amount}", ("lay_amount", lay_amount)("odds", binned_order.backer_multiplier)("back_amount", binned_order.amount_to_bet));
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(lay_amount, asset_id_type()), binned_order.backer_multiplier, 2);
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(lay_amount, asset_id_type()), binned_order.backer_multiplier);
}
bet_iter = bet_odds_idx.lower_bound(std::make_tuple(capitals_win_market.id));
@ -201,11 +201,11 @@ BOOST_AUTO_TEST_CASE(binned_order_books)
BOOST_CHECK(bet_odds_idx.lower_bound(std::make_tuple(capitals_win_market.id)) == bet_odds_idx.end());
// place lay bets at decimal odds of 1.55, 1.6, 1.65, 1.66, and 1.67
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 155 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10, 2);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 165 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 166 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 167 * GRAPHENE_BETTING_ODDS_PRECISION / 100, 2);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 155 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 16 * GRAPHENE_BETTING_ODDS_PRECISION / 10);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 165 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 166 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 167 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
binned_orders_point_one = bookie_api.get_binned_order_book(capitals_win_market.id, 1);
idump((binned_orders_point_one));
@ -219,7 +219,7 @@ BOOST_AUTO_TEST_CASE(binned_order_books)
// compute the matching lay order
share_type back_amount = bet_object::get_approximate_matching_amount(binned_order.amount_to_bet, binned_order.backer_multiplier, bet_type::lay, false /* round down */);
ilog("Alice is backing with ${back_amount} at odds ${odds} to match the binned lay amount ${lay_amount}", ("back_amount", back_amount)("odds", binned_order.backer_multiplier)("lay_amount", binned_order.amount_to_bet));
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(back_amount, asset_id_type()), binned_order.backer_multiplier, 2);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(back_amount, asset_id_type()), binned_order.backer_multiplier);
}
bet_iter = bet_odds_idx.lower_bound(std::make_tuple(capitals_win_market.id));
@ -247,12 +247,12 @@ BOOST_AUTO_TEST_CASE( peerplays_sport_create_test )
transfer(account_id_type(), bob_id, asset(10000000));
// have bob lay a bet for 1M (+20k fees) at 1:1 odds
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
// have alice back a matching bet at 1:1 odds (also costing 1.02M)
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
// caps win
resolve_betting_market_group(moneyline_betting_markets.id,
@ -263,8 +263,8 @@ BOOST_AUTO_TEST_CASE( peerplays_sport_create_test )
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
} FC_LOG_AND_RETHROW()
}
@ -281,25 +281,66 @@ BOOST_AUTO_TEST_CASE( cancel_unmatched_in_betting_group_test )
transfer(account_id_type(), bob_id, asset(10000000));
// have bob lay a bet for 1M (+20k fees) at 1:1 odds
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
// have alice back a matching bet at 1:1 odds (also costing 1.02M)
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
// place unmatched
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 10);
place_bet(bob_id, blackhawks_win_market.id, bet_type::lay, asset(600, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 20);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
place_bet(bob_id, blackhawks_win_market.id, bet_type::lay, asset(600, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 - 500 - 10);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000 - 600 - 20);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 500);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 600);
// cancel unmatched
cancel_unmatched_bets(moneyline_betting_markets.id);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
} FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE(inexact_odds)
{
try
{
ACTORS( (alice)(bob) );
CREATE_ICE_HOCKEY_BETTING_MARKET();
transfer(account_id_type(), alice_id, asset(10000000));
share_type alice_expected_balance = 10000000;
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
// lay 47 at 1.94 odds (50:47) -- this is an exact amount, nothing surprising should happen here
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(47, asset_id_type()), 194 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
alice_expected_balance -= 47;
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
// lay 100 at 1.91 odds (100:91) -- this is an inexact match, we should get refunded 9 and leave a bet for 91 on the books
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(100, asset_id_type()), 191 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
alice_expected_balance -= 91;
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), alice_expected_balance.value);
transfer(account_id_type(), bob_id, asset(10000000));
share_type bob_expected_balance = 10000000;
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
// now have bob match it with a back of 300 at 1.91
// This should:
// match the full 47 @ 1.94 with 50
// match the full 91 @ 1.91 with 100
// leaving 150
// back bets at 100:91 must be a multiple of 100, so refund 50
// leaves a back bet of 100 @ 1.91 on the books
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(300, asset_id_type()), 191 * GRAPHENE_BETTING_ODDS_PRECISION / 100);
bob_expected_balance -= 250;
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), bob_expected_balance.value);
}
FC_LOG_AND_RETHROW()
}
BOOST_AUTO_TEST_CASE( chained_market_create_test )
{
// Often you will want to create several objects that reference each other at the same time.
@ -430,12 +471,12 @@ struct simple_bet_test_fixture : database_fixture {
transfer(account_id_type(), bob_id, asset(10000));
// place bets at 10:1
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION, 2);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION, 20);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(100, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000, asset_id_type()), 11 * GRAPHENE_BETTING_ODDS_PRECISION);
// reverse positions at 1:1
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 22);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 22);
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(1100, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
capitals_win_betting_market_id = capitals_win_market.id;
blackhawks_win_betting_market_id = blackhawks_win_market.id;
@ -459,13 +500,13 @@ BOOST_AUTO_TEST_CASE( win )
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint32_t rake_value;
//rake_value = (-100 + 1100 - 1100) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
// alice starts with 10000, pays 100 (bet) + 2 (fee), wins 1100, then pays 1100 (bet) + 22 (fee), wins 0
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000 - 100 - 2 + 1100 - 1100 - 22 + 0);
// alice starts with 10000, pays 100 (bet), wins 1100, then pays 1100 (bet), wins 0
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000 - 100 + 1100 - 1100 + 0);
rake_value = (-1000 - 1100 + 2200) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
// bob starts with 10000, pays 1000 (bet) + 20 (fee), wins 0, then pays 1100 (bet) + 22 (fee), wins 2200
// bob starts with 10000, pays 1000 (bet), wins 0, then pays 1100 (bet), wins 2200
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000 - 1000 - 20 + 0 - 1100 - 22 + 2200 - rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000 - 1000 + 0 - 1100 + 2200 - rake_value);
} FC_LOG_AND_RETHROW()
}
@ -482,13 +523,13 @@ BOOST_AUTO_TEST_CASE( not_win )
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint32_t rake_value = (-100 - 1100 + 2200) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
// alice starts with 10000, pays 100 (bet) + 2 (fee), wins 0, then pays 1100 (bet) + 22 (fee), wins 2200
// alice starts with 10000, pays 100 (bet), wins 0, then pays 1100 (bet), wins 2200
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000 - 100 - 2 + 0 - 1100 - 22 + 2200 - rake_value);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000 - 100 + 0 - 1100 + 2200 - rake_value);
//rake_value = (-1000 + 1100 - 1100) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
// bob starts with 10000, pays 1000 (bet) + 20 (fee), wins 1100, then pays 1100 (bet) + 22 (fee), wins 0
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000 - 1000 - 20 + 1100 - 1100 - 22 + 0);
// bob starts with 10000, pays 1000 (bet), wins 1100, then pays 1100 (bet), wins 0
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000 - 1000 + 1100 - 1100 + 0);
} FC_LOG_AND_RETHROW()
}
@ -523,21 +564,21 @@ struct simple_bet_test_fixture_2 : database_fixture {
transfer(account_id_type(), bob_id, asset(10000));
// alice backs 1000 at 1:1, matches
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 20);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 20);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
// now alice lays at 2500 at 1:1. This should require a deposit of 500, with the remaining 200 being funded from exposure
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(2500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 50);
place_bet(alice_id, capitals_win_market.id, bet_type::lay, asset(2500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
// match the bet bit by bit. bob matches 500 of alice's 2500 bet. This effectively cancels half of bob's lay position
// so he immediately gets 500 back. It reduces alice's back position, but doesn't return any money to her (all 2000 of her exposure
// was already "promised" to her lay bet, so the 500 she would have received is placed in her refundable_unmatched_bets)
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 10);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
// match another 500, which will fully cancel bob's lay position and return the other 500 he had locked up in his position.
// alice's back position is now canceled, 1500 remains of her unmatched lay bet, and the 500 from canceling her position has
// been moved to her refundable_unmatched_bets
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 10);
place_bet(bob_id, capitals_win_market.id, bet_type::back, asset(500, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
capitals_win_betting_market_id = capitals_win_market.id;
}
@ -554,9 +595,9 @@ BOOST_AUTO_TEST_CASE(sport_update_test)
update_sport(ice_hockey.id, {{"en", "Hockey on Ice"}, {"zh_Hans", ""}, {"ja", "アイスホッケ"}});
transfer(account_id_type(), alice_id, asset(10000000));
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000);
} FC_LOG_AND_RETHROW()
}
@ -571,7 +612,7 @@ BOOST_AUTO_TEST_CASE(event_group_update_test)
transfer(account_id_type(), alice_id, asset(10000000));
transfer(account_id_type(), bob_id, asset(10000000));
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
const sport_object& ice_on_hockey = create_sport({{"en", "Hockey on Ice"}, {"zh_Hans", "冰球"}, {"ja", "アイスホッケー"}}); \
fc::optional<object_id_type> sport_id = ice_on_hockey.id;
@ -582,10 +623,10 @@ BOOST_AUTO_TEST_CASE(event_group_update_test)
update_event_group(nhl.id, sport_id, fc::optional<internationalized_string_type>());
update_event_group(nhl.id, sport_id, name);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
// caps win
resolve_betting_market_group(moneyline_betting_markets.id,
@ -596,8 +637,8 @@ BOOST_AUTO_TEST_CASE(event_group_update_test)
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
} FC_LOG_AND_RETHROW()
}
@ -613,7 +654,7 @@ BOOST_AUTO_TEST_CASE(event_update_test)
transfer(account_id_type(), alice_id, asset(10000000));
transfer(account_id_type(), bob_id, asset(10000000));
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
fc::optional<internationalized_string_type> empty;
fc::optional<internationalized_string_type> name = internationalized_string_type({{"en", "Washington Capitals vs. Chicago Blackhawks"}, {"zh_Hans", "華盛頓首都隊/芝加哥黑"}, {"ja", "ワシントン・キャピタルズ/シカゴ・ブラックホーク"}});
@ -631,10 +672,10 @@ BOOST_AUTO_TEST_CASE(event_update_test)
update_event(capitals_vs_blackhawks.id, event_group_id , empty, empty, empty_bool);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
// caps win
resolve_betting_market_group(moneyline_betting_markets.id,
@ -645,8 +686,8 @@ BOOST_AUTO_TEST_CASE(event_update_test)
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
} FC_LOG_AND_RETHROW()
}
@ -667,9 +708,9 @@ BOOST_AUTO_TEST_CASE(betting_market_rules_update_test)
update_betting_market_rules(betting_market_rules.id, name, desc);
transfer(account_id_type(), alice_id, asset(10000000));
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000);
} FC_LOG_AND_RETHROW()
}
@ -682,7 +723,7 @@ BOOST_AUTO_TEST_CASE(betting_market_group_update_test)
CREATE_ICE_HOCKEY_BETTING_MARKET();
transfer(account_id_type(), alice_id, asset(10000000));
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
fc::optional<internationalized_string_type> dempty;
fc::optional<object_id_type> empty_object_id;
@ -702,10 +743,10 @@ BOOST_AUTO_TEST_CASE(betting_market_group_update_test)
update_betting_market_group(moneyline_betting_markets.id, new_desc, new_event, new_rule, freeze);
transfer(account_id_type(), bob_id, asset(10000000));
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
// caps win
resolve_betting_market_group(moneyline_betting_markets.id,
@ -716,8 +757,8 @@ BOOST_AUTO_TEST_CASE(betting_market_group_update_test)
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
} FC_LOG_AND_RETHROW()
}
@ -730,7 +771,7 @@ BOOST_AUTO_TEST_CASE(betting_market_update_test)
CREATE_ICE_HOCKEY_BETTING_MARKET();
transfer(account_id_type(), alice_id, asset(10000000));
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
const betting_market_group_object& new_moneyline_betting_markets = create_betting_market_group({{"en", "New Moneyline"}}, capitals_vs_blackhawks.id, betting_market_rules.id, asset_id_type()); \
fc::optional<object_id_type> betting_market_group = new_moneyline_betting_markets.id;
@ -744,10 +785,10 @@ BOOST_AUTO_TEST_CASE(betting_market_update_test)
update_betting_market(blackhawks_win_market.id, betting_market_group, fc::optional<internationalized_string_type>());
transfer(account_id_type(), bob_id, asset(10000000));
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
// caps win
resolve_betting_market_group(new_moneyline_betting_markets.id,
@ -758,8 +799,8 @@ BOOST_AUTO_TEST_CASE(betting_market_update_test)
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
} FC_LOG_AND_RETHROW()
}
@ -780,9 +821,9 @@ BOOST_AUTO_TEST_SUITE(other_betting_tests)
{ \
if (1) \
{ \
edump(("###")); \
elog("###"); \
edump((e.to_detail_string())); \
edump(("###")); \
elog("###"); \
} \
FC_ASSERT(e.to_detail_string().find(reason) != \
std::string::npos, "expected error hasn't occured");\
@ -803,7 +844,7 @@ BOOST_FIXTURE_TEST_CASE( another_event_group_update_test, database_fixture)
transfer(account_id_type(), alice_id, asset(10000000));
transfer(account_id_type(), bob_id, asset(10000000));
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(alice_id, capitals_win_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
fc::optional<internationalized_string_type> name = internationalized_string_type({{"en", "IBM"}, {"zh_Hans", "國家冰球聯"}, {"ja", "ナショナルホッケーリー"}});
@ -832,10 +873,10 @@ BOOST_FIXTURE_TEST_CASE( another_event_group_update_test, database_fixture)
//GRAPHENE_REQUIRE_THROW(try_update_event_group(nhl.id, sport_id, fc::optional<internationalized_string_type>()), fc::exception);
TRY_EXPECT_THROW(try_update_event_group(nhl.id, sport_id, fc::optional<internationalized_string_type>()), fc::exception, "invalid sport specified");
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(bob_id, capitals_win_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
// caps win
resolve_betting_market_group(moneyline_betting_markets.id,
@ -846,8 +887,8 @@ BOOST_FIXTURE_TEST_CASE( another_event_group_update_test, database_fixture)
uint16_t rake_fee_percentage = db.get_global_properties().parameters.betting_rake_fee_percentage;
uint32_t rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Rake value " + std::to_string(rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 + 2000000 - rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
} FC_LOG_AND_RETHROW()
}
@ -895,17 +936,17 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_sf_test )
BOOST_TEST_MESSAGE("cilic_wins_market " << fc::variant(cilic_wins_market.id).as<std::string>());
BOOST_TEST_MESSAGE("querrey_wins_market " << fc::variant(querrey_wins_market.id).as<std::string>());
place_bet(alice_id, berdych_wins_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(bob_id, berdych_wins_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(alice_id, berdych_wins_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
place_bet(bob_id, berdych_wins_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
place_bet(alice_id, cilic_wins_market.id, bet_type::back, asset(100000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 100000 / 50 /* chain defaults to 2% fees */);
place_bet(bob_id, cilic_wins_market.id, bet_type::lay, asset(100000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 100000 / 50 /* chain defaults to 2% fees */);
place_bet(alice_id, cilic_wins_market.id, bet_type::back, asset(100000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
place_bet(bob_id, cilic_wins_market.id, bet_type::lay, asset(100000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 - 100000 - 2000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000 - 100000 - 2000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 100000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 100000);
// federer wins
resolve_betting_market_group(moneyline_berdych_vs_federer.id,
@ -915,8 +956,8 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_sf_test )
uint32_t bob_rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Bob's rake value " + std::to_string(bob_rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 - 100000 - 2000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000 - 100000 - 2000 + 2000000 - bob_rake_value);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 100000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 100000 + 2000000 - bob_rake_value);
// cilic wins
resolve_betting_market_group(moneyline_cilic_vs_querrey.id,
@ -926,8 +967,8 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_sf_test )
uint32_t alice_rake_value = (-100000 + 200000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Alice rake value " + std::to_string(alice_rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000 - 100000 - 2000 + 200000 - alice_rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000 - 100000 - 2000 + 2000000 - bob_rake_value);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 100000 + 200000 - alice_rake_value);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 100000 + 2000000 - bob_rake_value);
} FC_LOG_AND_RETHROW()
}
@ -949,8 +990,8 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_final_test )
BOOST_TEST_MESSAGE("federer_wins_final_market " << fc::variant(federer_wins_final_market.id).as<std::string>());
BOOST_TEST_MESSAGE("cilic_wins_final_market " << fc::variant(cilic_wins_final_market.id).as<std::string>());
place_bet(alice_id, cilic_wins_final_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(bob_id, cilic_wins_final_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION, 1000000 / 50 /* chain defaults to 2% fees */);
place_bet(alice_id, cilic_wins_final_market.id, bet_type::back, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
place_bet(bob_id, cilic_wins_final_market.id, bet_type::lay, asset(1000000, asset_id_type()), 2 * GRAPHENE_BETTING_ODDS_PRECISION);
betting_market_group_id_type moneyline_cilic_vs_federer_id = moneyline_cilic_vs_federer.id;
auto cilic_wins_final_market_id = cilic_wins_final_market.id;
@ -966,8 +1007,8 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_final_test )
const betting_market_group_object& betting_market_group = moneyline_cilic_vs_federer_id(db);
BOOST_CHECK_EQUAL(betting_market_group.total_matched_bets_amount.value, 2000000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000);
// federer wins
resolve_betting_market_group(moneyline_cilic_vs_federer_id,
@ -977,8 +1018,8 @@ BOOST_AUTO_TEST_CASE( wimbledon_2017_gentelmen_singles_final_test )
uint32_t bob_rake_value = (-1000000 + 2000000) * rake_fee_percentage / GRAPHENE_1_PERCENT / 100;
BOOST_TEST_MESSAGE("Bob's rake value " + std::to_string(bob_rake_value));
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000 - 20000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 - 20000 + 2000000 - bob_rake_value);
BOOST_CHECK_EQUAL(get_balance(alice_id, asset_id_type()), 10000000 - 1000000);
BOOST_CHECK_EQUAL(get_balance(bob_id, asset_id_type()), 10000000 - 1000000 + 2000000 - bob_rake_value);
} FC_LOG_AND_RETHROW()
}

View file

@ -225,7 +225,6 @@ void database_fixture::verify_asset_supplies( const database& db )
for (const bet_object& o : db.get_index_type<bet_object_index>().indices())
{
total_balances[o.amount_to_bet.asset_id] += o.amount_to_bet.amount;
total_balances[o.amount_to_bet.asset_id] += o.amount_reserved_for_fees;
}
for (const betting_market_position_object& o : db.get_index_type<betting_market_position_index>().indices())
{
@ -1396,14 +1395,13 @@ void database_fixture::update_betting_market(betting_market_id_type betting_mark
} FC_CAPTURE_AND_RETHROW( (betting_market_id) (group_id) (payout_condition) ) }
void database_fixture::place_bet(account_id_type bettor_id, betting_market_id_type betting_market_id, bet_type back_or_lay, asset amount_to_bet, bet_multiplier_type backer_multiplier, share_type amount_reserved_for_fees)
void database_fixture::place_bet(account_id_type bettor_id, betting_market_id_type betting_market_id, bet_type back_or_lay, asset amount_to_bet, bet_multiplier_type backer_multiplier)
{ try {
bet_place_operation bet_place_op;
bet_place_op.bettor_id = bettor_id;
bet_place_op.betting_market_id = betting_market_id;
bet_place_op.amount_to_bet = amount_to_bet;
bet_place_op.backer_multiplier = backer_multiplier;
bet_place_op.amount_reserved_for_fees = amount_reserved_for_fees;
bet_place_op.back_or_lay = back_or_lay;
trx.operations.push_back(bet_place_op);

View file

@ -319,7 +319,7 @@ struct database_fixture {
/*fc::optional<internationalized_string_type> description,*/
fc::optional<internationalized_string_type> payout_condition);
void place_bet(account_id_type bettor_id, betting_market_id_type betting_market_id, bet_type back_or_lay, asset amount_to_bet, bet_multiplier_type backer_multiplier, share_type amount_reserved_for_fees);
void place_bet(account_id_type bettor_id, betting_market_id_type betting_market_id, bet_type back_or_lay, asset amount_to_bet, bet_multiplier_type backer_multiplier);
void resolve_betting_market_group(betting_market_group_id_type betting_market_group_id, std::map<betting_market_id_type, betting_market_resolution_type> resolutions);
void cancel_unmatched_bets(betting_market_group_id_type betting_market_group_id);