Issue #47 - Basic, Untested, Market History Plugin
This commit is contained in:
parent
0d02361af0
commit
fdefc69baf
6 changed files with 342 additions and 2 deletions
|
|
@ -104,9 +104,9 @@ class plugin : public abstract_plugin
|
||||||
bpo::options_description& config_file_options
|
bpo::options_description& config_file_options
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
protected:
|
|
||||||
chain::database& database() { return *app().chain_database(); }
|
chain::database& database() { return *app().chain_database(); }
|
||||||
application& app()const { assert(_app); return *_app; }
|
application& app()const { assert(_app); return *_app; }
|
||||||
|
protected:
|
||||||
net::node& p2p_node() { return *app().p2p_node(); }
|
net::node& p2p_node() { return *app().p2p_node(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
add_subdirectory( witness )
|
add_subdirectory( witness )
|
||||||
add_subdirectory( account_history )
|
add_subdirectory( account_history )
|
||||||
|
add_subdirectory( market_history )
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@ namespace bpo = boost::program_options;
|
||||||
|
|
||||||
enum account_history_object_type
|
enum account_history_object_type
|
||||||
{
|
{
|
||||||
key_account_object_type
|
key_account_object_type = 0,
|
||||||
|
bucket_object_type = 1 ///< used in market_history_plugin
|
||||||
};
|
};
|
||||||
|
|
||||||
class key_account_object : public abstract_object<key_account_object>
|
class key_account_object : public abstract_object<key_account_object>
|
||||||
|
|
|
||||||
13
libraries/plugins/market_history/CMakeLists.txt
Normal file
13
libraries/plugins/market_history/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
file(GLOB HEADERS "include/graphene/market_history/*.hpp")
|
||||||
|
|
||||||
|
add_library( graphene_market_history
|
||||||
|
market_history_plugin.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries( graphene_market_history graphene_chain graphene_app )
|
||||||
|
target_include_directories( graphene_market_history
|
||||||
|
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
set_source_files_properties( market_history_plugin.cpp PROPERTIES COMPILE_FLAGS "/bigobj" )
|
||||||
|
endif(MSVC)
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Cryptonomex, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
|
||||||
|
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted until September 8, 2015, provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <graphene/app/plugin.hpp>
|
||||||
|
#include <graphene/chain/database.hpp>
|
||||||
|
|
||||||
|
#include <fc/thread/future.hpp>
|
||||||
|
|
||||||
|
namespace graphene { namespace market_history {
|
||||||
|
using namespace chain;
|
||||||
|
namespace bpo = boost::program_options;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Plugins should #define their SPACE_ID's so plugins with
|
||||||
|
// conflicting SPACE_ID assignments can be compiled into the
|
||||||
|
// same binary (by simply re-assigning some of the conflicting #defined
|
||||||
|
// SPACE_ID's in a build script).
|
||||||
|
//
|
||||||
|
// Assignment of SPACE_ID's cannot be done at run-time because
|
||||||
|
// various template automagic depends on them being known at compile
|
||||||
|
// time.
|
||||||
|
//
|
||||||
|
#ifndef ACCOUNT_HISTORY_SPACE_ID
|
||||||
|
#define ACCOUNT_HISTORY_SPACE_ID 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct bucket_key
|
||||||
|
{
|
||||||
|
asset_id_type base;
|
||||||
|
asset_id_type quote;
|
||||||
|
uint32_t seconds = 0;
|
||||||
|
fc::time_point_sec open;
|
||||||
|
|
||||||
|
friend bool operator < ( const bucket_key& a, const bucket_key& b )
|
||||||
|
{
|
||||||
|
return std::tie( a.base, a.quote, b.seconds, a.open ) < std::tie( b.base, b.quote, b.seconds, b.open );
|
||||||
|
}
|
||||||
|
friend bool operator == ( const bucket_key& a, const bucket_key& b )
|
||||||
|
{
|
||||||
|
return std::tie( a.base, a.quote, b.seconds, a.open ) == std::tie( b.base, b.quote, b.seconds, b.open );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bucket_object : public abstract_object<bucket_object>
|
||||||
|
{
|
||||||
|
static const uint8_t space_id = ACCOUNT_HISTORY_SPACE_ID;
|
||||||
|
static const uint8_t type_id = 1; // market_history_plugin type, referenced from account_history_plugin.hpp
|
||||||
|
|
||||||
|
price high()const { return asset( high_base, key.base ) / asset( high_quote, key.quote ); }
|
||||||
|
price low()const { return asset( low_base, key.base ) / asset( low_quote, key.quote ); }
|
||||||
|
|
||||||
|
bucket_key key;
|
||||||
|
share_type high_base;
|
||||||
|
share_type high_quote;
|
||||||
|
share_type low_base;
|
||||||
|
share_type low_quote;
|
||||||
|
share_type open_base;
|
||||||
|
share_type open_quote;
|
||||||
|
share_type close_base;
|
||||||
|
share_type close_quote;
|
||||||
|
share_type base_volume;
|
||||||
|
share_type quote_volume;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct by_key;
|
||||||
|
typedef multi_index_container<
|
||||||
|
bucket_object,
|
||||||
|
indexed_by<
|
||||||
|
hashed_unique< tag<by_id>, member< object, object_id_type, &object::id > >,
|
||||||
|
ordered_unique< tag<by_key>, member< bucket_object, bucket_key, &bucket_object::key > >
|
||||||
|
>
|
||||||
|
> bucket_object_multi_index_type;
|
||||||
|
|
||||||
|
typedef generic_index<bucket_object, bucket_object_multi_index_type> bucket_index;
|
||||||
|
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
class market_history_plugin_impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The market history plugin can be configured to track any number of intervals via its configuration. Once per block it
|
||||||
|
* will scan the virtual operations and look for fill_order_operations and then adjust the appropriate bucket objects for
|
||||||
|
* each fill order.
|
||||||
|
*/
|
||||||
|
class market_history_plugin : public graphene::app::plugin
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
market_history_plugin();
|
||||||
|
virtual ~market_history_plugin();
|
||||||
|
|
||||||
|
std::string plugin_name()const override;
|
||||||
|
virtual void plugin_set_program_options(bpo::options_description& cli, bpo::options_description& cfg) override;
|
||||||
|
virtual void plugin_initialize(const bpo::variables_map& options) override;
|
||||||
|
virtual void plugin_startup() override;
|
||||||
|
|
||||||
|
vector<bucket_object> get_history( const bucket_key& start, const bucket_key& end )const;
|
||||||
|
const flat_set<uint32_t>& tracked_buckets()const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class detail::market_history_plugin_impl;
|
||||||
|
std::unique_ptr<detail::market_history_plugin_impl> my;
|
||||||
|
};
|
||||||
|
|
||||||
|
} } //graphene::market_history
|
||||||
|
|
||||||
|
FC_REFLECT( graphene::market_history::bucket_key, (base)(quote)(seconds)(open) )
|
||||||
|
FC_REFLECT( graphene::market_history::bucket_object, (key)
|
||||||
|
(high_base)(high_quote)
|
||||||
|
(low_base)(low_quote)
|
||||||
|
(open_base)(open_quote)
|
||||||
|
(close_base)(close_quote)
|
||||||
|
(base_volume)(quote_volume) )
|
||||||
|
|
||||||
194
libraries/plugins/market_history/market_history_plugin.cpp
Normal file
194
libraries/plugins/market_history/market_history_plugin.cpp
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015, Cryptonomex, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
|
||||||
|
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
* are permitted until September 8, 2015, provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <graphene/market_history/market_history_plugin.hpp>
|
||||||
|
|
||||||
|
#include <graphene/chain/account_evaluator.hpp>
|
||||||
|
#include <graphene/chain/account_object.hpp>
|
||||||
|
#include <graphene/chain/config.hpp>
|
||||||
|
#include <graphene/chain/database.hpp>
|
||||||
|
#include <graphene/chain/evaluator.hpp>
|
||||||
|
#include <graphene/chain/key_object.hpp>
|
||||||
|
#include <graphene/chain/operation_history_object.hpp>
|
||||||
|
#include <graphene/chain/transaction_evaluation_state.hpp>
|
||||||
|
|
||||||
|
#include <fc/thread/thread.hpp>
|
||||||
|
|
||||||
|
namespace graphene { namespace market_history {
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
class market_history_plugin_impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
market_history_plugin_impl(market_history_plugin& _plugin)
|
||||||
|
:_self( _plugin ) {}
|
||||||
|
virtual ~market_history_plugin_impl();
|
||||||
|
|
||||||
|
/** this method is called as a callback after a block is applied
|
||||||
|
* and will process/index all operations that were applied in the block.
|
||||||
|
*/
|
||||||
|
void update_market_histories( const signed_block& b );
|
||||||
|
|
||||||
|
graphene::chain::database& database()
|
||||||
|
{
|
||||||
|
return _self.database();
|
||||||
|
}
|
||||||
|
|
||||||
|
market_history_plugin& _self;
|
||||||
|
flat_set<uint32_t> _tracked_buckets;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct operation_process_fill_order
|
||||||
|
{
|
||||||
|
market_history_plugin& _plugin;
|
||||||
|
fc::time_point_sec _now;
|
||||||
|
|
||||||
|
operation_process_fill_order( market_history_plugin& mhp, fc::time_point_sec n )
|
||||||
|
:_plugin(mhp),_now(n) {}
|
||||||
|
|
||||||
|
typedef void result_type;
|
||||||
|
|
||||||
|
/** do nothing for other operation types */
|
||||||
|
template<typename T>
|
||||||
|
void operator()( const T& )const{}
|
||||||
|
|
||||||
|
void operator()( const fill_order_operation& o )const
|
||||||
|
{
|
||||||
|
const auto& buckets = _plugin.tracked_buckets();
|
||||||
|
auto& db = _plugin.database();
|
||||||
|
const auto& bucket_idx = db.get_index_type<bucket_index>();
|
||||||
|
|
||||||
|
for( auto bucket : buckets )
|
||||||
|
{
|
||||||
|
bucket_key key;
|
||||||
|
key.base = o.pays.asset_id;
|
||||||
|
key.quote = o.receives.asset_id;
|
||||||
|
price trade_price = o.pays / o.receives;
|
||||||
|
if( key.base > key.quote )
|
||||||
|
{
|
||||||
|
std::swap( key.base,key.quote );
|
||||||
|
trade_price = ~trade_price;
|
||||||
|
}
|
||||||
|
|
||||||
|
key.seconds = bucket;
|
||||||
|
key.open = fc::time_point() + fc::seconds((_now.sec_since_epoch() / key.seconds) * key.seconds);
|
||||||
|
|
||||||
|
const auto& by_key_idx = bucket_idx.indices().get<by_key>();
|
||||||
|
auto itr = by_key_idx.find( key );
|
||||||
|
if( itr == by_key_idx.end() )
|
||||||
|
{ // create new bucket
|
||||||
|
db.create<bucket_object>( [&]( bucket_object& b ){
|
||||||
|
b.key = key;
|
||||||
|
b.quote_volume += trade_price.quote.amount;
|
||||||
|
b.open_base = trade_price.base.amount;
|
||||||
|
b.open_quote = trade_price.quote.amount;
|
||||||
|
b.close_base = trade_price.base.amount;
|
||||||
|
b.close_quote = trade_price.quote.amount;
|
||||||
|
b.high_base = b.close_base;
|
||||||
|
b.high_quote = b.close_quote;
|
||||||
|
b.low_base = b.close_base;
|
||||||
|
b.low_quote = b.close_quote;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // update existing bucket
|
||||||
|
db.modify( *itr, [&]( bucket_object& b ){
|
||||||
|
b.base_volume += trade_price.base.amount;
|
||||||
|
b.quote_volume += trade_price.quote.amount;
|
||||||
|
b.close_base = trade_price.base.amount;
|
||||||
|
b.close_quote = trade_price.quote.amount;
|
||||||
|
if( b.high() < trade_price )
|
||||||
|
{
|
||||||
|
b.high_base = b.close_base;
|
||||||
|
b.high_quote = b.close_quote;
|
||||||
|
}
|
||||||
|
if( b.low() > trade_price )
|
||||||
|
{
|
||||||
|
b.low_base = b.close_base;
|
||||||
|
b.low_quote = b.close_quote;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
market_history_plugin_impl::~market_history_plugin_impl()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void market_history_plugin_impl::update_market_histories( const signed_block& b )
|
||||||
|
{
|
||||||
|
graphene::chain::database& db = database();
|
||||||
|
const vector<operation_history_object>& hist = db.get_applied_operations();
|
||||||
|
for( auto op : hist )
|
||||||
|
op.op.visit( operation_process_fill_order( _self, b.timestamp ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
market_history_plugin::market_history_plugin() :
|
||||||
|
my( new detail::market_history_plugin_impl(*this) )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
market_history_plugin::~market_history_plugin()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string market_history_plugin::plugin_name()const
|
||||||
|
{
|
||||||
|
return "market_history";
|
||||||
|
}
|
||||||
|
|
||||||
|
void market_history_plugin::plugin_set_program_options(
|
||||||
|
boost::program_options::options_description& cli,
|
||||||
|
boost::program_options::options_description& cfg
|
||||||
|
)
|
||||||
|
{
|
||||||
|
cli.add_options()
|
||||||
|
("bucket-size", bpo::value<std::vector<uint32_t>>()->composing()->multitoken(), "Bucket size in seconds to track history for (may specify multiple times)")
|
||||||
|
;
|
||||||
|
cfg.add(cli);
|
||||||
|
}
|
||||||
|
|
||||||
|
void market_history_plugin::plugin_initialize(const boost::program_options::variables_map& options)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void market_history_plugin::plugin_startup()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const flat_set<uint32_t>& market_history_plugin::tracked_buckets() const
|
||||||
|
{
|
||||||
|
return my->_tracked_buckets;
|
||||||
|
}
|
||||||
|
|
||||||
|
} }
|
||||||
Loading…
Reference in a new issue