Enable building bookie plugin, add API for bookie plugin, and implement a

function for getting binned order books (as yet untested)
This commit is contained in:
Eric Frias 2017-07-27 19:35:13 -04:00
parent 3ac447b8a7
commit c4470131ac
11 changed files with 219 additions and 80 deletions

View file

@ -13,7 +13,7 @@ add_library( graphene_app
# need to link graphene_debug_witness because plugins aren't sufficiently isolated #246
#target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness )
target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness )
target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie )
target_include_directories( graphene_app
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" )

View file

@ -111,6 +111,12 @@ namespace graphene { namespace app {
if( _app.get_plugin( "debug_witness" ) )
_debug_api = std::make_shared< graphene::debug_witness::debug_api >( std::ref(_app) );
}
else if( api_name == "bookie_api" )
{
// can only enable this API if the plugin was loaded
if( _app.get_plugin( "bookie" ) )
_bookie_api = std::make_shared<graphene::bookie::bookie_api>(std::ref(_app));
}
return;
}
@ -269,6 +275,12 @@ namespace graphene { namespace app {
return *_debug_api;
}
fc::api<graphene::bookie::bookie_api> login_api::bookie() const
{
FC_ASSERT(_bookie_api);
return *_bookie_api;
}
#if 0
vector<account_id_type> get_relevant_accounts( const object* obj )
{

View file

@ -228,6 +228,9 @@ namespace detail {
wsc->register_api(login->database());
wsc->register_api(fc::api<graphene::app::login_api>(login));
wsc->register_api(fc::api<graphene::app::login_api>(login));
c->set_session_data( wsc );
std::string username = "*";
@ -455,6 +458,7 @@ namespace detail {
wild_access.allowed_apis.push_back( "network_broadcast_api" );
wild_access.allowed_apis.push_back( "history_api" );
wild_access.allowed_apis.push_back( "crypto_api" );
wild_access.allowed_apis.push_back( "bookie_api" );
_apiaccess.permission_map["*"] = wild_access;
}
@ -463,6 +467,7 @@ namespace detail {
reset_websocket_tls_server();
} FC_LOG_AND_RETHROW() }
optional< api_access_info > get_api_access_info(const string& username)const
{
optional< api_access_info > result;
@ -1083,3 +1088,7 @@ void application::startup_plugins()
// namespace detail
} }
// for some reason, g++ isn't instantiating this function on its own, force it to here
template
fc::api_id_type fc::api<graphene::bookie::bookie_api, fc::identity_member>::register_api(fc::api_connection&) const;

View file

@ -32,6 +32,7 @@
#include <graphene/accounts_list/accounts_list_plugin.hpp>
#include <graphene/debug_witness/debug_api.hpp>
#include <graphene/bookie/bookie_api.hpp>
#include <graphene/net/node.hpp>
@ -359,6 +360,8 @@ namespace graphene { namespace app {
fc::api<asset_api> asset()const;
/// @brief Retrieve the debug API (if available)
fc::api<graphene::debug_witness::debug_api> debug()const;
/// @brief Retrieve the bookie API (if available)
fc::api<graphene::bookie::bookie_api> bookie()const;
/// @brief Called to enable an API, not reflected.
void enable_api( const string& api_name );
@ -373,6 +376,7 @@ namespace graphene { namespace app {
optional< fc::api<crypto_api> > _crypto_api;
optional< fc::api<asset_api> > _asset_api;
optional< fc::api<graphene::debug_witness::debug_api> > _debug_api;
optional< fc::api<graphene::bookie::bookie_api> > _bookie_api;
};
}} // graphene::app
@ -441,4 +445,5 @@ FC_API(graphene::app::login_api,
(crypto)
(asset)
(debug)
(bookie)
)

View file

@ -2,6 +2,7 @@ file(GLOB HEADERS "include/graphene/bookie/*.hpp")
add_library( graphene_bookie
bookie_plugin.cpp
bookie_api.cpp
)
target_link_libraries( graphene_bookie graphene_chain graphene_app )

View file

@ -0,0 +1,123 @@
#include <fc/filesystem.hpp>
#include <fc/optional.hpp>
#include <fc/variant_object.hpp>
#include <fc/smart_ref_impl.hpp>
#include <graphene/app/application.hpp>
#include <graphene/chain/block_database.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/betting_market_object.hpp>
#include <graphene/chain/witness_object.hpp>
#include <graphene/utilities/key_conversion.hpp>
#include <graphene/bookie/bookie_api.hpp>
#include <graphene/bookie/bookie_plugin.hpp>
namespace graphene { namespace bookie {
namespace detail {
class bookie_api_impl
{
public:
bookie_api_impl(graphene::app::application& _app);
binned_order_book get_binned_order_book(graphene::chain::betting_market_id_type betting_market_id, int32_t precision);
std::shared_ptr<graphene::bookie::bookie_plugin> get_plugin();
graphene::app::application& app;
};
bookie_api_impl::bookie_api_impl(graphene::app::application& _app) : app(_app)
{}
binned_order_book bookie_api_impl::get_binned_order_book(graphene::chain::betting_market_id_type betting_market_id, int32_t precision)
{
std::shared_ptr<graphene::chain::database> db = app.chain_database();
const auto& bet_odds_idx = db->get_index_type<graphene::chain::bet_object_index>().indices().get<graphene::chain::by_odds>();
graphene::chain::bet_multiplier_type bin_size = GRAPHENE_BETTING_ODDS_PRECISION;
if (precision > 0)
for (int32_t i = 0; i < precision; ++i) {
FC_ASSERT(bin_size > (GRAPHENE_BETTING_MIN_MULTIPLIER - GRAPHENE_BETTING_ODDS_PRECISION), "invalid precision");
bin_size /= 10;
}
else if (precision < 0)
for (int32_t i = 0; i > precision; --i) {
FC_ASSERT(bin_size < (GRAPHENE_BETTING_MAX_MULTIPLIER - GRAPHENE_BETTING_ODDS_PRECISION), "invalid precision");
bin_size *= 10;
}
binned_order_book result;
// use a bet_object here for convenience. we really only use it to track the amount, odds, and back_or_lay
// note, the current bin is accumulating the matching bets, so it will be of type 'lay' when we're
// binning 'back' bets, and vice versa
fc::optional<bet_object> current_bin;
auto flush_current_bin = [&current_bin, &result]()
{
if (current_bin) // do nothing if the current bin is empty
{
order_bin current_order_bin;
current_order_bin.backer_multiplier = current_bin->backer_multiplier;
current_order_bin.amount_to_bet = current_bin->get_matching_amount();;
if (current_bin->back_or_lay == bet_type::lay)
result.aggregated_back_bets.emplace_back(std::move(current_order_bin));
else // current_bin is aggregating back positions
result.aggregated_lay_bets.emplace_back(std::move(current_order_bin));
current_bin.reset();
}
};
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))
flush_current_bin();
if (!current_bin)
{
// 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->amount_to_bet.amount = 0;
current_bin->back_or_lay = bet_odds_iter->back_or_lay == bet_type::back ? bet_type::lay : bet_type::back;
}
current_bin->amount_to_bet.amount += bet_odds_iter->get_matching_amount();
}
if (current_bin)
flush_current_bin();
return result;
}
std::shared_ptr<graphene::bookie::bookie_plugin> bookie_api_impl::get_plugin()
{
return app.get_plugin<graphene::bookie::bookie_plugin>("bookie");
}
} // detail
bookie_api::bookie_api(graphene::app::application& app) :
my(std::make_shared<detail::bookie_api_impl>(app))
{
}
binned_order_book bookie_api::get_binned_order_book(graphene::chain::betting_market_id_type betting_market_id, int32_t precision)
{
return my->get_binned_order_book(betting_market_id, precision);
}
} } // graphene::bookie

View file

@ -215,77 +215,7 @@ void bookie_plugin_impl::on_objects_changed(const vector<object_id_type>& change
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 )
{
// add to the operation history index
const auto& oho = db.create<operation_history_object>( [&]( operation_history_object& h )
{
if( o_op.valid() )
h = *o_op;
} );
if( !o_op.valid() )
{
ilog( "removing failed operation with ID: ${id}", ("id", oho.id) );
db.remove( oho );
continue;
}
const operation_history_object& op = *o_op;
// get the set of accounts this operation applies to
flat_set<account_id_type> impacted;
vector<authority> other;
operation_get_required_authorities( op.op, impacted, impacted, other );
if( op.op.which() == operation::tag< account_create_operation >::value )
impacted.insert( oho.result.get<object_id_type>() );
else
graphene::app::operation_get_impacted_accounts( op.op, impacted );
for( auto& a : other )
for( auto& item : a.account_auths )
impacted.insert( item.first );
// for each operation this account applies to that is in the config link it into the history
if( _tracked_accounts.size() == 0 )
{
for( auto& account_id : impacted )
{
// we don't do index_account_keys here anymore, because
// that indexing now happens in observers' post_evaluate()
// add history
const auto& stats_obj = account_id(db).statistics(db);
const auto& ath = db.create<account_transaction_history_object>( [&]( account_transaction_history_object& obj ){
obj.operation_id = oho.id;
obj.next = stats_obj.most_recent_op;
});
db.modify( stats_obj, [&]( account_statistics_object& obj ){
obj.most_recent_op = ath.id;
});
}
}
else
{
for( auto account_id : _tracked_accounts )
{
if( impacted.find( account_id ) != impacted.end() )
{
// add history
const auto& stats_obj = account_id(db).statistics(db);
const auto& ath = db.create<account_transaction_history_object>( [&]( account_transaction_history_object& obj ){
obj.operation_id = oho.id;
obj.next = stats_obj.most_recent_op;
});
db.modify( stats_obj, [&]( account_statistics_object& obj ){
obj.most_recent_op = ath.id;
});
}
}
}
}
const vector<optional<operation_history_object> >& hist = db.get_applied_operations();
}
} // end namespace detail
@ -308,24 +238,27 @@ void bookie_plugin::plugin_set_program_options(
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)")
;
cfg.add(cli);
//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)")
// ;
//cfg.add(cli);
}
void bookie_plugin::plugin_initialize(const boost::program_options::variables_map& options)
{
database().applied_block.connect( [&]( const signed_block& b){ my->on_block_applied(b); } );
ilog("bookie plugin: plugin_startup() begin");
//database().applied_block.connect( [&]( const signed_block& b){ my->on_block_applied(b); } );
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);
//LOAD_VALUE_SET(options, "tracked-accounts", my->_tracked_accounts, graphene::chain::account_id_type);
ilog("bookie plugin: plugin_startup() end");
}
void bookie_plugin::plugin_startup()
{
ilog("bookie plugin: plugin_startup()");
}
flat_set<account_id_type> bookie_plugin::tracked_accounts() const

View file

@ -0,0 +1,54 @@
#pragma once
#include <memory>
#include <string>
#include <fc/api.hpp>
#include <fc/variant_object.hpp>
#include <graphene/chain/protocol/types.hpp>
namespace graphene { namespace app {
class application;
} }
namespace graphene { namespace bookie {
namespace detail {
class bookie_api_impl;
}
struct order_bin {
graphene::chain::share_type amount_to_bet;
graphene::chain::bet_multiplier_type backer_multiplier;
};
struct binned_order_book {
std::vector<order_bin> aggregated_back_bets;
std::vector<order_bin> aggregated_lay_bets;
};
class bookie_api
{
public:
bookie_api(graphene::app::application& app);
/**
* Returns the current order book, binned according to the given precision.
* precision = 1 means bin using one decimal place: (1 - 1.1], (1.1 - 1.2], etc.
* precision = 2 would bin on (1 - 1.01], (1.01 - 1.02]
*/
binned_order_book get_binned_order_book(graphene::chain::betting_market_id_type betting_market_id, int32_t precision);
std::shared_ptr<detail::bookie_api_impl> my;
};
} }
FC_REFLECT(graphene::bookie::order_bin, (amount_to_bet)(backer_multiplier))
FC_REFLECT(graphene::bookie::binned_order_book, (aggregated_back_bets)(aggregated_lay_bets))
FC_API(graphene::bookie::bookie_api,
(get_binned_order_book)
)

View file

@ -10,7 +10,7 @@ if( GPERFTOOLS_FOUND )
endif()
target_link_libraries( debug_node
PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_debug_witness graphene_chain graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_debug_witness graphene_bookie graphene_chain graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
install( TARGETS
debug_node

View file

@ -11,7 +11,7 @@ endif()
# We have to link against graphene_debug_witness because deficiency in our API infrastructure doesn't allow plugins to be fully abstracted #246
target_link_libraries( witness_node
PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
PRIVATE graphene_app graphene_account_history graphene_market_history graphene_witness graphene_chain graphene_debug_witness graphene_bookie graphene_egenesis_full fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} )
# also add dependencies to graphene_generate_genesis graphene_generate_uia_sharedrop_genesis if you want those plugins
install( TARGETS

View file

@ -29,6 +29,7 @@
#include <graphene/market_history/market_history_plugin.hpp>
//#include <graphene/generate_genesis/generate_genesis_plugin.hpp>
//#include <graphene/generate_uia_sharedrop_genesis/generate_uia_sharedrop_genesis.hpp>
#include <graphene/bookie/bookie_plugin.hpp>
#include <fc/exception/exception.hpp>
#include <fc/thread/thread.hpp>
@ -80,6 +81,7 @@ int main(int argc, char** argv) {
//auto generate_genesis_plug = node->register_plugin<generate_genesis_plugin::generate_genesis_plugin>();
//auto generate_uia_sharedrop_genesis_plug = node->register_plugin<generate_uia_sharedrop_genesis::generate_uia_sharedrop_genesis_plugin>();
auto list_plug = node->register_plugin<accounts_list::accounts_list_plugin>();
auto bookie_plug = node->register_plugin<bookie::bookie_plugin>();
try
{