Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc053f2aea | ||
|
|
f95a1df158 |
No files matched your search
@@ -519,7 +519,6 @@ message TriggerPayupPrice{
|
||||
LAST_TRADE_PRICE = 2; //unused
|
||||
TRIGGER=3;
|
||||
}
|
||||
TriggerPriceType price_type = 10; // defunct
|
||||
TriggerPayupPriceType payup_price_type = 15;
|
||||
int32 payup_ticks = 20;
|
||||
}
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
#include "lib/orderman/orders/DynamicOrder.h"
|
||||
#include "lib/orderman/orders/NativeOrder.h"
|
||||
#include "lib/orderman/orders/TimeBoxOrder.h"
|
||||
#include "lib/orderman/orders/TriggerOrder.h"
|
||||
#include "lib/orderman/orders/algo/AlgoWrapperOrder.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/CoverAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/JoinAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/TopAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/IcebergAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/LeanAlgo.h"
|
||||
#include "lib/orderman/orders/algo/algo_orders/TriggerAlgo.h"
|
||||
#include "lib/orderman/orders/book_algos/OCOOrder.h"
|
||||
#include "lib/orderman/orders/book_algos/OffsetOrder.h"
|
||||
#include "lib/orderman/orders/book_algos/PricerOrder.h"
|
||||
@@ -176,7 +176,7 @@ Order &OrderFactory::allocate(OrderKey k, OrderKey pk,
|
||||
new orderman::AlgoWrapperOrder(std::make_unique<TopAlgo>()));
|
||||
break;
|
||||
case CustomOrderDefinition::kTrigger:
|
||||
order_ptr.reset(new orderman::TriggerOrder());
|
||||
order_ptr.reset( new orderman::AlgoWrapperOrder(std::make_unique<TriggerAlgo>()));
|
||||
break;
|
||||
case CustomOrderDefinition::kCover:
|
||||
order_ptr.reset(
|
||||
|
||||
@@ -1,534 +0,0 @@
|
||||
#include "TriggerOrder.h"
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/orderman/actors/order_manager/OrderBuilder.h"
|
||||
#include "lib/orderman/events/BookUpdateEvent.h"
|
||||
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
|
||||
#include "lib/os/os.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
#include "lib/orderman/orders/PreActivationOrderServices.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
struct TriggerOrder::PImpl {
|
||||
OrderServices *services;
|
||||
api::orderman::OrderDefinition order_def;
|
||||
double tick_size;
|
||||
// true if timeout_exit is enabled
|
||||
bool timeout_exit;
|
||||
uint32_t timeout_msecs;
|
||||
// true position cond exit is enabled
|
||||
bool price_cond_exit;
|
||||
api::orderman::OrderState state;
|
||||
// child state copied directly from the
|
||||
// child order udpate callback
|
||||
api::orderman::OrderState child_state;
|
||||
OrderBuilder order_builder;
|
||||
ChildOrder child_order;
|
||||
// true if child order has been placed once
|
||||
bool order_placed;
|
||||
Book book;
|
||||
CancelMode cancel_mode;
|
||||
// this tells that when the order is placed
|
||||
// initially, if the trigger price above the
|
||||
// market (trigger_condition's chosen market)
|
||||
bool trigger_above_market;
|
||||
// true is non 0 market corresponding
|
||||
// to the trigger market is obs'ed once
|
||||
bool market_observed_once;
|
||||
bool is_recovered;
|
||||
bool is_succ_inited;
|
||||
bts::TimeStamp child_order_time;
|
||||
PImpl()
|
||||
: services(nullptr), order_def(), timeout_exit(false),
|
||||
timeout_msecs(std::numeric_limits<uint32_t>::max()),
|
||||
price_cond_exit(false), state(), child_state(), child_order(),
|
||||
order_placed(false), book(), cancel_mode(CancelMode::none),
|
||||
market_observed_once(false), is_recovered(false),
|
||||
is_succ_inited(false) {}
|
||||
};
|
||||
|
||||
TriggerOrder::TriggerOrder() : _state(State::START), me(new PImpl()) {}
|
||||
TriggerOrder::~TriggerOrder() {}
|
||||
const api::orderman::OrderDefinition &TriggerOrder::definition() const {
|
||||
return me->order_def;
|
||||
}
|
||||
void TriggerOrder::on_init_new(const api::orderman::OrderDefinition &order_def,
|
||||
const PreActivationOrderServices &pre_os) {
|
||||
LOG(OrderMan, Info, "TriggerOrder::on_init_new key:{} state:{}", key(),
|
||||
_state);
|
||||
switch (_state) {
|
||||
case State::START:
|
||||
me->is_succ_inited = _on_init_new(order_def, pre_os);
|
||||
break;
|
||||
case State::WAITING_FOR_TRIGGER:
|
||||
case State::WAITING_FOR_FIRST_ACK:
|
||||
case State::STEADY:
|
||||
case State::END:
|
||||
std::ostringstream oss;
|
||||
oss << "TriggerOrder::on_init_new key:" << key() << "state:" << _state;
|
||||
bts_assert_msg(false, oss.str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
void TriggerOrder::on_init_recovered(const api::orderman::OrderRecord &rec,
|
||||
const PreActivationOrderServices &pre_os) {
|
||||
LOG(OrderMan, Info, "TriggerOrder::on_init_recovered key:{} state:{}", key(),
|
||||
_state);
|
||||
switch (_state) {
|
||||
case State::START:
|
||||
me->is_succ_inited = _on_init_new(rec.def(), pre_os);
|
||||
// get the state from what was recovered (we will only use
|
||||
// it for reporting)
|
||||
me->state = rec.state();
|
||||
// if it was not in a terminal state, we will call it cancelled
|
||||
if (!SpreaderUtils::order_in_terminal_state(me->state.status()))
|
||||
me->state.set_status(
|
||||
api::orderman::OrderStatus::ORDER_STATUS_CANCELED);
|
||||
me->is_recovered = true;
|
||||
break;
|
||||
case State::WAITING_FOR_TRIGGER:
|
||||
case State::WAITING_FOR_FIRST_ACK:
|
||||
case State::STEADY:
|
||||
case State::END:
|
||||
std::ostringstream oss;
|
||||
oss << "TriggerOrder::on_init_recovered key:" << key()
|
||||
<< "state:" << _state;
|
||||
bts_assert_msg(false, oss.str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
OrderOpResult TriggerOrder::on_activate(Price price, Quantity qty,
|
||||
OrderServices &services) {
|
||||
LOG(OrderMan, Info,
|
||||
"TriggerOrder::on_activate key:{} state:{} price:{} qty:{}", key(),
|
||||
_state, price, qty);
|
||||
switch (_state) {
|
||||
case State::START:
|
||||
me->services = &services;
|
||||
me->state.set_price(price.value());
|
||||
me->state.set_quantity(qty.value());
|
||||
if (me->is_recovered || !me->is_succ_inited)
|
||||
_switch_state(State::END);
|
||||
else {
|
||||
me->order_builder.activate(services);
|
||||
me->child_order = me->order_builder.build();
|
||||
services.subscribe_to_child_updates(
|
||||
[this](const OrderStateUpdateEvent &os) {
|
||||
this->_on_child_order_update(os);
|
||||
});
|
||||
services.subscribe_to_book(SecurityKey(me->order_def.security_key()),
|
||||
[this](const BookUpdateEvent &bk_event) {
|
||||
this->_on_book_update(bk_event);
|
||||
});
|
||||
services.subscribe_to_time(
|
||||
[this](const bts::mercury::TimerEvent &te) { _on_time(te); });
|
||||
_switch_state(State::WAITING_FOR_TRIGGER);
|
||||
}
|
||||
break;
|
||||
case State::WAITING_FOR_TRIGGER:
|
||||
case State::WAITING_FOR_FIRST_ACK:
|
||||
case State::STEADY:
|
||||
case State::END: {
|
||||
std::ostringstream oss;
|
||||
oss << "TriggerOrder::on_activate key:" << key() << "state:" << _state;
|
||||
bts_assert_msg(false, oss.str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return OrderOpResult{};
|
||||
}
|
||||
// order price, qty change
|
||||
OrderOpResult TriggerOrder::on_update(Price price, Quantity qty) {
|
||||
LOG(OrderMan, Info,
|
||||
"TriggerOrder::on_update key:{} state:{} price:{} qty:{}", key(), _state,
|
||||
price, qty);
|
||||
switch (_state) {
|
||||
case State::START:
|
||||
me->state.set_price(price.value());
|
||||
me->state.set_quantity(qty.value());
|
||||
// cant report because does not have order_services yet
|
||||
break;
|
||||
case State::WAITING_FOR_TRIGGER:
|
||||
me->state.set_price(price.value());
|
||||
me->state.set_quantity(qty.value());
|
||||
_report_state();
|
||||
_analyze_trigger_and_shoot();
|
||||
break;
|
||||
case State::WAITING_FOR_FIRST_ACK:
|
||||
case State::STEADY:
|
||||
me->state.set_last_msg(
|
||||
"Trigger order can not be modified after the trigger has been hit");
|
||||
_report_state();
|
||||
break;
|
||||
case State::END:
|
||||
break;
|
||||
}
|
||||
return OrderOpResult{};
|
||||
}
|
||||
OrderOpResult TriggerOrder::on_cancel() {
|
||||
LOG(OrderMan, Info, "TriggerOrder::on_cancel key:{} state:{}", key(),
|
||||
_state);
|
||||
me->cancel_mode = CancelMode::external;
|
||||
switch (_state) {
|
||||
case State::START:
|
||||
_switch_state(State::END);
|
||||
break;
|
||||
case State::WAITING_FOR_TRIGGER:
|
||||
_switch_state(State::END);
|
||||
break;
|
||||
case State::WAITING_FOR_FIRST_ACK:
|
||||
// we dont have a WAITING_FOR_ACK_CANCELLED
|
||||
// (like the Quoter), so this message is
|
||||
// rejected and reported
|
||||
me->state.set_last_msg("Order is in flight");
|
||||
_report_state();
|
||||
break;
|
||||
case State::STEADY:
|
||||
_cancel_order(CancelMode::external);
|
||||
break;
|
||||
case State::END:
|
||||
break;
|
||||
}
|
||||
return OrderOpResult{};
|
||||
}
|
||||
void TriggerOrder::_on_book_update(const BookUpdateEvent &b_event) {
|
||||
switch (_state) {
|
||||
case State::START:
|
||||
break;
|
||||
case State::WAITING_FOR_TRIGGER:
|
||||
if (b_event.key().value() == me->order_def.security_key()) {
|
||||
me->book = b_event.book;
|
||||
_observe_market_once();
|
||||
_analyze_trigger_and_shoot();
|
||||
}
|
||||
break;
|
||||
case State::WAITING_FOR_FIRST_ACK:
|
||||
break;
|
||||
case State::STEADY:
|
||||
if (b_event.key().value() == me->order_def.security_key()) {
|
||||
if (me->price_cond_exit) {
|
||||
me->book = b_event.book;
|
||||
_analyze_trigger_and_cancel();
|
||||
}
|
||||
}
|
||||
case State::END:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void TriggerOrder::_on_child_order_update(const OrderStateUpdateEvent &o_ev) {
|
||||
if (o_ev.key == me->child_order.key()) {
|
||||
auto ev_state = o_ev.rebuild_state();
|
||||
me->child_state = ev_state;
|
||||
switch (_state) {
|
||||
case State::START:
|
||||
case State::WAITING_FOR_TRIGGER:
|
||||
bts_assert(false);
|
||||
break;
|
||||
case State::WAITING_FOR_FIRST_ACK:
|
||||
if (SpreaderUtils::order_acknowledged(o_ev.status)) {
|
||||
if (SpreaderUtils::order_in_terminal_state(o_ev.status))
|
||||
_switch_state(State::END);
|
||||
else
|
||||
_switch_state(State::STEADY);
|
||||
}
|
||||
break;
|
||||
case State::STEADY:
|
||||
if (SpreaderUtils::order_in_terminal_state(ev_state.status()))
|
||||
_switch_state(State::END);
|
||||
break;
|
||||
case State::END:
|
||||
break;
|
||||
}
|
||||
_report_state();
|
||||
}
|
||||
}
|
||||
void TriggerOrder::_on_time(const bts::mercury::TimerEvent &) {
|
||||
switch (_state) {
|
||||
case State::START:
|
||||
case State::WAITING_FOR_TRIGGER:
|
||||
case State::WAITING_FOR_FIRST_ACK:
|
||||
break;
|
||||
case State::STEADY: {
|
||||
if (me->timeout_exit)
|
||||
_analyze_trigger_and_cancel();
|
||||
break;
|
||||
}
|
||||
case State::END:
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool TriggerOrder::_on_init_new(const api::orderman::OrderDefinition &order_def,
|
||||
const PreActivationOrderServices &pre_os) {
|
||||
me->order_def = order_def;
|
||||
if (!me->order_def.has_custom_def() ||
|
||||
!me->order_def.custom_def().has_trigger()) {
|
||||
LOG(OrderMan, Error,
|
||||
"TriggerOrder::_on_init_new key:{} state:{} "
|
||||
"OrderDefinition does not have TriggerOrderDefinition",
|
||||
key(), _state);
|
||||
me->state.set_last_msg("Trigger Order not properly formed");
|
||||
return false;
|
||||
}
|
||||
if (pre_os.sec_db.resolve(SecurityKey(order_def.security_key())) ==
|
||||
nullptr) {
|
||||
LOG(OrderMan, Error,
|
||||
"TriggerOrder::_on_init_new key:{} state:{} "
|
||||
"Could not resolve sec_key:{}",
|
||||
key(), _state, order_def.security_key());
|
||||
me->state.set_last_msg("Could not resolve Trigger Order security");
|
||||
return false;
|
||||
}
|
||||
const auto &trigger_order_def = order_def.custom_def().trigger();
|
||||
if (pre_os.sec_db.resolve(SecurityKey(
|
||||
trigger_order_def.trigger_action().security_id())) == nullptr) {
|
||||
LOG(OrderMan, Error,
|
||||
"TriggerOrder::_on_init_new key:{} state:{} "
|
||||
"Could not resolve child key sec_key:{}",
|
||||
key(), _state, trigger_order_def.trigger_action().security_id());
|
||||
me->state.set_last_msg("Could not resolve Trigger Order child security");
|
||||
return false;
|
||||
}
|
||||
if (!trigger_order_def.trigger_action().has_payup() &&
|
||||
bts::os::is_nan(trigger_order_def.trigger_action().price())) {
|
||||
LOG(OrderMan, Error,
|
||||
"TriggerOrder::_on_init_new key:{} state:{} "
|
||||
"illegal child order price",
|
||||
key(), _state);
|
||||
me->state.set_last_msg("Illegal Child Order price");
|
||||
return false;
|
||||
}
|
||||
me->timeout_exit = trigger_order_def.has_exit_condition() &&
|
||||
trigger_order_def.exit_condition().timeout_enabled();
|
||||
me->timeout_msecs =
|
||||
trigger_order_def.has_exit_condition()
|
||||
? 1000.0 * trigger_order_def.exit_condition().timeout_seconds()
|
||||
: std::numeric_limits<uint32_t>::max();
|
||||
me->price_cond_exit =
|
||||
trigger_order_def.has_exit_condition() &&
|
||||
trigger_order_def.exit_condition().conditional_cancel();
|
||||
|
||||
api::orderman::OrderDefinition child_order_def = order_def;
|
||||
child_order_def.set_security_key(
|
||||
trigger_order_def.trigger_action().security_id());
|
||||
child_order_def.mutable_custom_def()->CopyFrom(trigger_order_def.child());
|
||||
me->order_builder = OrderBuilder(key(), child_order_def, pre_os.order_factory);
|
||||
const auto *sec_ptr =
|
||||
pre_os.sec_db.resolve(SecurityKey(order_def.security_key()));
|
||||
me->tick_size = std::min(sec_ptr->contract_spec().low_tick(),
|
||||
sec_ptr->contract_spec().high_tick());
|
||||
LOG(OrderMan, Info,
|
||||
"TriggerOrder::_on_init_new key:{} state:{} "
|
||||
"trigger_price_type:{} child_order_price:{}",
|
||||
key(), _state, trigger_order_def.trigger_condition().price_type(),
|
||||
trigger_order_def.trigger_action().price());
|
||||
|
||||
return true;
|
||||
}
|
||||
void TriggerOrder::_report_state() {
|
||||
if (_activated()) {
|
||||
_compute_state();
|
||||
me->services->report_state(me->state);
|
||||
}
|
||||
}
|
||||
void TriggerOrder::_switch_state(State new_state) {
|
||||
switch (new_state) {
|
||||
case State::START:
|
||||
break;
|
||||
case State::WAITING_FOR_TRIGGER:
|
||||
bts_assert(_state == State::START);
|
||||
break;
|
||||
case State::WAITING_FOR_FIRST_ACK:
|
||||
bts_assert(_state == State::WAITING_FOR_TRIGGER);
|
||||
break;
|
||||
case State::STEADY:
|
||||
bts_assert(_state == State::WAITING_FOR_FIRST_ACK);
|
||||
break;
|
||||
case State::END:
|
||||
bts_assert(_state != State::END);
|
||||
break;
|
||||
}
|
||||
LOG(OrderMan, Info, "TriggerOrder::_report_state key:{} {}->{}", key(),
|
||||
_state, new_state);
|
||||
_state = new_state;
|
||||
_report_state();
|
||||
}
|
||||
void TriggerOrder::_analyze_trigger_and_shoot() {
|
||||
if (me->market_observed_once) {
|
||||
double trigger_price = _trigger_price();
|
||||
const md::PriceSize *obs_md = _observed_market();
|
||||
if (_trigger_met(obs_md, trigger_price, me->tick_size,
|
||||
me->trigger_above_market)) {
|
||||
me->child_order_time = bts::Timer::now();
|
||||
me->order_placed = true;
|
||||
LOG(OrderMan, Info,
|
||||
"TriggerOrder::_analyze_trigger_and_shoot"
|
||||
"key:{} state:{} "
|
||||
"trigger_above_md:{} trigger_price:{} md:{} "
|
||||
"order_price:{} order_qty:{}",
|
||||
key(), _state, me->trigger_above_market, trigger_price,
|
||||
obs_md->price(), _child_order_price(), me->state.quantity());
|
||||
me->child_order.activate(Price(_child_order_price()),
|
||||
Quantity(me->state.quantity()));
|
||||
_switch_state(State::WAITING_FOR_FIRST_ACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
void TriggerOrder::_analyze_trigger_and_cancel() {
|
||||
if (me->timeout_exit) {
|
||||
int64_t time_diff = ms_diff(bts::Timer::now(), me->child_order_time);
|
||||
if (time_diff > me->timeout_msecs) {
|
||||
LOG(OrderMan, Info,
|
||||
"TriggerOrder::_analyze_trigger_and_cancel "
|
||||
"key:{} state:{} msecs:{} "
|
||||
"time out, cancelling order",
|
||||
key(), _state, time_diff);
|
||||
_cancel_order(CancelMode::trigger);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (me->price_cond_exit) {
|
||||
double trigger_price = _trigger_price();
|
||||
const md::PriceSize *obs_md = _observed_market();
|
||||
if (!_trigger_met(obs_md, trigger_price, me->tick_size,
|
||||
me->trigger_above_market)) {
|
||||
LOG(OrderMan, Info,
|
||||
"TriggerOrder::_analyze_trigger_and_cancel"
|
||||
"key:{} state:{} "
|
||||
"price condition untrue, cancelling order"
|
||||
"trigger_above_md:{} trigger_price:{} md:{} ",
|
||||
key(), _state, me->trigger_above_market, trigger_price,
|
||||
obs_md->price());
|
||||
_cancel_order(CancelMode::trigger);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
api::orderman::OrderStatus TriggerOrder::_status() const {
|
||||
switch (_state) {
|
||||
// will only be called once the order
|
||||
// has been activated
|
||||
case State::START:
|
||||
case State::WAITING_FOR_TRIGGER:
|
||||
case State::WAITING_FOR_FIRST_ACK:
|
||||
case State::STEADY:
|
||||
return api::orderman::OrderStatus::ORDER_STATUS_WORKING;
|
||||
case State::END:
|
||||
if (!me->is_succ_inited)
|
||||
return api::orderman::OrderStatus::ORDER_STATUS_ERROR;
|
||||
else {
|
||||
if (me->order_placed) {
|
||||
if (!SpreaderUtils::order_in_terminal_state(
|
||||
me->child_state.status()))
|
||||
return api::orderman::OrderStatus::ORDER_STATUS_WORKING;
|
||||
else {
|
||||
if (SpreaderUtils::order_in_error_state(
|
||||
me->child_state.status()))
|
||||
return api::orderman::OrderStatus::ORDER_STATUS_ERROR;
|
||||
else if (SpreaderUtils::order_in_completed_state(
|
||||
me->child_state.status()))
|
||||
return api::orderman::OrderStatus::ORDER_STATUS_COMPLETED;
|
||||
else if (SpreaderUtils::order_in_cancelled_state(
|
||||
me->child_state.status())) {
|
||||
switch (me->cancel_mode) {
|
||||
case CancelMode::none: // this means an unscolicted child cxl
|
||||
case CancelMode::external:
|
||||
return api::orderman::OrderStatus::ORDER_STATUS_CANCELED;
|
||||
case CancelMode::trigger:
|
||||
return api::orderman::OrderStatus::ORDER_STATUS_COMPLETED;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
return api::orderman::OrderStatus::ORDER_STATUS_CANCELED;
|
||||
}
|
||||
}
|
||||
bts_assert(false);
|
||||
return api::orderman::OrderStatus::ORDER_STATUS_INACTIVE;
|
||||
}
|
||||
void TriggerOrder::_compute_state() {
|
||||
if (!me->is_recovered) {
|
||||
me->state.set_status(_status());
|
||||
me->state.set_filled_quantity(me->child_state.filled_quantity());
|
||||
me->state.set_last_price(me->child_state.last_price());
|
||||
me->state.set_avg_price(me->child_state.avg_price());
|
||||
if (_state == State::STEADY)
|
||||
me->state.set_display_status(bts::api::orderman::OrderState::HIDDEN);
|
||||
else
|
||||
me->state.set_display_status(bts::api::orderman::OrderState::VISIBLE);
|
||||
}
|
||||
}
|
||||
void TriggerOrder::_observe_market_once() {
|
||||
if (!me->market_observed_once) {
|
||||
const md::PriceSize *obs_md = _observed_market();
|
||||
if (obs_md != nullptr && obs_md->size() > 0) {
|
||||
me->market_observed_once = true;
|
||||
me->trigger_above_market = _trigger_price() >= obs_md->price();
|
||||
LOG(OrderMan, Info,
|
||||
"TriggerOrder::_observe_market_once "
|
||||
"key:{} state:{} "
|
||||
"md_p:{} md_q:{} trigger_above_md:{}",
|
||||
key(), _state, obs_md->price(), obs_md->size(),
|
||||
me->trigger_above_market);
|
||||
}
|
||||
}
|
||||
}
|
||||
const md::PriceSize *TriggerOrder::_observed_market() const {
|
||||
if (me->order_def.custom_def().trigger().trigger_condition().price_type() ==
|
||||
api::orderman::TriggerPriceType::BID) {
|
||||
const auto &rv = me->book.bid()[0];
|
||||
return &rv;
|
||||
} else if (me->order_def.custom_def()
|
||||
.trigger()
|
||||
.trigger_condition()
|
||||
.price_type() == api::orderman::TriggerPriceType::ASK) {
|
||||
const auto &rv = me->book.ask()[0];
|
||||
return &rv;
|
||||
} else {
|
||||
bts_assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
double TriggerOrder::_trigger_price() const { return me->state.price(); }
|
||||
double TriggerOrder::_child_order_price() const {
|
||||
const auto &trigger_action =
|
||||
me->order_def.custom_def().trigger().trigger_action();
|
||||
double rv = bts::os::nan();
|
||||
if (trigger_action.has_payup()) {
|
||||
double offset = trigger_action.payup().payup_ticks() * me->tick_size;
|
||||
if (me->order_def.side() == api::Side::SIDE_BUY)
|
||||
rv = _trigger_price() + offset;
|
||||
else
|
||||
rv = _trigger_price() - offset;
|
||||
} else
|
||||
rv = trigger_action.price();
|
||||
rv = bts::securities::raw_round_to_quote_price(rv, me->tick_size,
|
||||
me->order_def.side());
|
||||
return rv;
|
||||
}
|
||||
void TriggerOrder::_cancel_order(CancelMode cancel_mode) {
|
||||
me->cancel_mode = cancel_mode;
|
||||
me->child_order.cancel();
|
||||
LOG(OrderMan, Info, "TriggerOrder::_cancel_order key:{} state:{} mode:{}",
|
||||
key(), _state, cancel_mode);
|
||||
_switch_state(State::END);
|
||||
}
|
||||
bool TriggerOrder::_activated() const { return me->services != nullptr; }
|
||||
bool TriggerOrder::_gr_than_eq(double p1, double p2, double tick_size) {
|
||||
return bts::securities::test_price_equality(p1, p2, tick_size) || p1 >= p2;
|
||||
}
|
||||
bool TriggerOrder::_trigger_met(const md::PriceSize *obs_md, double trpr,
|
||||
double tick_size, bool trigger_above_md) {
|
||||
if (obs_md != nullptr)
|
||||
return (trigger_above_md &&
|
||||
_gr_than_eq(obs_md->price(), trpr, tick_size)) ||
|
||||
(!trigger_above_md &&
|
||||
_gr_than_eq(trpr, obs_md->price(), tick_size));
|
||||
return false;
|
||||
}
|
||||
} // namespace orderman
|
||||
} // namespace bts
|
||||
@@ -1,112 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "lib/orderman/orders/Order.h"
|
||||
#include "lib/api/services/OrderService.server.pb.h"
|
||||
|
||||
namespace bts {
|
||||
namespace api {namespace orderman {class OrderDefinition;}}
|
||||
namespace mercury {class TimerEvent;}
|
||||
namespace md {class PriceSize;}
|
||||
namespace orderman {
|
||||
|
||||
class OrderStateUpdateEvent;
|
||||
class BookUpdateEvent;
|
||||
class PreActivationOrderServices;
|
||||
|
||||
class TriggerOrder : public Order
|
||||
{
|
||||
private:
|
||||
|
||||
enum State{
|
||||
// remains here till on_actiavte is called
|
||||
START=0,
|
||||
// waiting for trigger conditions, sends
|
||||
// order and waits to steady
|
||||
WAITING_FOR_TRIGGER=1,
|
||||
// once the order is submitted it gets into
|
||||
// this state until getting a callback
|
||||
// from the child order
|
||||
WAITING_FOR_FIRST_ACK=2,
|
||||
// order is out
|
||||
STEADY=3,
|
||||
END=4
|
||||
};
|
||||
// this is the ways a child order
|
||||
// can be cancelled
|
||||
enum CancelMode{
|
||||
// not cancelled, initial value
|
||||
none=0,
|
||||
// meaning the trigger order
|
||||
// cancelled the child order because
|
||||
// of time or conditional reasons
|
||||
trigger,
|
||||
// the trigger order is itself
|
||||
// externally cancelled
|
||||
// through the cancel call
|
||||
external
|
||||
};
|
||||
public:
|
||||
TriggerOrder();
|
||||
~TriggerOrder();
|
||||
api::orderman::OrderDefinition const& definition() const ;
|
||||
void on_init_new(api::orderman::OrderDefinition const&,
|
||||
const PreActivationOrderServices&) override;
|
||||
void on_init_recovered(api::orderman::OrderRecord const&,
|
||||
const PreActivationOrderServices &) override;
|
||||
OrderOpResult on_activate(Price, Quantity,
|
||||
OrderServices&) override ;
|
||||
OrderOpResult on_update(Price, Quantity) override;
|
||||
OrderOpResult on_cancel() override;
|
||||
private:
|
||||
// callbacks from book and child order udpates
|
||||
void _on_book_update(const BookUpdateEvent&);
|
||||
void _on_child_order_update(OrderStateUpdateEvent const&);
|
||||
void _on_time (const bts::mercury::TimerEvent&);
|
||||
private:
|
||||
// called from init_new
|
||||
bool _on_init_new(const api::orderman::OrderDefinition& order_def,
|
||||
const PreActivationOrderServices& pre_os);
|
||||
void _report_state();
|
||||
void _switch_state(State new_state);
|
||||
// this is called in the WAITING_FOR_TRIGGER state,
|
||||
// to check for the trigger condition and shoot
|
||||
// an order if the condition is met
|
||||
void _analyze_trigger_and_shoot();
|
||||
// this is called in the STEADY state when the
|
||||
// child order is working. (Based on the
|
||||
// parameters) if timed-out or the trigger
|
||||
// condition being false, the
|
||||
// child order is cancelled
|
||||
void _analyze_trigger_and_cancel();
|
||||
api::orderman::OrderStatus _status() const;
|
||||
void _compute_state();
|
||||
// for non trailing trigger prices, we need a
|
||||
// notion if initially the trigger price
|
||||
// was over the observed market or not
|
||||
void _observe_market_once();
|
||||
// based on the trigger market to watch returns
|
||||
// a pointer to the market (bid, ask, ltp). If not
|
||||
// present returns a nullptr
|
||||
const md::PriceSize* _observed_market() const;
|
||||
// this is the price of the parent order
|
||||
// (the trigger price)
|
||||
double _trigger_price() const;
|
||||
// this is in the trigger action
|
||||
double _child_order_price() const;
|
||||
void _cancel_order(CancelMode cancel_mode);
|
||||
bool _activated() const;
|
||||
//returns true is the trigger condition is met
|
||||
static bool _trigger_met(const md::PriceSize* obs_md,
|
||||
double trigger_price,
|
||||
double tick_size,
|
||||
bool trigger_above_md);
|
||||
//returns true is p1 >= p2
|
||||
static bool _gr_than_eq(double p1, double p2, double tick_size);
|
||||
private:
|
||||
State _state;
|
||||
struct PImpl;
|
||||
std::unique_ptr<PImpl> me;
|
||||
};
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,156 @@
|
||||
#include "TriggerAlgo.h"
|
||||
#include "lib/orderman/orders/algo/IAlgoServices.h"
|
||||
#include "lib/domain_model/Security.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#include "lib/orderman/order_factory/IOrderFactory.h"
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "lib/utils/PBUtils.h"
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
using namespace api::orderman;
|
||||
enum class Status { PAUSED, WORKING };
|
||||
|
||||
struct TriggerAlgo::Impl {
|
||||
IAlgoServices *algo = nullptr;
|
||||
Status status = Status::PAUSED;
|
||||
OrderKey child_key;
|
||||
ChildTemplate *child_template = nullptr;
|
||||
TriggerOrderDefinition definition;
|
||||
};
|
||||
|
||||
bool check_trigger(const TriggerCondition&) { return false; }
|
||||
bool check_trigger(const TriggerExitCondition&) { return false; }
|
||||
|
||||
TriggerAlgo::TriggerAlgo() :
|
||||
me(new Impl())
|
||||
{ }
|
||||
TriggerAlgo::~TriggerAlgo() { }
|
||||
|
||||
// Not latency sensitive. Do expensive initialization here
|
||||
OrderOpResult TriggerAlgo::do_before_launch(IBeforeLaunchAlgoServices& blas) {
|
||||
const auto& def = blas.definition();
|
||||
// Validation
|
||||
bts_assert(def.has_custom_def());
|
||||
bts_assert(def.custom_def().has_trigger());
|
||||
const auto& trigdef = def.custom_def().trigger();
|
||||
me->definition = trigdef;
|
||||
|
||||
if (!trigdef.has_child()) {
|
||||
return OrderOpResult{ORDER_ERROR_INVALID_ORDER_DEFINITON,
|
||||
"Order must have either a child order definition "};
|
||||
}
|
||||
if (!trigdef.has_trigger_condition()) {
|
||||
return OrderOpResult{ORDER_ERROR_INVALID_ORDER_DEFINITON,
|
||||
"Order must have trigger condition "};
|
||||
}
|
||||
if(!trigdef.has_exit_condition()) {
|
||||
return OrderOpResult{ORDER_ERROR_INVALID_ORDER_DEFINITON,
|
||||
"Order must have exit condition "};
|
||||
}
|
||||
|
||||
init_child_template(blas, trigdef.child());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void TriggerAlgo::do_launch(IAlgoServices& algo) {
|
||||
me->algo = &algo;
|
||||
LOG(OrderMan, Diag, "Starting Trigger Order {}. {} {}@{} Def: {}",
|
||||
algo.order_key().value(),
|
||||
securities::side_name(algo.definition().side()), algo.quantity().value(),
|
||||
algo.price().value(), utils::pb::to_string(algo.definition()));
|
||||
|
||||
algo.subscribe_timer();
|
||||
me->status = Status::PAUSED;
|
||||
on_timer();
|
||||
}
|
||||
|
||||
void TriggerAlgo::do_launch_attached(IAlgoServices& algo, OrderKey k, ChildState state) {
|
||||
me->algo = &algo;
|
||||
LOG(OrderMan, Diag, "Starting Trigger Order {} attached to child {}. {} {}@{} Def: {}",
|
||||
algo.order_key().value(), k.value(),
|
||||
securities::side_name(algo.definition().side()), algo.quantity().value(),
|
||||
algo.price().value(), utils::pb::to_string(algo.definition()));
|
||||
|
||||
algo.subscribe_timer();
|
||||
me->child_key = k;
|
||||
me->status = Status::WORKING;
|
||||
on_child_change(k,state);
|
||||
on_timer();
|
||||
}
|
||||
void TriggerAlgo::do_update(const api::orderman::Update& update) {
|
||||
LOG(OrderMan, Diag, "Trigger order {} receiving update: {}",
|
||||
algo().order_key().value(), utils::pb::to_string(update));
|
||||
|
||||
// validate the update
|
||||
if (update.has_custom() && !update.custom().has_trigger()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Update child_update;
|
||||
child_update.set_price(update.price());
|
||||
child_update.set_quantity(update.quantity());
|
||||
|
||||
if (update.has_custom()) {
|
||||
const auto& trigger_update = update.custom().trigger();
|
||||
// Copy updated fields from trigger_update without erasing missing values (like child)
|
||||
me->definition.MergeFrom(trigger_update);
|
||||
if (trigger_update.has_child()) {
|
||||
*child_update.mutable_custom() = trigger_update.child();
|
||||
init_child_template(algo(), trigger_update.child());
|
||||
}
|
||||
}
|
||||
|
||||
if (me->status == Status::WORKING) {
|
||||
algo().update_child(me->child_key, child_update);
|
||||
}
|
||||
|
||||
// Q: Better to call on_timer now or just wait?
|
||||
on_timer();
|
||||
}
|
||||
|
||||
void TriggerAlgo::on_child_change(OrderKey k, const ChildState& st) {
|
||||
if (k == me->child_key) {
|
||||
algo().update_fill_state(st.total_fills, st.last_price);
|
||||
switch(st.status) {
|
||||
case ChildStatus::WORKING:
|
||||
return;
|
||||
case ChildStatus::COMPLETED:
|
||||
// Q: Does trigger coplete when the child completes or does it wait until condition is met again?
|
||||
algo().complete();
|
||||
return;
|
||||
case ChildStatus::CANCELED:
|
||||
algo().cancel("Unexpected child cancel");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
void TriggerAlgo::on_timer() {
|
||||
// Q: Do we need to throttle here or just make sure check_trigger is fast?
|
||||
if (me->status == Status::PAUSED) {
|
||||
if (check_trigger(me->definition.trigger_condition())) {
|
||||
activate_order(me->definition.trigger_action());
|
||||
}
|
||||
}
|
||||
else if (me->status == Status::WORKING) {
|
||||
// check exit condition
|
||||
if (check_trigger(me->definition.exit_condition())) {
|
||||
terminate_order();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TriggerAlgo::init_child_template(IBeforeLaunchAlgoServices& blas, const api::orderman::CustomOrderDefinition& def) {
|
||||
auto child_def = blas.definition();
|
||||
*child_def.mutable_custom_def() = def;
|
||||
me->child_template = &(blas.make_child_template(child_def));
|
||||
}
|
||||
void TriggerAlgo::activate_order(const TriggerAction&) {
|
||||
}
|
||||
void TriggerAlgo::terminate_order() {
|
||||
}
|
||||
IAlgoServices& TriggerAlgo::algo() const { return *me->algo; }
|
||||
|
||||
}} //namespace
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "lib/orderman/orders/algo/IAlgoOrder.h"
|
||||
#include <memory>
|
||||
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
// TriggerAlgo starts and stops a child order based on an external event
|
||||
// such as a market move or a specified clock time.
|
||||
|
||||
class TriggerAlgo : public IAlgoOrder {
|
||||
public:
|
||||
TriggerAlgo();
|
||||
~TriggerAlgo();
|
||||
|
||||
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_timer() override;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> me;
|
||||
|
||||
void init_child_template(IBeforeLaunchAlgoServices&, const api::orderman::CustomOrderDefinition&);
|
||||
void activate_order(const api::orderman::TriggerAction&);
|
||||
void terminate_order();
|
||||
IAlgoServices& algo() const;
|
||||
|
||||
};
|
||||
|
||||
}} //namespace
|
||||
Generated
+68
-23
@@ -132,6 +132,7 @@
|
||||
"resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
|
||||
"integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"kind-of": "^3.0.2",
|
||||
"longest": "^1.0.1",
|
||||
@@ -1832,7 +1833,8 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.1.1",
|
||||
@@ -1883,7 +1885,8 @@
|
||||
"balanced-match": {
|
||||
"version": "0.4.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.1",
|
||||
@@ -1898,6 +1901,7 @@
|
||||
"version": "0.0.9",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"inherits": "~2.0.0"
|
||||
}
|
||||
@@ -1906,6 +1910,7 @@
|
||||
"version": "2.10.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"hoek": "2.x.x"
|
||||
}
|
||||
@@ -1914,6 +1919,7 @@
|
||||
"version": "1.1.7",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^0.4.1",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -1922,7 +1928,8 @@
|
||||
"buffer-shims": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
@@ -1939,12 +1946,14 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
@@ -1952,22 +1961,26 @@
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"cryptiles": {
|
||||
"version": "2.0.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"boom": "2.x.x"
|
||||
}
|
||||
@@ -2007,7 +2020,8 @@
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
@@ -2039,7 +2053,8 @@
|
||||
"extsprintf": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
@@ -2061,12 +2076,14 @@
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"fstream": {
|
||||
"version": "1.0.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"inherits": "~2.0.0",
|
||||
@@ -2122,6 +2139,7 @@
|
||||
"version": "7.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
@@ -2134,7 +2152,8 @@
|
||||
"graceful-fs": {
|
||||
"version": "4.1.11",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"har-schema": {
|
||||
"version": "1.0.5",
|
||||
@@ -2162,6 +2181,7 @@
|
||||
"version": "3.1.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"boom": "2.x.x",
|
||||
"cryptiles": "2.x.x",
|
||||
@@ -2172,7 +2192,8 @@
|
||||
"hoek": {
|
||||
"version": "2.16.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.1.1",
|
||||
@@ -2189,6 +2210,7 @@
|
||||
"version": "1.0.6",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
@@ -2197,7 +2219,8 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.4",
|
||||
@@ -2209,6 +2232,7 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "1.0.1"
|
||||
}
|
||||
@@ -2222,7 +2246,8 @@
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
@@ -2295,12 +2320,14 @@
|
||||
"mime-db": {
|
||||
"version": "1.27.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.15",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"mime-db": "1.27.0"
|
||||
}
|
||||
@@ -2309,6 +2336,7 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.7"
|
||||
}
|
||||
@@ -2316,12 +2344,14 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@@ -2376,7 +2406,8 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.8.2",
|
||||
@@ -2394,6 +2425,7 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -2423,7 +2455,8 @@
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"performance-now": {
|
||||
"version": "0.2.0",
|
||||
@@ -2434,7 +2467,8 @@
|
||||
"process-nextick-args": {
|
||||
"version": "1.0.7",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"punycode": {
|
||||
"version": "1.4.1",
|
||||
@@ -2472,6 +2506,7 @@
|
||||
"version": "2.2.9",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"buffer-shims": "~1.0.0",
|
||||
"core-util-is": "~1.0.0",
|
||||
@@ -2516,6 +2551,7 @@
|
||||
"version": "2.6.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"glob": "^7.0.5"
|
||||
}
|
||||
@@ -2523,7 +2559,8 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.3.0",
|
||||
@@ -2547,6 +2584,7 @@
|
||||
"version": "1.0.9",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"hoek": "2.x.x"
|
||||
}
|
||||
@@ -2580,6 +2618,7 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@@ -2590,6 +2629,7 @@
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
@@ -2604,6 +2644,7 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@@ -2618,6 +2659,7 @@
|
||||
"version": "2.2.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"block-stream": "*",
|
||||
"fstream": "^1.0.2",
|
||||
@@ -2673,7 +2715,8 @@
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.0.1",
|
||||
@@ -2702,7 +2745,8 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4011,7 +4055,8 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
|
||||
"integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.3.1",
|
||||
|
||||
Reference in new issue
Block a user