Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
479f3dccff | ||
|
|
c272d8a8c2 | ||
|
|
fbd8bbd4a8 | ||
|
|
3da52b350f |
No files matched your search
@@ -143,7 +143,7 @@ namespace mbts.spark {
|
||||
string candidate = string.Empty;
|
||||
if (a.HasValue) {
|
||||
var algo_name = a.Value.name;
|
||||
var words = algo_name.Split(delim);
|
||||
var words = algo_name.Split(delim, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (words.Length == 1) {
|
||||
var single_word = words[0];
|
||||
if (single_word.Length > 1) {
|
||||
@@ -152,7 +152,7 @@ namespace mbts.spark {
|
||||
candidate = single_word;
|
||||
}
|
||||
} else {
|
||||
candidate = words.Take(2).Select(w => w.Substring(0, 1)).list_string(string.Empty);
|
||||
candidate = string.Join(string.Empty, words.Take(2).Select(w => w.Substring(0, 1)));
|
||||
}
|
||||
}
|
||||
return candidate.ToUpper();
|
||||
|
||||
@@ -69,4 +69,5 @@ message DynamicAlgos{
|
||||
optional bool enable_tas = 3 [default = false];
|
||||
optional bool enable_tascounter = 4 [default = false];
|
||||
optional bool enable_limit_cover = 5 [default = false];
|
||||
optional bool enable_onsignal = 6 [default = false];
|
||||
}
|
||||
@@ -211,6 +211,9 @@ inline bool gr_than(double p1, double p2, double tick_size) {
|
||||
return gr_than_eq(p1, p2, tick_size) &&
|
||||
!test_price_equality(p1, p2, tick_size);
|
||||
}
|
||||
inline bool test_price_equality(double price_1, double price_2) {
|
||||
return std::fabs(price_2 - price_1) < market_epsilon();
|
||||
}
|
||||
|
||||
} // namespace securities
|
||||
} // namespace bts
|
||||
@@ -0,0 +1,247 @@
|
||||
#include "lib/orderman/orders/algo/algo_orders/OnSignalAlgo.h"
|
||||
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||
#include "lib/orderman/orderman_utils/Book.h"
|
||||
#include "lib/orderman/orderman_utils/orderman_utils.h"
|
||||
#include "lib/orderman/orders/algo/IAlgoServices.h"
|
||||
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||
#include "lib/orderman/actors/SODPosition.h"
|
||||
#include "lib/os/os.h"
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include "lib/utils/datetime.h"
|
||||
#include <google/protobuf/util/time_util.h>
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
using namespace google::protobuf;
|
||||
using namespace boost;
|
||||
|
||||
struct OnSignalLegData{
|
||||
const SecurityKey sec_key;
|
||||
ChildTemplate* order_template = nullptr;
|
||||
const Price price;
|
||||
const Quantity quantity;
|
||||
const api::Side side;
|
||||
OrderKey order_key;
|
||||
ChildState order_state;
|
||||
};
|
||||
|
||||
enum class OnSignalAlgo::State { WAITING_FOR_SIGNAL, WORKING, DONE };
|
||||
|
||||
struct OnSignalAlgoParams{
|
||||
std::optional<SecurityKey> signal_key_1 = std::nullopt;
|
||||
std::optional<google::protobuf::Timestamp> time = std::nullopt;
|
||||
bool preopen = false;
|
||||
bool open = false;
|
||||
std::vector<OnSignalLegData> legs;
|
||||
OnSignalLegData* find_leg_from_ord_key (OrderKey ord_key){
|
||||
for (auto& l : legs)
|
||||
if(l.order_key == ord_key)
|
||||
return &l;
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct OnSignalAlgo::PImpl {
|
||||
IAlgoServices* algo_services = nullptr;
|
||||
State state = State::WAITING_FOR_SIGNAL;
|
||||
OnSignalAlgoParams algo_params;
|
||||
PImpl(){
|
||||
}
|
||||
};
|
||||
|
||||
ChildTemplate* _get_template (IBeforeLaunchAlgoServices& serv,
|
||||
const CustomOrderDefinition& cod,
|
||||
SecurityKey key,
|
||||
api::Side side){
|
||||
OrderDefinition rv = serv.definition();
|
||||
rv.set_security_key(key.value());
|
||||
rv.set_side(side);
|
||||
rv.mutable_custom_def()->CopyFrom(cod);
|
||||
return &(serv.make_child_template(rv));
|
||||
}
|
||||
|
||||
bool _convert_params (IBeforeLaunchAlgoServices& serv,
|
||||
const CustomOrderDefinition& cod,
|
||||
OnSignalAlgoParams& out_p){
|
||||
const DynamicOrderDefinition& def = cod.dynamic();
|
||||
const auto& params = def.order_params();
|
||||
auto hh = get_if<uint32_t>(params, "Time Hours");
|
||||
auto mm = get_if<uint32_t>(params, "Time Minutes");
|
||||
auto ss = get_if<uint32_t>(params, "Time Seconds");
|
||||
if(hh.has_value() && hh.value() <= 23 &&
|
||||
mm.has_value() && mm.value() <= 59 &&
|
||||
ss.has_value() && ss.value() <= 59)
|
||||
out_p.time = SODPosition::today_local_time_to_utc_timestamp (
|
||||
hh.value(),mm.value(),ss.value(),true,false);
|
||||
auto preopen = get_if<bool>(params, "Preopen");
|
||||
out_p.preopen = preopen.has_value() ? preopen.value() : false;
|
||||
auto open = get_if<bool>(params, "Open");
|
||||
out_p.open = open.has_value() ? open.value() : false;
|
||||
auto k1 = get_if<uint32_t>(params, "Signal Contract 1");
|
||||
if(k1.has_value())
|
||||
out_p.signal_key_1 = SecurityKey(k1.value());
|
||||
for (int i=0;;i++){
|
||||
auto idx = std::to_string(i+1);
|
||||
auto contract = get_if<uint32_t>(params, "Order " + idx + " Contract");
|
||||
auto custom_ord = get_if<CustomOrderDefinition>(params, "Order " + idx);
|
||||
auto price = get_if<double>(params, "Order " + idx + " Price");
|
||||
auto qty = get_if<int32_t>(params, "Order " + idx + " Quantity");
|
||||
if(contract.has_value() && custom_ord.has_value() && price.has_value() &&
|
||||
qty.has_value() && qty.value() != 0){
|
||||
auto side = qty > 0 ? api::Side::SIDE_BUY : api::Side::SIDE_SELL;
|
||||
uint32_t eff_qty = std::abs(qty.value());
|
||||
auto key = SecurityKey(contract.value());
|
||||
ChildTemplate* child_template = _get_template(serv,custom_ord.value(),key,side);
|
||||
OnSignalLegData leg =
|
||||
{
|
||||
key,
|
||||
child_template,
|
||||
Price(price.value()),
|
||||
Quantity(eff_qty),
|
||||
side,
|
||||
OrderKey(0),
|
||||
ChildState()
|
||||
};
|
||||
out_p.legs.push_back(leg);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
bool valid = out_p.legs.size() > 0 &&
|
||||
(out_p.time.has_value() ||
|
||||
(out_p.signal_key_1.has_value() && (out_p.preopen || out_p.open)));
|
||||
return valid;
|
||||
}
|
||||
|
||||
OnSignalAlgo::OnSignalAlgo() : me (new PImpl()){}
|
||||
OnSignalAlgo::~OnSignalAlgo() {}
|
||||
|
||||
OrderOpResult OnSignalAlgo::do_before_launch(IBeforeLaunchAlgoServices& serv){
|
||||
auto& def = serv.definition();
|
||||
if (def.has_custom_def() &&
|
||||
def.custom_def().has_dynamic() &&
|
||||
_convert_params(serv,def.custom_def(),me->algo_params))
|
||||
return {};
|
||||
return {OrderError::ORDER_ERROR_INVALID_PARAMETER,
|
||||
std::string("Parameters are not well formed")};
|
||||
}
|
||||
void OnSignalAlgo::do_launch(IAlgoServices& algo_services){
|
||||
me->algo_services = &algo_services;
|
||||
me->algo_services->subscribe_timer();
|
||||
if(me->algo_params.signal_key_1.has_value())
|
||||
me->algo_services->subscribe_status(me->algo_params.signal_key_1.value());
|
||||
}
|
||||
void OnSignalAlgo::do_launch_attached(IAlgoServices& algo_services,
|
||||
OrderKey,
|
||||
ChildState){
|
||||
me->algo_services = &algo_services;
|
||||
_cxl_and_end(true, "OnSignal Algo can not be attached to working orders");
|
||||
}
|
||||
void OnSignalAlgo::do_update(api::orderman::Update const &){
|
||||
LOG(OrderMan, Warn, "OnSignalAlgo::do_update order:{} state:{} update not allowed, please cxl and re-enter ",
|
||||
me->algo_services->order_key().value(),
|
||||
static_cast<int>(me->state));
|
||||
}
|
||||
void OnSignalAlgo::on_child_change(OrderKey key, const ChildState& st){
|
||||
LOG(OrderMan, Info,
|
||||
"OnSignalAlgo::on_child_change key:{} child_key:{} state:{} status:{} "
|
||||
"total_fills:{} last_price:{}",
|
||||
me->algo_services->order_key(),
|
||||
key,
|
||||
static_cast<int>(me->state),
|
||||
static_cast<int>(st.status),
|
||||
st.total_fills,
|
||||
st.last_price);
|
||||
switch (me->state){
|
||||
case (OnSignalAlgo::State::WAITING_FOR_SIGNAL):
|
||||
LOG(OrderMan, Error,
|
||||
"OnSignalAlgo::on_child_change key:{} in WAITING_FOR_SIGNAL",
|
||||
me->algo_services->order_key());
|
||||
break;
|
||||
case (OnSignalAlgo::State::WORKING):
|
||||
{
|
||||
auto ld_ptr = me->algo_params.find_leg_from_ord_key(key);
|
||||
if (ld_ptr != nullptr)
|
||||
ld_ptr->order_state = st;
|
||||
}
|
||||
for (const auto& l : me->algo_params.legs)
|
||||
if(l.order_state.status == ChildStatus::WORKING)
|
||||
return;
|
||||
_cxl_and_end(false,"");
|
||||
break;
|
||||
case (OnSignalAlgo::State::DONE):
|
||||
break;
|
||||
}
|
||||
}
|
||||
void OnSignalAlgo::on_status(SecurityKey key, co2::TradingStatus s){
|
||||
if(me->algo_params.signal_key_1.has_value() && me->algo_params.signal_key_1 == key)
|
||||
switch (me->state){
|
||||
case (OnSignalAlgo::State::WAITING_FOR_SIGNAL):
|
||||
if(me->algo_params.preopen && s == co2::TradingStatus::TRADINGSTATUS_PRE_OPEN)
|
||||
_fire_orders();
|
||||
else if(me->algo_params.open && s == co2::TradingStatus::TRADINGSTATUS_NORMAL)
|
||||
_fire_orders();
|
||||
break;
|
||||
case (OnSignalAlgo::State::WORKING):
|
||||
case (OnSignalAlgo::State::DONE):
|
||||
break;
|
||||
}
|
||||
}
|
||||
void OnSignalAlgo::on_timer(){
|
||||
if(me->algo_params.time.has_value())
|
||||
switch (me->state){
|
||||
case (OnSignalAlgo::State::WAITING_FOR_SIGNAL):
|
||||
{
|
||||
auto thresh = me->algo_params.time.value();
|
||||
auto now = util::TimeUtil::GetCurrentTime();
|
||||
auto diff = util::TimeUtil::TimestampToMicroseconds(now) -
|
||||
util::TimeUtil::TimestampToMicroseconds(thresh);
|
||||
if(diff >= 0)
|
||||
_fire_orders();
|
||||
break;
|
||||
}
|
||||
case (OnSignalAlgo::State::WORKING):
|
||||
case (OnSignalAlgo::State::DONE):
|
||||
break;
|
||||
}
|
||||
}
|
||||
void OnSignalAlgo::_fire_orders(){
|
||||
for (auto& l : me->algo_params.legs)
|
||||
l.order_key = me->algo_services->launch_child(*l.order_template,l.price,l.quantity);
|
||||
_switch_state(State::WORKING);
|
||||
}
|
||||
void OnSignalAlgo::_cxl_and_end (bool is_error, const std::string& err_msg){
|
||||
if(is_error)
|
||||
me->algo_services->terminate_error(err_msg);
|
||||
else
|
||||
me->algo_services->complete();
|
||||
_switch_state(OnSignalAlgo::State::DONE);
|
||||
}
|
||||
void OnSignalAlgo::_switch_state (State new_state){
|
||||
switch (new_state){
|
||||
case (OnSignalAlgo::State::WAITING_FOR_SIGNAL):
|
||||
bts_assert(false);
|
||||
break;
|
||||
case (OnSignalAlgo::State::WORKING):
|
||||
bts_assert(me->state == OnSignalAlgo::State::WAITING_FOR_SIGNAL);
|
||||
break;
|
||||
case (OnSignalAlgo::State::DONE):
|
||||
bts_assert(me->state != OnSignalAlgo::State::DONE);
|
||||
break;
|
||||
}
|
||||
LOG(OrderMan, Info,"OnSignalAlgo::_switch_state key:{} {}->{} ",
|
||||
me->algo_services->order_key(),
|
||||
static_cast<int>(me->state),
|
||||
static_cast<int>(new_state));
|
||||
me->state = new_state;
|
||||
}
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/orderman/orders/algo/IAlgoOrder.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
class ChildTemplate;
|
||||
struct LegData;
|
||||
|
||||
class OnSignalAlgo : public IAlgoOrder {
|
||||
public:
|
||||
OnSignalAlgo();
|
||||
~OnSignalAlgo();
|
||||
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_child_change(OrderKey, ChildState const &) override;
|
||||
void on_status(SecurityKey, co2::TradingStatus) override;
|
||||
void on_timer() override;
|
||||
|
||||
private:
|
||||
enum class State;
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
private:
|
||||
IAlgoServices& _algo() const ;
|
||||
// acts on md, cognizant of the state
|
||||
void _switch_state (State new_state);
|
||||
void _fire_orders();
|
||||
void _cxl_and_end (bool is_error, const std::string& err_msg);
|
||||
};
|
||||
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -538,6 +538,14 @@ bool PyramidAlgo::_md_recvd() const{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gr_than_eq(double p1, double p2){
|
||||
return securities::test_price_equality(p1,p2) || p1 > p2;
|
||||
}
|
||||
|
||||
bool less_than_eq(double p1, double p2){
|
||||
return securities::test_price_equality(p1,p2) || p1 < p2;
|
||||
}
|
||||
|
||||
bool PyramidAlgo::_working_state_met() const{
|
||||
double cover_sum = 0.0;
|
||||
size_t i = 0;
|
||||
@@ -545,10 +553,17 @@ bool PyramidAlgo::_working_state_met() const{
|
||||
if (i++ > 0)
|
||||
cover_sum += l.opp_md()[0].price();
|
||||
bool rv = !os::is_nan(cover_sum) &&
|
||||
((me->side == api::Side::SIDE_BUY &&
|
||||
me->out_price.value() <= cover_sum) ||
|
||||
(me->side == api::Side::SIDE_SELL &&
|
||||
me->out_price.value() >= cover_sum));
|
||||
(
|
||||
(
|
||||
me->side == api::Side::SIDE_BUY &&
|
||||
less_than_eq(me->out_price.value(),cover_sum)
|
||||
)
|
||||
||
|
||||
(
|
||||
me->side == api::Side::SIDE_SELL &&
|
||||
gr_than_eq(me->out_price.value(),cover_sum)
|
||||
)
|
||||
);
|
||||
if (!rv && !os::is_nan(cover_sum)){
|
||||
// price condition is not met, but if the
|
||||
// cum qty at the top leg price is > threashold
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "PyramidAlgo.h"
|
||||
#include "TASAlgo.h"
|
||||
#include "TASCounterAlgo.h"
|
||||
#include "OnSignalAlgo.h"
|
||||
|
||||
using namespace bts;
|
||||
using namespace api::orderman;
|
||||
@@ -200,7 +201,7 @@ extern "C" void init_tas_plugin(bts::SparkServices& srv) {
|
||||
auto idx = std::to_string(i+1);
|
||||
*tas_desc.add_param_desc() = param_double("Order " + idx + " Price");
|
||||
*tas_desc.add_param_desc() = param_uint32("Order " + idx + " Quantity");
|
||||
*tas_desc.add_param_desc() = param_bool("Order " + idx + " Buy?");
|
||||
*tas_desc.add_param_desc() = param_bool("Order " + idx + " Buy?");
|
||||
}
|
||||
srv.register_dynamic_algo_order(tas_desc, []() { return std::make_unique<orderman::TASAlgo>(); });
|
||||
}
|
||||
@@ -212,6 +213,27 @@ extern "C" void init_limit_cover(bts::SparkServices & srv) {
|
||||
srv.register_dynamic_algo_order(lc_desc, []() { return std::make_unique<orderman::LimitCoverAlgo>(); });
|
||||
}
|
||||
|
||||
extern "C" void init_on_signal_plugin(bts::SparkServices& srv) {
|
||||
DynamicOrderDescriptor on_signal;
|
||||
on_signal.set_dynamic_order_type("OnSignal");
|
||||
*on_signal.add_param_desc() = param_uint32("Time Hours");
|
||||
*on_signal.add_param_desc() = param_uint32("Time Minutes");
|
||||
*on_signal.add_param_desc() = param_uint32("Time Seconds");
|
||||
*on_signal.add_param_desc() = param_bool("Preopen");
|
||||
*on_signal.add_param_desc() = param_bool("Open");
|
||||
// contracts who's signals have to be received to send the algo
|
||||
*on_signal.add_param_desc() = param_security_key("Signal Contract 1");
|
||||
size_t n_child_ords = 10;
|
||||
for (size_t i=0; i< n_child_ords; i++){
|
||||
auto idx = std::to_string(i+1);
|
||||
*on_signal.add_param_desc() = param_security_key("Order " + idx + " Contract");
|
||||
*on_signal.add_param_desc() = param_order("Order " + idx);
|
||||
*on_signal.add_param_desc() = param_double("Order " + idx + " Price");
|
||||
*on_signal.add_param_desc() = param_int32("Order " + idx + " Quantity");
|
||||
}
|
||||
srv.register_dynamic_algo_order(on_signal, []() { return std::make_unique<orderman::OnSignalAlgo>(); });
|
||||
}
|
||||
|
||||
extern "C" void init_spark_plugin(bts::SparkServices &srv) {
|
||||
auto& dyn_algo_config = config::get<config::toggles::DynamicAlgos>();
|
||||
if(dyn_algo_config.enable_dorman())
|
||||
@@ -224,6 +246,8 @@ extern "C" void init_spark_plugin(bts::SparkServices &srv) {
|
||||
init_tascounter_plugin(srv);
|
||||
if (dyn_algo_config.enable_limit_cover())
|
||||
init_limit_cover(srv);
|
||||
if (dyn_algo_config.enable_onsignal())
|
||||
init_on_signal_plugin(srv);
|
||||
}
|
||||
|
||||
}}}
|
||||
Reference in new issue
Block a user