A mostly blank Algo template that can be used as a jump-start for developing a new Algo.
#include "spark/lib/config/config.h" #include "spark/lib/config/protos/system.pb.h" #include "spark/lib/domain_model/Security.h" #include "spark/lib/orderman/orders/algo/IAlgoOrder.h" #include "spark/lib/orderman/orders/algo/IAlgoServices.h" #include "spark/lib/plugin/SparkServices.h" #include "spark/lib/orderman/order_factory/IOrderFactory.h" #include "spark/lib/orderman/orderman_utils/Book.h" #include "spark/lib/orderman/orderman_utils/orderman_utils.h" #include "spark/lib/utils/PBUtils.h" using namespace bts::api::orderman; using namespace bts::orderman; namespace bts { namespace order_example { // MyAlgo is a blank IAlgoOrder which you can copy when starting a new Algo class MyAlgo : public IAlgoOrder { public: MyAlgo(); ~MyAlgo(); OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override; void do_launch(IAlgoServices &) override; void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override; void do_update(api::orderman::Update const &) override; void on_book(SecurityKey, Book const &) override; void on_child_change(OrderKey, ChildState const &) override; private: IAlgoServices* algo_services; ChildTemplate *child_template = nullptr; OrderKey child_key = null_order_key(); }; MyAlgo::MyAlgo() : algo_services(nullptr) {} MyAlgo::~MyAlgo() {} // This is called outside of the latency sensitive path. // Expensive initialization should be done here, rather than in do_launch() OrderOpResult MyAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) { LOG(Sdk,Info, "MyAlgo: do_before_launch"); // If we are not attaching, create a child template so we can // quickly launch our child when the time comes. if (!serv.is_attaching()) { // We will be sending a limit order with the same side, // security, trader, account, etc, as this algo order, so just // copy our definition to the child definition to start with. auto child_def = serv.definition(); // Now add a limit order component to mark our child as a limit // order. Limit orders do not require any extra parameters // beyond those already inherited from this algo order // (i.e. beyond what we obtained through serv.definition()) so // we can just use a default constructed LimitOrderDefinition. *child_def.mutable_custom_def()->mutable_limit() = LimitOrderDefinition{}; // Now just build the child template and cache it for future use child_template = &(serv.make_child_template(child_def)); } // Default constructed result is successful return {}; } // Called in the latency sensitive path, once, when the algo is first launched. // If the Algo is attaching to an existing order do_launch_attached is called instead. void MyAlgo::do_launch(IAlgoServices &services) { algo_services = &services; // Subscribe to book updates for this security services.subscribe_book(SecurityKey(services.security().id())); // Launch a child limit order child_key = services.launch_child(*child_template, services.price(), services.quantity()); LOG(Sdk,Info, "MyAlgo: do_launch"); } // Called in the latency sensitive path, once, when the algo is first // launched. In this case the algo is provided with an existing order // it is now responsible for managing. void MyAlgo::do_launch_attached(IAlgoServices &services, OrderKey attached_order, ChildState st) { algo_services = &services; // Subscribe to book updates for this security services.subscribe_book(SecurityKey(services.security().id())); // Keep track of the child key child_key = attached_order; LOG(Sdk,Info, "MyAlgo: do_launch_attached"); } // Called when a human (or a supervising algo) updates a parameter for // this algo. The implementation validates the update and depending on // the validation outcome either applies the update or terminates the algo. void MyAlgo::do_update(api::orderman::Update const &update) { } // Called when the book for the specified security has changed void MyAlgo::on_book(SecurityKey, Book const &b) { } // Our child reported a change void MyAlgo::on_child_change(OrderKey, ChildState const &st) { } // This is called by the spark daemon at startup. In this call we // describe our algo's parameters and register a factory function to // create an instance of the algo. extern "C" void init_spark_plugin(bts::SparkServices &serv) { using namespace bts; DynamicOrderDescriptor desc; // Start by naming our order type desc.set_dynamic_order_type("My Algo"); *desc.add_param_desc() = param_bool("Enable Feature"); *desc.add_param_desc() = param_uint32("Feature Quantity"); // Associate the descriptor with a builder function and register // that with the Spark server. auto result = serv.register_dynamic_algo_order( desc, []() { return std::make_unique<MyAlgo>(); }); if (result != bts::AlgoRegistrationResult::SUCCESS) { // The desc has errors // Check the error log (/sparkdata/data/logging/log) for more details } } } // namespace order_example } // namespace bts