diff --git a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp index 79484194..201cf3a1 100644 --- a/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp +++ b/libraries/plugins/market_history/include/graphene/market_history/market_history_plugin.hpp @@ -117,6 +117,7 @@ class market_history_plugin : public graphene::app::plugin const boost::program_options::variables_map& options) override; virtual void plugin_startup() override; + uint32_t max_history()const; const flat_set& tracked_buckets()const; private: diff --git a/libraries/plugins/market_history/market_history_plugin.cpp b/libraries/plugins/market_history/market_history_plugin.cpp index c097befd..25c85b79 100644 --- a/libraries/plugins/market_history/market_history_plugin.cpp +++ b/libraries/plugins/market_history/market_history_plugin.cpp @@ -53,6 +53,7 @@ class market_history_plugin_impl market_history_plugin& _self; flat_set _tracked_buckets; + uint32_t _maximum_history_per_bucket_size = 1000; }; @@ -76,17 +77,23 @@ struct operation_process_fill_order auto& db = _plugin.database(); const auto& bucket_idx = db.get_index_type(); + auto max_history = _plugin.max_history(); for( auto bucket : buckets ) { + auto cutoff = (fc::time_point() + fc::seconds( bucket * max_history)); + bucket_key key; key.base = o.pays.asset_id; key.quote = o.receives.asset_id; - price trade_price = o.pays / o.receives; + + /** for every matched order there are two fill order operations created, one for + * each side. We can filter the duplicates by only considering the fill operations where + * the base > quote + */ if( key.base > key.quote ) - { - std::swap( key.base,key.quote ); - trade_price = ~trade_price; - } + continue; + + price trade_price = o.pays / o.receives; key.seconds = bucket; key.open = fc::time_point() + fc::seconds((_now.sec_since_epoch() / key.seconds) * key.seconds); @@ -127,6 +134,23 @@ struct operation_process_fill_order } }); } + + if( max_history != 0 ) + { + key.open = fc::time_point_sec(); + auto itr = by_key_idx.lower_bound( key ); + + while( itr != by_key_idx.end() && + itr->key.base == key.base && + itr->key.quote == key.quote && + itr->key.seconds == bucket && + itr->key.open < cutoff ) + { + auto old_itr = itr; + ++itr; + db.remove( *old_itr ); + } + } } } }; @@ -136,6 +160,9 @@ market_history_plugin_impl::~market_history_plugin_impl() void market_history_plugin_impl::update_market_histories( const signed_block& b ) { + if( _maximum_history_per_bucket_size == 0 ) return; + if( _tracked_buckets.size() == 0 ) return; + graphene::chain::database& db = database(); const vector& hist = db.get_applied_operations(); for( auto op : hist ) @@ -169,18 +196,23 @@ void market_history_plugin::plugin_set_program_options( ) { cli.add_options() - ("bucket-size", boost::program_options::value>()->composing()->multitoken(), "Bucket size in seconds to track history for (may specify multiple times)") + ("bucket-size", boost::program_options::value>()->composing()->multitoken(), + "Track market history by grouping orders into buckets of equal size measured in seconds, may specify more than one bucket size") + ("history-per-size", boost::program_options::value()->default_value(1000), + "How far back in time to track history for each bucket size, measured in the number of buckets (default: 1000)") ; cfg.add(cli); } void market_history_plugin::plugin_initialize(const boost::program_options::variables_map& options) -{ +{ try { database().applied_block.connect( [&]( const signed_block& b){ my->update_market_histories(b); } ); database().add_index< primary_index< bucket_index > >(); LOAD_VALUE_SET(options, "bucket-size", my->_tracked_buckets, uint32_t); -} + if( options.count( "history-per-size" ) ) + my->_maximum_history_per_bucket_size = options["history-per-size"].as(); +} FC_CAPTURE_AND_RETHROW() } void market_history_plugin::plugin_startup() { @@ -191,4 +223,9 @@ const flat_set& market_history_plugin::tracked_buckets() const return my->_tracked_buckets; } +uint32_t market_history_plugin::max_history()const +{ + return my->_maximum_history_per_bucket_size; +} + } } diff --git a/tests/common/database_fixture.cpp b/tests/common/database_fixture.cpp index 45803265..e34bae4e 100644 --- a/tests/common/database_fixture.cpp +++ b/tests/common/database_fixture.cpp @@ -19,6 +19,7 @@ #include #include +#include #include @@ -46,13 +47,17 @@ using std::cerr; database_fixture::database_fixture() : app(), db( *app.chain_database() ) { + try { auto ahplugin = app.register_plugin(); + auto mhplugin = app.register_plugin(); boost::program_options::variables_map options; // app.initialize(); ahplugin->plugin_set_app(&app); ahplugin->plugin_initialize(options); + mhplugin->plugin_set_app(&app); + mhplugin->plugin_initialize(options); secret_hash_type::encoder enc; fc::raw::pack(enc, delegate_priv_key); @@ -68,11 +73,17 @@ database_fixture::database_fixture() fc::reflector::visit(fee_schedule_type::fee_set_visitor{genesis_state.initial_parameters.current_fees, 0}); db.init_genesis(genesis_state); ahplugin->plugin_startup(); + mhplugin->plugin_startup(); generate_block(); genesis_key(db); // attempt to deref trx.set_expiration(db.head_block_time() + fc::minutes(1)); + } catch ( const fc::exception& e ) + { + edump( (e.to_detail_string()) ); + throw; + } return; }