|
|
|
@@ -23,10 +23,17 @@
|
|
|
|
|
#include "lib/utils/bts_assert.h"
|
|
|
|
|
#include <bits/stdint-uintn.h>
|
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
|
#include <chrono>
|
|
|
|
|
#include <elf.h>
|
|
|
|
|
#include <filesystem>
|
|
|
|
|
#include <google/protobuf/timestamp.pb.h>
|
|
|
|
|
#include <google/protobuf/util/time_util.h>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <optional>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <thread>
|
|
|
|
|
#include <tuple>
|
|
|
|
|
#include <unordered_map>
|
|
|
|
|
|
|
|
|
|
using namespace bts::api::orderman;
|
|
|
|
@@ -69,6 +76,11 @@ double eps = 0.0000001;
|
|
|
|
|
bool eq(double d1, double d2) { return std::abs(d1 - d2) < eps; }
|
|
|
|
|
bool greater_then_eq(double d1, double d2) { return eq(d1, d2) || d2 > d1; }
|
|
|
|
|
|
|
|
|
|
void sleep_log() {
|
|
|
|
|
return;
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns the number of shots that are within the Shots Window
|
|
|
|
|
size_t n_recent_shots(
|
|
|
|
|
const std::vector<TEAShot> &shots, uint64_t window_nanos) {
|
|
|
|
@@ -100,7 +112,91 @@ bool in_flight_orders(const std::vector<TEAShot> &shots) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::set<SecurityKey> parse_securities(const std::string &secs) {
|
|
|
|
|
// returns the securities that match the criteria from the 2nd - 8th arg
|
|
|
|
|
std::set<SecurityKey> match_securities(
|
|
|
|
|
const std::vector<api::Security const *> &secs,
|
|
|
|
|
api::Venue venue,
|
|
|
|
|
const std::string &symbol,
|
|
|
|
|
uint32_t exp_month,
|
|
|
|
|
uint32_t exp_day,
|
|
|
|
|
uint32_t exp_yr,
|
|
|
|
|
std::tuple<double, double> strike_range,
|
|
|
|
|
api::PutOrCall cp) {
|
|
|
|
|
std::set<SecurityKey> rv = {};
|
|
|
|
|
double strike_min = std::get<0>(strike_range);
|
|
|
|
|
double strike_max = std::get<1>(strike_range);
|
|
|
|
|
for (const auto &s : secs) {
|
|
|
|
|
if (s->exchange() == venue && s->has_option() && s->symbol() == symbol) {
|
|
|
|
|
const auto &oc = s->option();
|
|
|
|
|
if (oc.call_put() == cp && oc.strike() >= strike_min &&
|
|
|
|
|
oc.strike() <= strike_max && oc.expiration_year() == exp_yr &&
|
|
|
|
|
oc.expiration_month() == exp_month &&
|
|
|
|
|
oc.expiration_day() == exp_day)
|
|
|
|
|
rv.insert(SecurityKey(s->id()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parses the sec expressed as text in the secs string.
|
|
|
|
|
// the format is symbol:exp:call/put:strike range, ex
|
|
|
|
|
// BTC:08/14/2023:Call:25000-30000
|
|
|
|
|
std::set<SecurityKey> parse_securities_from_range(
|
|
|
|
|
const std::string &secs,
|
|
|
|
|
const std::vector<api::Security const *> &sec_vec) {
|
|
|
|
|
LOG(OrderMan, Info,
|
|
|
|
|
"TaskingEyeAlgo::parse_securities_from_range secs: {} sec_vec: {}", secs,
|
|
|
|
|
sec_vec.size());
|
|
|
|
|
sleep_log();
|
|
|
|
|
if (secs.empty())
|
|
|
|
|
return {};
|
|
|
|
|
auto parse_exp = [](const std::string &exp_str) {
|
|
|
|
|
LOG(OrderMan, Info,
|
|
|
|
|
"TaskingEyeAlgo::parse_securities_from_range parse_exp exp: {}",
|
|
|
|
|
exp_str);
|
|
|
|
|
std::vector<std::string> exp_parts;
|
|
|
|
|
boost::split(exp_parts, exp_str, boost::is_any_of("/"));
|
|
|
|
|
LOG(OrderMan, Info,
|
|
|
|
|
"TaskingEyeAlgo::parse_securities_from_range exp_parts: {}",
|
|
|
|
|
exp_parts.size());
|
|
|
|
|
uint32_t mm = static_cast<std::uint32_t>(std::stoul(exp_parts[0]));
|
|
|
|
|
uint32_t dd = static_cast<std::uint32_t>(std::stoul(exp_parts[1]));
|
|
|
|
|
uint32_t yy = static_cast<std::uint32_t>(std::stoul(exp_parts[2]));
|
|
|
|
|
LOG(OrderMan, Info,
|
|
|
|
|
"TaskingEyeAlgo::parse_securities_from_range {} mm: {} dd: {} yy: {}",
|
|
|
|
|
mm, dd, yy);
|
|
|
|
|
return std::tuple<uint32_t, uint32_t, uint32_t>(mm, dd, yy);
|
|
|
|
|
};
|
|
|
|
|
auto parse_strike_range = [](const std::string &strike_range_str) {
|
|
|
|
|
std::vector<std::string> strike_parts;
|
|
|
|
|
boost::split(strike_parts, strike_range_str, boost::is_any_of("-"));
|
|
|
|
|
double k_min = std::stod(strike_parts[0]);
|
|
|
|
|
double k_max = std::stod(strike_parts[1]);
|
|
|
|
|
return std::tuple<double, double>(k_min, k_max);
|
|
|
|
|
};
|
|
|
|
|
std::vector<std::string> parts;
|
|
|
|
|
boost::split(parts, secs, boost::is_any_of(":"));
|
|
|
|
|
const std::string &symbol = parts[0];
|
|
|
|
|
const std::string &exp_str = parts[1];
|
|
|
|
|
const std::string &cp_str = parts[2];
|
|
|
|
|
const auto &strike_range_str = parts[3];
|
|
|
|
|
LOG(OrderMan, Info,
|
|
|
|
|
"TaskingEyeAlgo::parse_securities_from_range symbol: {} exp: {} cp: {} "
|
|
|
|
|
"stike_range: {}",
|
|
|
|
|
symbol, exp_str, cp_str, strike_range_str);
|
|
|
|
|
// debugging
|
|
|
|
|
sleep_log();
|
|
|
|
|
auto [mm, dd, yy] = parse_exp(exp_str);
|
|
|
|
|
auto strike_range = parse_strike_range(strike_range_str);
|
|
|
|
|
api::PutOrCall cp = (cp_str[0] == 'C' || cp_str[0] == 'c')
|
|
|
|
|
? api::PutOrCall::PUTORCALL_CALL
|
|
|
|
|
: api::PutOrCall::PUTORCALL_PUT;
|
|
|
|
|
|
|
|
|
|
return match_securities(
|
|
|
|
|
sec_vec, api::Venue::VENUE_DERIBIT, symbol, mm, dd, yy, strike_range,
|
|
|
|
|
cp);
|
|
|
|
|
}
|
|
|
|
|
std::set<SecurityKey> parse_securities_from_keys(const std::string &secs) {
|
|
|
|
|
std::set<SecurityKey> rv = {};
|
|
|
|
|
std::vector<std::string> parts;
|
|
|
|
|
boost::split(parts, secs, boost::is_any_of(","));
|
|
|
|
@@ -113,10 +209,34 @@ std::set<SecurityKey> parse_securities(const std::string &secs) {
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::set<SecurityKey> parse_securities(
|
|
|
|
|
const std::string &secs,
|
|
|
|
|
const std::vector<api::Security const *> &sec_vec) {
|
|
|
|
|
bts_assert(!secs.empty());
|
|
|
|
|
LOG(OrderMan, Info, "TaskingEyeAlgo::parse_securities sec: {}", secs);
|
|
|
|
|
sleep_log();
|
|
|
|
|
std::set<SecurityKey> rv = {};
|
|
|
|
|
if (secs.find("/") != std::string::npos &&
|
|
|
|
|
secs.find(":") != std::string::npos) {
|
|
|
|
|
rv = parse_securities_from_range(secs, sec_vec);
|
|
|
|
|
} else {
|
|
|
|
|
rv = parse_securities_from_keys(secs);
|
|
|
|
|
}
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
for (auto k : rv) {
|
|
|
|
|
ss << k << " ";
|
|
|
|
|
}
|
|
|
|
|
LOG(OrderMan, Info, "TaskingEyeAlgo::parse_securities sec: {} rv: {}", secs,
|
|
|
|
|
ss.str());
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct TaskingEyeAlgo ::PImpl {
|
|
|
|
|
IAlgoServices *service_ptr = nullptr;
|
|
|
|
|
analytics::ITheoCalculator const *theo_calc = nullptr;
|
|
|
|
|
TEAState state = TEAState::ACTIVE;
|
|
|
|
|
bool wait_for_signal = false;
|
|
|
|
|
std::string signal_file = "";
|
|
|
|
|
double edge = 100000;
|
|
|
|
|
double shot_size = 0;
|
|
|
|
|
double target_qty = 0;
|
|
|
|
@@ -150,31 +270,62 @@ TaskingEyeAlgo::~TaskingEyeAlgo() {}
|
|
|
|
|
OrderOpResult TaskingEyeAlgo::do_before_launch(
|
|
|
|
|
IBeforeLaunchAlgoServices &serv) {
|
|
|
|
|
auto is_data_valid = [&]() {
|
|
|
|
|
return greater_then_eq(0.1, me->shot_size) &&
|
|
|
|
|
greater_then_eq(1, me->target_qty) &&
|
|
|
|
|
greater_then_eq(1, me->max_shots_per_window) &&
|
|
|
|
|
greater_then_eq(1000000000, me->window_size_nanos) &&
|
|
|
|
|
me->side != api::SIDE_UNDEFINED;
|
|
|
|
|
bool rv = greater_then_eq(0.1, me->shot_size) &&
|
|
|
|
|
greater_then_eq(1, me->target_qty) &&
|
|
|
|
|
greater_then_eq(1, me->max_shots_per_window) &&
|
|
|
|
|
greater_then_eq(1000000000, me->window_size_nanos) &&
|
|
|
|
|
me->side != api::SIDE_UNDEFINED;
|
|
|
|
|
bool signal_correct = !me->wait_for_signal || !me->signal_file.empty();
|
|
|
|
|
rv = rv && signal_correct;
|
|
|
|
|
return rv;
|
|
|
|
|
};
|
|
|
|
|
LOG(OrderMan, Info,
|
|
|
|
|
"TaskingEyeAlgo::do_before_launch order_key: {} security: {} "
|
|
|
|
|
"definition: {}",
|
|
|
|
|
serv.order_key().value(), utils::pb::to_string(serv.security()),
|
|
|
|
|
utils::pb::to_string(serv.definition()));
|
|
|
|
|
sleep_log();
|
|
|
|
|
auto &def = serv.definition();
|
|
|
|
|
if (def.has_custom_def() && def.custom_def().has_dynamic()) {
|
|
|
|
|
const auto &d_params = def.custom_def().dynamic().order_params();
|
|
|
|
|
auto wait_for_signal = get_if<bool>(d_params, "Wait For Signal");
|
|
|
|
|
me->wait_for_signal =
|
|
|
|
|
wait_for_signal.has_value() ? wait_for_signal.value() : false;
|
|
|
|
|
auto signal_file = get_if<std::string>(d_params, "Signal File");
|
|
|
|
|
me->signal_file = signal_file.has_value() ? signal_file.value() : "";
|
|
|
|
|
me->edge = get_if<double>(d_params, "Edge").value();
|
|
|
|
|
LOG(OrderMan, Info, "TaskingEyeAlgo::do_before_launch edge: {}",
|
|
|
|
|
me->edge);
|
|
|
|
|
sleep_log();
|
|
|
|
|
me->shot_size = get_if<double>(d_params, "Shot Size").value();
|
|
|
|
|
LOG(OrderMan, Info, "TaskingEyeAlgo::do_before_launch shot_size: {}",
|
|
|
|
|
me->shot_size);
|
|
|
|
|
sleep_log();
|
|
|
|
|
me->target_qty = get_if<double>(d_params, "Target Quantity").value();
|
|
|
|
|
LOG(OrderMan, Info, "TaskingEyeAlgo::do_before_launch target_qty: {}",
|
|
|
|
|
me->target_qty);
|
|
|
|
|
sleep_log();
|
|
|
|
|
// TODO, make sure the side if right
|
|
|
|
|
me->side = def.side();
|
|
|
|
|
me->max_shots_per_window =
|
|
|
|
|
get_if<uint32_t>(d_params, "Max Shots Per Window").value();
|
|
|
|
|
LOG(OrderMan, Info,
|
|
|
|
|
"TaskingEyeAlgo::do_before_launch max_shots_per_window: {}",
|
|
|
|
|
me->max_shots_per_window);
|
|
|
|
|
sleep_log();
|
|
|
|
|
uint32_t window_size_secs =
|
|
|
|
|
get_if<uint32_t>(d_params, "Window Size (secs)").value();
|
|
|
|
|
LOG(OrderMan, Info,
|
|
|
|
|
"TaskingEyeAlgo::do_before_launch window_size_secs: {}",
|
|
|
|
|
window_size_secs);
|
|
|
|
|
sleep_log();
|
|
|
|
|
me->window_size_nanos = seconds_to_nanos(window_size_secs);
|
|
|
|
|
auto sec_str = get_if<std::string>(d_params, "Securities").value();
|
|
|
|
|
auto parsed_secs = parse_securities(sec_str);
|
|
|
|
|
LOG(OrderMan, Info, "TaskingEyeAlgo::do_before_launch Securities: {}",
|
|
|
|
|
sec_str);
|
|
|
|
|
sleep_log();
|
|
|
|
|
auto parsed_secs = parse_securities(sec_str, serv.known_securities());
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
std::vector<SecurityKey> final_secs;
|
|
|
|
|
if (parsed_secs.empty()) {
|
|
|
|
|
final_secs.push_back(SecurityKey{serv.security().id()});
|
|
|
|
@@ -186,18 +337,21 @@ OrderOpResult TaskingEyeAlgo::do_before_launch(
|
|
|
|
|
auto ct = get_tea_child_template(serv, sec_key, me->side);
|
|
|
|
|
me->sec_map.insert({sec_key, {ct}});
|
|
|
|
|
LOG(OrderMan, Info,
|
|
|
|
|
"TaskingEyeAlgo::do_before_launch order_key: {} successfully "
|
|
|
|
|
"TaskingEyeAlgo::do_before_launch order_key: {} "
|
|
|
|
|
"successfully "
|
|
|
|
|
"created ChildTemplate for sec_key: {}",
|
|
|
|
|
serv.order_key(), sec_key);
|
|
|
|
|
}
|
|
|
|
|
LOG(OrderMan, Info,
|
|
|
|
|
"TaskingEyeAlgo::do_before_launch algo constructed order_key: {} "
|
|
|
|
|
"edge: {} shot_size: {} is_buy: {} target_qty: {} "
|
|
|
|
|
"max_shots_per_window: {} window_size_nanos: {} n_secs: {}",
|
|
|
|
|
"TaskingEyeAlgo::do_before_launch algo constructed order_key: "
|
|
|
|
|
"{} "
|
|
|
|
|
"edge: {} shot_size: {} side: {} target_qty: {} "
|
|
|
|
|
"max_shots_per_window: {} window_size_nanos: {} n_secs: {} "
|
|
|
|
|
"wait_for_signal: {} signal_file: {}",
|
|
|
|
|
serv.order_key(), me->edge, me->shot_size,
|
|
|
|
|
me->side == api::SIDE_BUY, me->target_qty,
|
|
|
|
|
bts::api::Side_Name(me->side), me->target_qty,
|
|
|
|
|
me->max_shots_per_window, me->window_size_nanos,
|
|
|
|
|
me->sec_map.size());
|
|
|
|
|
me->sec_map.size(), me->wait_for_signal, me->signal_file);
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -213,7 +367,8 @@ void TaskingEyeAlgo::do_launch(IAlgoServices &serv) {
|
|
|
|
|
for (const auto &kv : me->sec_map) {
|
|
|
|
|
auto sec_key = kv.first;
|
|
|
|
|
LOG(OrderMan, Info,
|
|
|
|
|
"TaskingEyeAlgo::do_before_launch subscribing book for sec_key: {}",
|
|
|
|
|
"TaskingEyeAlgo::do_before_launch subscribing book for sec_key: "
|
|
|
|
|
"{}",
|
|
|
|
|
sec_key);
|
|
|
|
|
me->service_ptr->subscribe_book(sec_key);
|
|
|
|
|
}
|
|
|
|
@@ -315,9 +470,9 @@ void TaskingEyeAlgo::on_theos(
|
|
|
|
|
if (!sec_data.driver_sec_key.has_value() ||
|
|
|
|
|
sec_data.driver_sec_key->value() != driver.value()) {
|
|
|
|
|
LOG(OrderMan, Info,
|
|
|
|
|
"TaskingEyeAlgo::on_theos order_key: {} subscribing to driver "
|
|
|
|
|
"sec_key: {}",
|
|
|
|
|
me->service_ptr->order_key(), driver.value());
|
|
|
|
|
"TaskingEyeAlgo::on_theos order_key: {} subscribing to driver for "
|
|
|
|
|
"sec_key: {} driver_key: {}",
|
|
|
|
|
me->service_ptr->order_key(), sec_key, driver.value());
|
|
|
|
|
sec_data.driver_sec_key = SecurityKey(driver.value());
|
|
|
|
|
sub_to_driver(SecurityKey(driver.value()));
|
|
|
|
|
}
|
|
|
|
@@ -331,8 +486,14 @@ void TaskingEyeAlgo::on_position(PositionUpdate const &pu) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TaskingEyeAlgo::_evaluate_and_shoot() {
|
|
|
|
|
if (me->state != TEAState::ACTIVE)
|
|
|
|
|
if (me->state != TEAState::ACTIVE) {
|
|
|
|
|
LOG(OrderMan, Warn,
|
|
|
|
|
"TaskingEyeAlgo::_evaluate_and_shoot order_key: {} state is not "
|
|
|
|
|
"active state: {} ",
|
|
|
|
|
me->service_ptr->order_key(), me->state);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto max_shots_per_win_reached = [&]() {
|
|
|
|
|
return n_recent_shots(me->shots, me->window_size_nanos) >=
|
|
|
|
|
me->max_shots_per_window;
|
|
|
|
@@ -343,12 +504,36 @@ void TaskingEyeAlgo::_evaluate_and_shoot() {
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
auto signal_condition_met = [&]() {
|
|
|
|
|
if (me->wait_for_signal) {
|
|
|
|
|
return std::filesystem::exists(me->signal_file);
|
|
|
|
|
} else
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
if (!signal_condition_met()) {
|
|
|
|
|
// Log ?
|
|
|
|
|
LOG(OrderMan, Warn,
|
|
|
|
|
"TaskingEyeAlgo::_evaluate_and_shoot order_key: {} Waiting for "
|
|
|
|
|
"Signal",
|
|
|
|
|
me->service_ptr->order_key());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (auto &kv : me->sec_map) {
|
|
|
|
|
auto sec_key = kv.first;
|
|
|
|
|
if (max_shots_per_win_reached())
|
|
|
|
|
if (max_shots_per_win_reached()) {
|
|
|
|
|
LOG(OrderMan, Warn,
|
|
|
|
|
"TaskingEyeAlgo::_evaluate_and_shoot order_key: {} "
|
|
|
|
|
"max_shots_per_win_reached",
|
|
|
|
|
me->service_ptr->order_key());
|
|
|
|
|
return;
|
|
|
|
|
if (sec_has_active_order(sec_key))
|
|
|
|
|
}
|
|
|
|
|
if (sec_has_active_order(sec_key)) {
|
|
|
|
|
LOG(OrderMan, Warn,
|
|
|
|
|
"TaskingEyeAlgo::_evaluate_and_shoot sec_key: {} has working "
|
|
|
|
|
"order, skipping",
|
|
|
|
|
sec_key.value());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
Price price;
|
|
|
|
|
Quantity qty;
|
|
|
|
|
if (_in_shooting_zone(sec_key, price, qty)) {
|
|
|
|
@@ -357,7 +542,8 @@ void TaskingEyeAlgo::_evaluate_and_shoot() {
|
|
|
|
|
auto ch_ord_key =
|
|
|
|
|
me->service_ptr->launch_child(*sec_data.order, price, qty);
|
|
|
|
|
LOG(OrderMan, Info,
|
|
|
|
|
"TakingEyeAlgo::_evaluate_and_shoot ord_key: {} child_ord_key: {} "
|
|
|
|
|
"TakingEyeAlgo::_evaluate_and_shoot ord_key: {} child_ord_key: "
|
|
|
|
|
"{} "
|
|
|
|
|
"price: {} qty: {}",
|
|
|
|
|
me->service_ptr->order_key(), ch_ord_key, price, qty.full_value());
|
|
|
|
|
me->shots.push_back(TEAShot{sec_key, ch_ord_key});
|
|
|
|
@@ -375,8 +561,13 @@ bool TaskingEyeAlgo::_in_shooting_zone(
|
|
|
|
|
TEASecData const &sec_data = it->second;
|
|
|
|
|
const md::PriceSize &driver_bid = sec_data.driver_book.bid().at(0);
|
|
|
|
|
const md::PriceSize &driver_ask = sec_data.driver_book.ask().at(0);
|
|
|
|
|
if (eq(driver_bid.size(), 0) || eq(driver_ask.size(), 0))
|
|
|
|
|
if (eq(driver_bid.size(), 0) || eq(driver_ask.size(), 0)) {
|
|
|
|
|
LOG(OrderMan, Warn,
|
|
|
|
|
"TaskingEyeAlgo::_in_shooting_zone no driver for sec_key: {} "
|
|
|
|
|
"driver_bid_qty: {} driver_ask_qty: {}",
|
|
|
|
|
sec_key, driver_bid.size(), driver_ask.size());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// finding weighted mean (TODO change it to mark price later)
|
|
|
|
|
double driver = (driver_bid.price() * driver_bid.size() +
|
|
|
|
|
driver_ask.price() * driver_ask.size()) /
|
|
|
|
@@ -386,31 +577,55 @@ bool TaskingEyeAlgo::_in_shooting_zone(
|
|
|
|
|
// TODO normalize theo
|
|
|
|
|
double theo = ptp.theo();
|
|
|
|
|
theo = theo / driver;
|
|
|
|
|
if (me->side == api::SIDE_BUY) {
|
|
|
|
|
// auto analyze_buy =
|
|
|
|
|
auto analyze_buy = [&](const md::PriceSize &md_ask, double theo,
|
|
|
|
|
size_t level, Price &price, Quantity &qty) -> bool {
|
|
|
|
|
double ee_buy_pr = theo - me->edge;
|
|
|
|
|
const md::PriceSize &md_ask = sec_data.sec_book.ask().at(0);
|
|
|
|
|
LOG(OrderMan, Diag,
|
|
|
|
|
"TaskingEyeAlgo::_in_shooting_zone sec_key: {} driver: {} theo: {} "
|
|
|
|
|
"ee_price: {} md: {}/{}",
|
|
|
|
|
sec_key, driver, theo, ee_buy_pr, md_ask.price(), md_ask.size());
|
|
|
|
|
bool rv = false;
|
|
|
|
|
if (greater_then_eq(1, md_ask.size()) &&
|
|
|
|
|
greater_then_eq(md_ask.price(), ee_buy_pr)) {
|
|
|
|
|
price = Price(md_ask.price());
|
|
|
|
|
qty = std::min(static_cast<double>(md_ask.size()), me->shot_size);
|
|
|
|
|
return true;
|
|
|
|
|
qty = me->shot_size;
|
|
|
|
|
rv = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
double ee_sell_pr = theo + me->edge;
|
|
|
|
|
const md::PriceSize &md_bid = sec_data.sec_book.bid().at(0);
|
|
|
|
|
LOG(OrderMan, Diag,
|
|
|
|
|
"TaskingEyeAlgo::_in_shooting_zone sec_key: {} driver: {} theo: {} "
|
|
|
|
|
"ee_price: {} md: {}/{}",
|
|
|
|
|
sec_key, driver, theo, ee_sell_pr, md_bid.price(), md_bid.size());
|
|
|
|
|
"TaskingEyeAlgo::_in_shooting_zone::analyze_buy sec_key: {} "
|
|
|
|
|
"theo: {} level: {}"
|
|
|
|
|
"ee_buy_pr: {} rv: {}",
|
|
|
|
|
sec_key, level, theo, ee_buy_pr, rv);
|
|
|
|
|
return rv;
|
|
|
|
|
};
|
|
|
|
|
auto analyze_sell = [&](const md::PriceSize &md_bid, double theo,
|
|
|
|
|
size_t level, Price &price, Quantity &qty) -> bool {
|
|
|
|
|
double ee_sell_pr = theo + me->edge;
|
|
|
|
|
bool rv = false;
|
|
|
|
|
if (greater_then_eq(1, md_bid.size()) &&
|
|
|
|
|
greater_then_eq(ee_sell_pr, md_bid.price())) {
|
|
|
|
|
price = Price(md_bid.price());
|
|
|
|
|
qty = std::min(static_cast<double>(md_bid.size()), me->shot_size);
|
|
|
|
|
return true;
|
|
|
|
|
qty = me->shot_size;
|
|
|
|
|
rv = true;
|
|
|
|
|
}
|
|
|
|
|
LOG(OrderMan, Diag,
|
|
|
|
|
"TaskingEyeAlgo::_in_shooting_zone::analyze_sell sec_key: {} theo:"
|
|
|
|
|
"{} level: {} ee_sell_pr: {} rv: {}",
|
|
|
|
|
sec_key, theo, level, ee_sell_pr, rv);
|
|
|
|
|
return rv;
|
|
|
|
|
};
|
|
|
|
|
if (me->side == api::SIDE_BUY) {
|
|
|
|
|
const auto &ask_book = sec_data.sec_book.ask();
|
|
|
|
|
for (int i = ask_book.size() - 1; i >= 0; i--) {
|
|
|
|
|
const auto &md_ask = ask_book.at(i);
|
|
|
|
|
if (analyze_buy(md_ask, theo, i, price, qty)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const auto &bid_book = sec_data.sec_book.bid();
|
|
|
|
|
for (int i = bid_book.size() - 1; i >= 0; i--) {
|
|
|
|
|
const md::PriceSize &md_bid = bid_book.at(i);
|
|
|
|
|
if (analyze_sell(md_bid, theo, i, price, qty)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|