Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3de164c53 | ||
|
|
1d1c0d6b85 | ||
|
|
2f127ecc18 | ||
|
|
3fc080d02c | ||
|
|
d86543261c | ||
|
|
1a7fcb2027 |
No files matched your search
@@ -24,7 +24,7 @@ static class ExchangeSpecificUitlities{
|
||||
}
|
||||
|
||||
public static bool is_ice_lots(SecurityImmutable sec){
|
||||
return sec.Ice.MarketDescription.Contains("ICE Lots");
|
||||
return sec.Ice.IsIceLots;
|
||||
}
|
||||
|
||||
public static bool is_tas(SecurityImmutable sec){
|
||||
@@ -33,25 +33,36 @@ static class ExchangeSpecificUitlities{
|
||||
|
||||
#endregion
|
||||
|
||||
#region CPP
|
||||
#region CPP
|
||||
public static double get_future_cpp (SecurityImmutable sec){
|
||||
if(sec.Symbol == "HHD")
|
||||
// only futures in HHD are the dailies, everything
|
||||
// else is a strip or spread
|
||||
return 1;
|
||||
// TODO, this should be part of the security
|
||||
if(sec.Symbol == "HHD")
|
||||
return 2500;
|
||||
if(is_ice_lots(sec))
|
||||
return 2500;
|
||||
else{
|
||||
var days_in_month = DateTime.DaysInMonth(Convert.ToInt32(sec.Future.ExpirationYear),
|
||||
Convert.ToInt32(sec.Future.ExpirationMonth));
|
||||
return days_in_month; // because the size is multiplied by 2500 already
|
||||
// other futures - HNG, HHD, HEN - are like daily
|
||||
// strips
|
||||
UInt32 days_in_month = 31;
|
||||
var mm = sec.Future.ExpirationMonth;
|
||||
if(mm <=11)
|
||||
days_in_month = Convert.ToUInt32(
|
||||
DateTime.DaysInMonth(
|
||||
Convert.ToInt32(sec.Future.ExpirationYear),
|
||||
// expects 1-12
|
||||
Convert.ToInt32(sec.Future.ExpirationMonth+1)));
|
||||
else
|
||||
// TODO : sometimes Jan is 12 !
|
||||
days_in_month = 31;
|
||||
return days_in_month * 2500;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static double get_spread_cpp(SecurityImmutable sec){
|
||||
// for purpose of pnl we are 0'ing out the spread pnl
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
public static double get_strip_cpp(PNLUnitImmutable pnl,
|
||||
SecurityImmutable sec){
|
||||
// add up all the legs
|
||||
|
||||
@@ -81,9 +81,9 @@ void PositionUtilities::apply_fill (PositionEntry& pos,
|
||||
const Fill& f,
|
||||
bool is_add){
|
||||
// strip legs are not applied to positions
|
||||
if(f.fill_kind() == Fill::FILL_KIND_STRIP_LEG ||
|
||||
f.fill_kind() == Fill::FILL_KIND_SOD_STRIP_LEG)
|
||||
return;
|
||||
// if(f.fill_kind() == Fill::FILL_KIND_STRIP_LEG ||
|
||||
// f.fill_kind() == Fill::FILL_KIND_SOD_STRIP_LEG)
|
||||
// return;
|
||||
PositionUtilities::apply_fill(pos,
|
||||
f.side(),
|
||||
f.last_price(),
|
||||
@@ -113,6 +113,13 @@ void PositionUtilities::total_position(const PositionUnit& p,
|
||||
p.admin_day().short_pos() + p.admin_sod().short_pos();
|
||||
}
|
||||
|
||||
void PositionUtilities::day_position(const PositionUnit& p,
|
||||
uint32_t& total_long,
|
||||
uint32_t& total_short){
|
||||
total_long = p.day().long_pos() + p.admin_day().long_pos();
|
||||
total_short = p.day().short_pos() + p.admin_day().short_pos();
|
||||
}
|
||||
|
||||
PositionEntry PositionUtilities::add_positions (
|
||||
const std::vector<PositionEntry const*>& v){
|
||||
double long_pos = 0, long_price = 0, short_pos = 0, short_price = 0;
|
||||
|
||||
@@ -39,6 +39,10 @@ public:
|
||||
uint32_t& total_long,
|
||||
uint32_t& total_short);
|
||||
|
||||
static void day_position(const bts::api::position::PositionUnit& p,
|
||||
uint32_t& total_long,
|
||||
uint32_t& total_short);
|
||||
|
||||
static bts::api::position::PositionEntry add_positions (
|
||||
const std::vector<bts::api::position::PositionEntry const*>& v);
|
||||
|
||||
|
||||
@@ -219,13 +219,32 @@ inline bool test_price_equality(double price_1, double price_2) {
|
||||
// ice specialization /////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline bool is_ice_spread(const bts::api::ICEExchangeData& ice_data){
|
||||
const auto& strip = ice_data.strip_name();
|
||||
return strip.find("/") != std::string::npos;
|
||||
// TODO, move this to its own file
|
||||
|
||||
static const uint32_t days_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31};
|
||||
|
||||
inline Quantity from_exchange_qty (Quantity exchange_qty,
|
||||
const bts::api::Security& sec){
|
||||
return Quantity(exchange_qty.value()/sec.ice().exchange_qty_mult());
|
||||
}
|
||||
|
||||
inline Quantity to_exchange_qty (Quantity internal_qty,
|
||||
const bts::api::Security& sec){
|
||||
return Quantity(internal_qty.value()*sec.ice().exchange_qty_mult());
|
||||
}
|
||||
|
||||
inline bool is_spread(const bts::api::Security& sec){
|
||||
return sec.has_ice() && sec.has_spread() &&
|
||||
sec.ice().strip_name().find("/") != std::string::npos;
|
||||
}
|
||||
|
||||
inline bool is_strip(const bts::api::Security& sec){
|
||||
return sec.has_ice() && sec.has_spread() && !is_ice_spread(sec.ice());
|
||||
auto rv = sec.has_ice() && sec.has_spread() &&
|
||||
!is_spread(sec);
|
||||
if(rv != sec.ice().is_strip())
|
||||
LOG(OrderMan, Error, "Sec:{} is_strip incorrect, expected:{}",
|
||||
sec.id(),rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
inline bool is_spread_of_strips(const bts::api::Security& sec){
|
||||
@@ -239,5 +258,61 @@ inline bool is_spread_of_strips(const bts::api::Security& sec){
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool is_strip_with_no_leg_fills(const bts::api::Security& sec){
|
||||
return sec.has_ice() &&
|
||||
sec.symbol() == "HHD" &&
|
||||
is_strip(sec);
|
||||
}
|
||||
|
||||
inline bool is_daily_contract(const api::Security& s){
|
||||
return s.symbol() == "HHD";
|
||||
}
|
||||
|
||||
// risk qty is the qty used for risk purposes for
|
||||
// 1 lot of this security.
|
||||
// daily futures and ice lots are considered 1,
|
||||
// other futures (HNG,HIS,HEN etc) are the days
|
||||
// in the month
|
||||
inline uint32_t get_fut_risk_qty(const api::Security& s){
|
||||
bts_assert(s.has_future());
|
||||
bts_assert(s.has_ice());
|
||||
if(is_daily_contract(s))
|
||||
return 1;
|
||||
else if(s.ice().is_ice_lots())
|
||||
return 1;
|
||||
else{
|
||||
auto mm = s.future().expiration_month();
|
||||
if(mm <=11)
|
||||
return days_in_month[mm];
|
||||
else{
|
||||
LOG(persist, Error, "Illegal month:{} for sec:{}",
|
||||
mm, s.id());
|
||||
// TODO
|
||||
// Jan is showing as 12 sometimes
|
||||
return 31;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t get_spread_direct_risk_qty(const api::Security& s){
|
||||
bts_assert(s.has_spread());
|
||||
bts_assert(s.has_ice());
|
||||
if(s.ice().is_ice_lots())
|
||||
return 1;
|
||||
else
|
||||
return 30;
|
||||
}
|
||||
|
||||
inline uint32_t get_risk_qty (const api::Security& s){
|
||||
if(s.has_future())
|
||||
return get_fut_risk_qty(s);
|
||||
else if (s.has_spread())
|
||||
return get_spread_direct_risk_qty(s);
|
||||
else{
|
||||
bts_assert(false);
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace securities
|
||||
} // namespace bts
|
||||
@@ -116,6 +116,9 @@ message ICEExchangeData {
|
||||
string strip_name = 140;
|
||||
string mic_code = 150;
|
||||
bool is_strip = 160;
|
||||
bool is_ice_lots = 170;
|
||||
// for example 2500 for HNG, this is the qty the exchange needs to see
|
||||
uint32 exchange_qty_mult = 180;
|
||||
}
|
||||
|
||||
message FutureContract {
|
||||
|
||||
@@ -35,6 +35,8 @@ FixedSecurity::FixedSecurity() :
|
||||
ice_deal_denominator(0),
|
||||
ice_settle_denominator(0),
|
||||
is_ice_strip(false),
|
||||
is_ice_lots(false),
|
||||
ice_exchange_qty_mult(1),
|
||||
_symbol(),
|
||||
_cfi_code(),
|
||||
_security_desc(),
|
||||
@@ -197,6 +199,8 @@ void convert_security(const FixedSecurity& source, Security* presult) {
|
||||
dest.mutable_ice()->set_strip_name(source.ice_strip_name());
|
||||
dest.mutable_ice()->set_mic_code(source.mic_code());
|
||||
dest.mutable_ice()->set_is_strip(source.is_ice_strip);
|
||||
dest.mutable_ice()->set_is_ice_lots(source.is_ice_lots);
|
||||
dest.mutable_ice()->set_exchange_qty_mult(source.ice_exchange_qty_mult);
|
||||
break;
|
||||
case Venue::VENUE_BTS:
|
||||
break;
|
||||
@@ -253,7 +257,6 @@ void convert_security(const FixedSecurity& source, Security* presult) {
|
||||
case SecurityType_INT_MIN_SENTINEL_DO_NOT_USE_:
|
||||
break;
|
||||
}
|
||||
|
||||
get_contract_spec(source, *dest.mutable_contract_spec());
|
||||
}
|
||||
|
||||
@@ -323,6 +326,8 @@ void convert_security(const Security& src, FixedSecurity& dest, bool set_securit
|
||||
dest.ice_deal_denominator = src.ice().deal_denominator();
|
||||
dest.ice_settle_denominator = src.ice().settle_denominator();
|
||||
dest.is_ice_strip = src.ice().is_strip();
|
||||
dest.is_ice_lots = src.ice().is_ice_lots();
|
||||
dest.ice_exchange_qty_mult = src.ice().exchange_qty_mult();
|
||||
|
||||
dest.ice_market_description(src.ice().market_description());
|
||||
dest.ice_cleared_alias(src.ice().cleared_alias());
|
||||
|
||||
@@ -63,6 +63,8 @@ public:
|
||||
int8_t ice_deal_denominator;
|
||||
int8_t ice_settle_denominator;
|
||||
bool is_ice_strip;
|
||||
bool is_ice_lots;
|
||||
uint32_t ice_exchange_qty_mult;
|
||||
|
||||
STRING_PROPERTY(ice_market_description);
|
||||
STRING_PROPERTY(ice_cleared_alias);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "lib/utils/helper_macros.h"
|
||||
#include "lib/utils/Dispatcher.h"
|
||||
#include "lib/utils/bts_chrono.h"
|
||||
#include "lib/domain_model/Security.h"
|
||||
|
||||
#include <boost/pool/object_pool.hpp>
|
||||
#include <atomic>
|
||||
@@ -59,6 +60,17 @@ struct ICEExecutionGateway::PImpl
|
||||
md::SecurityOracle sec_oracle;
|
||||
};
|
||||
|
||||
void from_exchange_qty (const api::Security& sec, bts::orderman::ExecutionReport* exec_rep){
|
||||
exec_rep->quantity = bts::securities::from_exchange_qty(
|
||||
exec_rep->quantity,sec);
|
||||
exec_rep->filled_quantity = bts::securities::from_exchange_qty(
|
||||
exec_rep->filled_quantity,sec);
|
||||
exec_rep->last_shares = bts::securities::from_exchange_qty(
|
||||
exec_rep->last_shares,sec);
|
||||
exec_rep->max_show = bts::securities::from_exchange_qty(
|
||||
exec_rep->max_show,sec);
|
||||
}
|
||||
|
||||
ICEExecutionGateway::ICEExecutionGateway(const config::JetFIX::Connection &cfg, trace::PerfLog &log, md::SecurityOracle o)
|
||||
: IExecutionGateway(), me(new PImpl(cfg, log, o))
|
||||
{
|
||||
@@ -420,6 +432,7 @@ void ICEExecutionGateway::on_exe_report(const jetfix::fix::ICEExecutionReportMsg
|
||||
data->last_shares = msg.has<fix::LastShares>()
|
||||
? Quantity(msg.get<fix::LastShares>())
|
||||
: Quantity(0);
|
||||
from_exchange_qty(*sec,data);
|
||||
auto last_px_sec = data->leg_security ? data->leg_security : sec;
|
||||
data->last_price = msg.has<fix::LastPx>()
|
||||
? securities::decode_exchange_price(*last_px_sec, Price(msg.get<fix::LastPx>()))
|
||||
|
||||
@@ -263,9 +263,9 @@ make_pos_event(api::position::PositionUnit const &pu,
|
||||
auto sec = o.resolve(SecurityKey(pu.security_id()));
|
||||
if (sec) {
|
||||
uint32_t total_long_pos, total_short_pos;
|
||||
bts::position::PositionUtilities::total_position(pu,
|
||||
total_long_pos,
|
||||
total_short_pos);
|
||||
bts::position::PositionUtilities::day_position(pu,
|
||||
total_long_pos,
|
||||
total_short_pos);
|
||||
FilledPositionEvent ev{
|
||||
*sec, AccountID(pu.account_id()),
|
||||
Quantity(total_long_pos),
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "lib/os/os.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/span.pb.h"
|
||||
#include "lib/domain_model/Security.h"
|
||||
|
||||
|
||||
namespace bts {
|
||||
@@ -125,9 +126,34 @@ public:
|
||||
AccountID account_id,
|
||||
Quantity long_pos,
|
||||
Quantity short_pos){
|
||||
LOG(OrderMan, Info,
|
||||
"LimitStore::set_filled_position sec:{} sym:{} acc:{} "
|
||||
"long:{} short:{}",
|
||||
security.id(), security.symbol(), account_id, long_pos, short_pos);
|
||||
SecurityLimitTree* sec_tree = _find_security(security.id());
|
||||
bts_assert(sec_tree != nullptr);
|
||||
sec_tree->update_position(account_id, long_pos, short_pos);
|
||||
// there are some strips where we dont store the leg
|
||||
// fills (because the exchange does not give us that, for
|
||||
// example the HHD strip of daily futures). in this case,
|
||||
// we add it manually here
|
||||
if(bts::securities::is_strip_with_no_leg_fills(security)){
|
||||
bts_assert(security.has_spread());
|
||||
for (const auto& l : security.spread().legs()){
|
||||
auto lside = l.side();
|
||||
auto lqty = l.quantity();
|
||||
Quantity llong, lshort;
|
||||
if(lside == api::Side::SIDE_BUY){
|
||||
llong = Quantity(long_pos.value() * lqty);
|
||||
lshort = Quantity(short_pos.value() * lqty);
|
||||
}
|
||||
else{
|
||||
llong = Quantity(short_pos.value() * lqty);
|
||||
lshort = Quantity(long_pos.value() * lqty);
|
||||
}
|
||||
set_filled_position(l.security(),account_id,llong,lshort);
|
||||
}
|
||||
}
|
||||
}
|
||||
void set_pnl (AccountID account_id, double pnl);
|
||||
void set_excess_liquidity (AccountID account_id, double excess_liquidity);
|
||||
@@ -174,7 +200,7 @@ public:
|
||||
const api::Security& security,
|
||||
api::Side side,
|
||||
Quantity quantity,
|
||||
bool enforce_clip = true) const{
|
||||
bool enforce_clip = true){
|
||||
Limiter::LimiterQueryResult rv; rv.result = Limiter::OK;
|
||||
const auto acct_it = _account_data_map.find(account_id.value());
|
||||
AccountData const* account_ptr = (acct_it == _account_data_map.end()) ?
|
||||
@@ -185,33 +211,21 @@ public:
|
||||
_margin_limit(account_ptr,rv);
|
||||
if(!rv.ok())
|
||||
return rv;
|
||||
const SecurityLimitTree* sec_tree = _find_security(security.id());
|
||||
SecurityLimitTree* sec_tree = _find_security(security.id());
|
||||
bts_assert(sec_tree != nullptr);
|
||||
rv = sec_tree->can_send_new_order(account_id, side, quantity, enforce_clip);
|
||||
if (rv.ok() && security.has_spread() && sec_tree->is_residual_pos_spread()){
|
||||
// if its rejected we dont need to validate further,
|
||||
// however if its ok and its a spread we need to check
|
||||
// the legs
|
||||
for (const api::ContractLeg& leg : security.spread().legs()){
|
||||
api::Side effective_leg_side = side == api::Side::SIDE_BUY ? leg.side() : LimiterUtils::invert_side(leg.side());
|
||||
Quantity effective_leg_qty = Quantity (quantity.value() * std::abs(leg.quantity()));
|
||||
// in the case of a spread with future legs the clip limits
|
||||
// for the legs are not checked. if the legs are are spreads
|
||||
// themselves then they are checked (eventually it will recuse
|
||||
// down to a non spread)
|
||||
rv = can_send_new_order (account_id, leg.security(), effective_leg_side, effective_leg_qty,
|
||||
leg.security().has_spread());
|
||||
if (!rv.ok())
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
if (rv.ok() && security.has_spread())
|
||||
return _check_new_order_pos_violations_recursively(security,
|
||||
account_id,
|
||||
side,
|
||||
quantity);
|
||||
return rv;
|
||||
}
|
||||
Limiter::LimiterQueryResult can_update_order(AccountID account_id,
|
||||
const api::Security& security,
|
||||
api::Side side,
|
||||
Quantity from, Quantity to,
|
||||
bool enforce_clip = true) const{
|
||||
bool enforce_clip = true){
|
||||
Limiter::LimiterQueryResult rv; rv.result = Limiter::OK;
|
||||
const auto acct_it = _account_data_map.find(account_id.value());
|
||||
AccountData const* account_ptr = (acct_it == _account_data_map.end()) ?
|
||||
@@ -226,17 +240,12 @@ public:
|
||||
bts_assert(sec_tree != nullptr);
|
||||
rv = sec_tree->can_update_order(account_id, side, from, to, enforce_clip);
|
||||
// same logic for the spread legs as above
|
||||
if (rv.ok() && security.has_spread() && sec_tree->is_residual_pos_spread()){
|
||||
for (const api::ContractLeg& leg : security.spread().legs()){
|
||||
api::Side effective_leg_side = side == api::Side::SIDE_BUY ? leg.side() : LimiterUtils::invert_side(leg.side());
|
||||
Quantity effective_leg_from_qty = Quantity (from.value() * std::abs(leg.quantity()));
|
||||
Quantity effective_leg_to_qty = Quantity (to.value() * std::abs(leg.quantity()));
|
||||
rv = can_update_order (account_id, leg.security(), effective_leg_side, effective_leg_from_qty, effective_leg_to_qty,
|
||||
leg.security().has_spread());
|
||||
if (!rv.ok())
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
if (rv.ok() && security.has_spread())
|
||||
return _check_order_update_pos_violations_recursively(security,
|
||||
account_id,
|
||||
side,
|
||||
from,
|
||||
to);
|
||||
return rv;
|
||||
}
|
||||
// test only functions
|
||||
@@ -320,5 +329,94 @@ private:
|
||||
out_res.error_msg = "Could not find account";
|
||||
}
|
||||
}
|
||||
|
||||
// 07/16/2020
|
||||
|
||||
// adds the working order to the current state.
|
||||
// if s is a spread, it iterates through the legs and
|
||||
// adds the legs with the appropriate side, qty
|
||||
void _add_or_remove_working_order(const api::Security& s,
|
||||
AccountID acc,
|
||||
api::Side side,
|
||||
Quantity qty,
|
||||
bool is_add,
|
||||
bool real_op){
|
||||
LOG(OrderMan, Info,
|
||||
"LimitStore::_add_or_remove_working_order "
|
||||
"real_op:{} is_add:{} sec:{} acc:{} side:{} qty:{} ",
|
||||
real_op, is_add, s.id(), acc, side, qty);
|
||||
SecurityLimitTree* sec_tree = _find_security(s.id());
|
||||
if(is_add)
|
||||
sec_tree->add_working_order(acc,side,qty,real_op);
|
||||
else
|
||||
sec_tree->remove_working_order(acc,side,qty,real_op);
|
||||
if(s.has_spread()){
|
||||
for (const auto& l : s.spread().legs()){
|
||||
const auto& lsec = l.security();
|
||||
api::Side eff_leg_side = side == api::Side::SIDE_BUY ? l.side() :
|
||||
LimiterUtils::invert_side(l.side());
|
||||
Quantity eff_leg_qty = Quantity (qty.value() * std::abs(l.quantity()));
|
||||
_add_or_remove_working_order(lsec,acc,eff_leg_side,eff_leg_qty,is_add,real_op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for the current state of the positions, check the violations
|
||||
// on the side passed in the arg
|
||||
Limiter::LimiterQueryResult _check_curr_pos_violations_recursively(
|
||||
const api::Security& s,
|
||||
AccountID acc,
|
||||
api::Side side){
|
||||
SecurityLimitTree* sec_tree = _find_security(s.id());
|
||||
auto rv =
|
||||
sec_tree->can_send_new_order(acc,side,Quantity(0),false);
|
||||
if(rv.ok()){
|
||||
if(s.has_spread()){
|
||||
for (const auto& l : s.spread().legs()){
|
||||
const auto& lsec = l.security();
|
||||
api::Side eff_leg_side = side == api::Side::SIDE_BUY ? l.side() :
|
||||
LimiterUtils::invert_side(l.side());
|
||||
auto l_rv =_check_curr_pos_violations_recursively(lsec,acc,eff_leg_side);
|
||||
if(!l_rv.ok())
|
||||
return l_rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
Limiter::LimiterQueryResult _check_new_order_pos_violations_recursively(
|
||||
const api::Security& s,
|
||||
AccountID acc,
|
||||
api::Side side,
|
||||
Quantity qty){
|
||||
_add_or_remove_working_order(s,acc,side,qty,true,false);
|
||||
auto rv = _check_curr_pos_violations_recursively(s,acc,side);
|
||||
_add_or_remove_working_order(s,acc,side,qty,false,false);
|
||||
return rv;
|
||||
}
|
||||
|
||||
Limiter::LimiterQueryResult _check_order_update_pos_violations_recursively(
|
||||
const api::Security& s,
|
||||
AccountID acc,
|
||||
api::Side side,
|
||||
Quantity from,
|
||||
Quantity to){
|
||||
if(to.value() > from.value()){
|
||||
// this is the equivelant of a new order of
|
||||
// qty = to-from, on the same side
|
||||
auto eff_qty = Quantity(to.value()-from.value());
|
||||
auto eff_side = side;
|
||||
return _check_new_order_pos_violations_recursively(s,acc,eff_side,eff_qty);
|
||||
}
|
||||
else{
|
||||
// this is the equivelant of a new order of
|
||||
// qty = from-to, on the opp side
|
||||
auto eff_qty = Quantity(from.value()-to.value());
|
||||
auto eff_side = LimiterUtils::invert_side(side);
|
||||
return _check_new_order_pos_violations_recursively(s,acc,eff_side,eff_qty);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}}
|
||||
@@ -42,6 +42,10 @@ void Limiter::set_filled_position(api::Security const &security,
|
||||
// add_securities was called or before add was called
|
||||
// (we can put it in a queue and examine it on the
|
||||
// add_securities call)
|
||||
LOG(OrderMan, Info, "Limiter::set_filled_position sec:{} sym:{} acc:{} "
|
||||
"long:{} short:{}",
|
||||
security.id(),security.symbol(),account_id,
|
||||
long_pos,short_pos);
|
||||
if (!_contains_security(&security))
|
||||
LOG(OrderMan, Error, "Limiter::set_filled_position could not find"
|
||||
"sec_key:{} this should be investigated",
|
||||
@@ -59,6 +63,9 @@ void Limiter::add_to_working_position(api::Security const &security,
|
||||
AccountID account_id,
|
||||
api::Side side,
|
||||
Quantity quantity){
|
||||
LOG(OrderMan, Info, "Limiter::add_to_working_position "
|
||||
"sec:{} acc:{} side:{} qty:{}",
|
||||
security.id(),account_id,side,quantity);
|
||||
if (!_contains_security(&security))
|
||||
add_security(security);
|
||||
if (!me->limit_store->has_account(account_id)) {
|
||||
@@ -71,6 +78,9 @@ void Limiter::remove_from_working_position(api::Security const &security,
|
||||
AccountID account_id,
|
||||
api::Side side,
|
||||
Quantity quantity) {
|
||||
LOG(OrderMan, Info, "Limiter::remove_from_working_position "
|
||||
"sec:{} acc:{} side:{} qty:{}",
|
||||
security.id(),account_id,side,quantity);
|
||||
if (!_contains_security(&security))
|
||||
add_security(security);
|
||||
if (!me->limit_store->has_account(account_id)) {
|
||||
|
||||
@@ -35,7 +35,9 @@ class Limiter {
|
||||
// Set the current *filled* position, i.e. actual number of
|
||||
// contracts owned, for the specified sec and account. Spreads are
|
||||
// not recursively deconstructed into their legs, i.e this sets the
|
||||
// position only for the passed security.
|
||||
// position only for the passed security
|
||||
// (this function is called by the framework for the
|
||||
// the legs)
|
||||
void set_filled_position(api::Security const &, AccountID,
|
||||
Quantity long_pos,
|
||||
Quantity short_pos);
|
||||
@@ -48,11 +50,6 @@ class Limiter {
|
||||
void remove_from_working_position(api::Security const &, AccountID,
|
||||
api::Side, Quantity);
|
||||
|
||||
void update_position(const api::position::PositionUnit &);
|
||||
// this can be a partial list
|
||||
void update_position(
|
||||
const std::vector<api::position::PositionUnit const *> &positions);
|
||||
|
||||
void set_day_pnl(AccountID account_id, double pnl);
|
||||
void set_excess_liquidity(AccountID account_id, double excesss_liquidity);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include "lib/orderman/orderman_utils/orderman_common.h"
|
||||
#include "lib/api/services/OrderService.pb.h"
|
||||
#include "Limiter.h"
|
||||
|
||||
namespace bts{
|
||||
@@ -9,12 +10,36 @@ namespace orderman{
|
||||
|
||||
class LimiterUtils{
|
||||
public:
|
||||
|
||||
static std::string product_string(const bts::api::admin::ProductScope& p){
|
||||
auto _type = [&]() -> std::string {
|
||||
switch(p.security_type()){
|
||||
case api::SecurityType::SECURITYTYPE_UNSET:
|
||||
return "UNSET";
|
||||
case api::SecurityType::SECURITYTYPE_STOCK:
|
||||
return "STOCK";
|
||||
case api::SecurityType::SECURITYTYPE_OPTION:
|
||||
return "OPTION";
|
||||
case api::SecurityType::SECURITYTYPE_FUTURE:
|
||||
return "FUTURE";
|
||||
case api::SecurityType::SECURITYTYPE_SPREAD:
|
||||
return "SPREAD";
|
||||
case api::SecurityType::SECURITYTYPE_SYNTHETIC:
|
||||
return "SYNTHETIC";
|
||||
default:
|
||||
return "UNSET";
|
||||
}
|
||||
return "UNSET";
|
||||
};
|
||||
return p.security_symbol() + " " + _type();
|
||||
}
|
||||
static void can_send_new_order_net(api::Side side,
|
||||
Quantity quantity,
|
||||
bool is_security_level,
|
||||
int long_pos_limit,
|
||||
int short_pos_limit,
|
||||
int net_pos,
|
||||
const bts::api::admin::ProductScope& p,
|
||||
Limiter::LimiterQueryResult& out_res){
|
||||
out_res.result = Limiter::OK;
|
||||
bts_assert(long_pos_limit >= 0);
|
||||
@@ -31,7 +56,9 @@ public:
|
||||
}
|
||||
else {
|
||||
out_res.result = Limiter::PROD_LONG_POS_BREACHED;
|
||||
out_res.error_msg = fmt::format("Product Long Position (Max {} Current {})", long_pos_limit, net_pos);
|
||||
out_res.error_msg = fmt::format(
|
||||
"Product Long Position for {} Max {} ({})",
|
||||
product_string(p), long_pos_limit, net_pos_new);
|
||||
}
|
||||
}
|
||||
else if (net_pos_new < short_pos_limit && side==api::Side::SIDE_SELL){
|
||||
@@ -41,7 +68,9 @@ public:
|
||||
}
|
||||
else {
|
||||
out_res.result = Limiter::PROD_SHORT_POS_BREACHED;
|
||||
out_res.error_msg = fmt::format("Product Short Position (Max {} Current {})", short_pos_limit, net_pos);
|
||||
out_res.error_msg = fmt::format(
|
||||
"Product Short Position for {} Max {} ({})",
|
||||
product_string(p), short_pos_limit, net_pos_new);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,6 +81,7 @@ public:
|
||||
int long_pos_limit,
|
||||
int short_pos_limit,
|
||||
int net_pos,
|
||||
const bts::api::admin::ProductScope& p,
|
||||
Limiter::LimiterQueryResult& out_res){
|
||||
out_res.result = Limiter::OK;
|
||||
if (to.value() > from.value()){
|
||||
@@ -68,7 +98,9 @@ public:
|
||||
}
|
||||
else {
|
||||
out_res.result = Limiter::PROD_LONG_POS_BREACHED;
|
||||
out_res.error_msg = fmt::format("Product Long Position (Max {} Current {})", long_pos_limit, net_pos);
|
||||
out_res.error_msg = fmt::format(
|
||||
"Product Long Position for {} Max {} ({})",
|
||||
product_string(p), long_pos_limit, net_pos_new);
|
||||
}
|
||||
}
|
||||
else if (net_pos_new < short_pos_limit){
|
||||
@@ -78,7 +110,9 @@ public:
|
||||
}
|
||||
else {
|
||||
out_res.result = Limiter::SEC_SHORT_POS_BREACHED;
|
||||
out_res.error_msg = fmt::format("Product Short Position (Max {} Current {})", short_pos_limit, net_pos);
|
||||
out_res.error_msg = fmt::format(
|
||||
"Product Short Position for {} Max {} ({})",
|
||||
product_string(p), short_pos_limit, net_pos_new);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,21 +123,22 @@ public:
|
||||
uint32_t short_pos_limit,
|
||||
uint32_t long_pos,
|
||||
uint32_t short_pos,
|
||||
const bts::api::admin::ProductScope& p,
|
||||
Limiter::LimiterQueryResult& out_res){
|
||||
out_res.result = Limiter::OK;
|
||||
if(side == api::Side::SIDE_BUY){
|
||||
if(long_pos + quantity.value() > long_pos_limit){
|
||||
out_res.error_msg = fmt::format("Long Position (Abs) "
|
||||
"(Max {} Current {})",
|
||||
long_pos_limit,long_pos);
|
||||
out_res.error_msg = fmt::format("Long Position (Abs) for {} "
|
||||
"(Max {} {})",
|
||||
product_string(p),long_pos_limit,long_pos + quantity.value());
|
||||
out_res.result = Limiter::SEC_LONG_POS_BREACHED;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(short_pos + quantity.value() > short_pos_limit){
|
||||
out_res.error_msg = fmt::format("Short Position (Abs) "
|
||||
"(Max {} Current {})",
|
||||
short_pos_limit,short_pos);
|
||||
out_res.error_msg = fmt::format("Short Position (Abs) for {}"
|
||||
"(Max {} {})",
|
||||
product_string(p),short_pos_limit,short_pos + quantity.value());
|
||||
out_res.result = Limiter::SEC_SHORT_POS_BREACHED;
|
||||
}
|
||||
}
|
||||
@@ -115,23 +150,24 @@ public:
|
||||
uint32_t short_pos_limit,
|
||||
uint32_t long_pos,
|
||||
uint32_t short_pos,
|
||||
const bts::api::admin::ProductScope& p,
|
||||
Limiter::LimiterQueryResult& out_res){
|
||||
out_res.result = Limiter::OK;
|
||||
if (to.value() > from.value()){
|
||||
uint32_t diff = to.value() - from.value();
|
||||
if(side == api::Side::SIDE_BUY){
|
||||
if(long_pos + diff > long_pos_limit){
|
||||
out_res.error_msg = fmt::format("Long Position (Abs) "
|
||||
"(Max {} Current {})",
|
||||
long_pos_limit,long_pos);
|
||||
out_res.error_msg = fmt::format("Long Position (Abs) for {}"
|
||||
"(Max {} {})",
|
||||
product_string(p),long_pos_limit,long_pos);
|
||||
out_res.result = Limiter::SEC_LONG_POS_BREACHED;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(short_pos + diff > short_pos_limit){
|
||||
out_res.error_msg = fmt::format("Short Position (Abs) "
|
||||
"(Max {} Current {})",
|
||||
short_pos_limit,short_pos);
|
||||
out_res.error_msg = fmt::format("Short Position (Abs) for {}"
|
||||
"(Max {} {})",
|
||||
product_string(p),short_pos_limit,short_pos + diff);
|
||||
out_res.result = Limiter::SEC_SHORT_POS_BREACHED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,17 +46,37 @@ public:
|
||||
_long_pos=source._long_pos;
|
||||
_short_pos=source._short_pos;
|
||||
}
|
||||
void add_position(api::Side side, uint32_t qty){
|
||||
// real_op in both the functions below is for logging purposes only
|
||||
void add_position(api::Side side, uint32_t qty, bool real_op=true){
|
||||
if (side==api::Side::SIDE_BUY)
|
||||
_long_pos += qty;
|
||||
else
|
||||
_short_pos += qty;
|
||||
}
|
||||
void remove_position(api::Side side, uint32_t qty){
|
||||
LOG(OrderMan, Info, "ProductLimitNode::position "
|
||||
"real_op:{} add type:{} sym:{} "
|
||||
"side:{} qty:{} "
|
||||
"long_pos:{} short_pos:{}",
|
||||
real_op,
|
||||
_limit.product().security_type(),
|
||||
_limit.product().security_symbol(),
|
||||
side, qty,
|
||||
_long_pos, _short_pos);
|
||||
|
||||
}
|
||||
void remove_position(api::Side side, uint32_t qty, bool real_op=true){
|
||||
if (side==api::Side::SIDE_BUY)
|
||||
_long_pos = _subtract_unsigned(_long_pos, qty);
|
||||
else
|
||||
_short_pos = _subtract_unsigned(_short_pos, qty);
|
||||
LOG(OrderMan, Info, "ProductLimitNode::position "
|
||||
"real_op:{} remove type:{} sym:{}"
|
||||
"side:{} qty:{} "
|
||||
"long_pos:{} short_pos:{}",
|
||||
real_op,
|
||||
_limit.product().security_type(),
|
||||
_limit.product().security_symbol(),
|
||||
side, qty,
|
||||
_long_pos, _short_pos);
|
||||
}
|
||||
// risk limit checks
|
||||
////////////////////
|
||||
@@ -75,6 +95,7 @@ public:
|
||||
static_cast<int>(_long_pos_limit),
|
||||
-1 * static_cast<int>(_short_pos_limit),
|
||||
net_pos(),
|
||||
_limit.product(),
|
||||
result);
|
||||
else
|
||||
LimiterUtils::can_send_new_order_abs (side,
|
||||
@@ -83,6 +104,7 @@ public:
|
||||
_short_pos_limit,
|
||||
_long_pos,
|
||||
_short_pos,
|
||||
_limit.product(),
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
@@ -103,6 +125,7 @@ public:
|
||||
static_cast<int>(_long_pos_limit),
|
||||
-1 * static_cast<int>(_short_pos_limit),
|
||||
net_pos(),
|
||||
_limit.product(),
|
||||
result);
|
||||
else
|
||||
LimiterUtils::can_update_order_abs (side,
|
||||
@@ -112,6 +135,7 @@ public:
|
||||
_short_pos_limit,
|
||||
_long_pos,
|
||||
_short_pos,
|
||||
_limit.product(),
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -18,11 +18,13 @@ namespace bts{
|
||||
namespace orderman{
|
||||
|
||||
// Scope : <security, account>
|
||||
// Created by : SecurityLimitTree
|
||||
// Keeps the running position and clip limits
|
||||
// for a single <security, account>.
|
||||
// Has a reference to the ProductLimitNode for the product
|
||||
// that this security belongs to.
|
||||
// Position limits are enforced through the ProductLimitNode
|
||||
// and clip limits are enforced here
|
||||
class SecurityLimitNode{
|
||||
private:
|
||||
const uint32_t _account_id;
|
||||
@@ -54,11 +56,11 @@ public:
|
||||
// simplicity's sake we do this
|
||||
_product_node.copy_positions_from(source._product_node);
|
||||
}
|
||||
void add_working_order(api::Side side, uint32_t qty){
|
||||
_product_node.add_position(side, qty);
|
||||
void add_working_order(api::Side side, uint32_t qty, bool real_op=true){
|
||||
_product_node.add_position(side, qty, real_op);
|
||||
}
|
||||
void remove_working_order(api::Side side, uint32_t qty){
|
||||
_product_node.remove_position(side, qty);
|
||||
void remove_working_order(api::Side side, uint32_t qty, bool real_op=true){
|
||||
_product_node.remove_position(side, qty, real_op);
|
||||
}
|
||||
void update_position (AccountID account_id,
|
||||
Quantity long_pos,
|
||||
|
||||
@@ -7,14 +7,30 @@ using namespace bts::admin;
|
||||
namespace bts {
|
||||
namespace orderman {
|
||||
|
||||
SecurityLimitTree::QtyNormalization get_qty_norm (const api::Security& s){
|
||||
if(s.has_ice())
|
||||
return SecurityLimitTree::QtyNormalization
|
||||
{
|
||||
s.ice().exchange_qty_mult(),
|
||||
bts::securities::get_risk_qty(s)
|
||||
};
|
||||
else
|
||||
return SecurityLimitTree::QtyNormalization {1,1};
|
||||
}
|
||||
|
||||
SecurityLimitTree::SecurityLimitTree(
|
||||
const api::Security &security,
|
||||
ProductLimitTree &product_tree,
|
||||
const AdminData &admin_data)
|
||||
:
|
||||
_sec_key(security.id()),
|
||||
_is_residual_pos_spread(
|
||||
bts::admin::AdminServiceUtils::residual_pos_spread(security))
|
||||
{
|
||||
bts::admin::AdminServiceUtils::residual_pos_spread(security)),
|
||||
_qty_norm(get_qty_norm(security)){
|
||||
LOG(OrderMan,Info,"SecurityLimitTree sec:{} qty_ratio:{} risk_qty:{}",
|
||||
security.id(),
|
||||
_qty_norm.exchange_qty_mult,
|
||||
_qty_norm.unit_risk_qty);
|
||||
for (const auto &account : admin_data.accounts()) {
|
||||
ProductLimitNode *product_node_ptr =
|
||||
product_tree.find_account(account.id());
|
||||
|
||||
@@ -14,37 +14,81 @@
|
||||
namespace bts{
|
||||
namespace orderman{
|
||||
// Scope : <security>
|
||||
// this keeps the state a single security for all accounts
|
||||
// Created by : LimitStore
|
||||
// this keeps the state of a single security for all accounts
|
||||
// The only state is a vector of SecurityLimitNode (which keeps
|
||||
// the actual data)
|
||||
class SecurityLimitTree{
|
||||
public:
|
||||
// This is to handle the condition when the risk control
|
||||
// is entered in different terms than what the order management
|
||||
// system (and exchange) deal with.
|
||||
// This is a 2 step process
|
||||
// exchange_qty_mult : exchange deals with qty's that are
|
||||
// different than what the ui (both trading and risk)
|
||||
// deals with. for example HNG at ICE
|
||||
// is multiple of 2500, but the ui shows it
|
||||
// as 1, 5000 as 2 etc.
|
||||
// unit_risk_qty : this is purely for risk reasons. for example
|
||||
// the risk for HNG is entered in terms of the ICE lot,
|
||||
// for HHD it is entered in terms of the daily future etc.
|
||||
struct QtyNormalization{
|
||||
const uint32_t exchange_qty_mult = 1;
|
||||
const uint32_t unit_risk_qty = 1;
|
||||
};
|
||||
private:
|
||||
const SecurityKey _sec_key;
|
||||
const bool _is_residual_pos_spread;
|
||||
const QtyNormalization _qty_norm;
|
||||
std::vector<SecurityLimitNode> _tree;
|
||||
public:
|
||||
public:
|
||||
SecurityLimitTree(
|
||||
const api::Security& security,
|
||||
ProductLimitTree& product_tree,
|
||||
const bts::api::admin::AdminData& admin_data);
|
||||
// position updates ////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// real_op is used for logging purposes in all the functions below
|
||||
void add_working_order(AccountID account_id,
|
||||
api::Side side,
|
||||
Quantity quantity){
|
||||
Quantity quantity_arg,
|
||||
bool real_op=true){
|
||||
auto quantity = _convert_qty(quantity_arg);
|
||||
LOG(OrderMan, Info, "SecurityLimitTree::add_working_order "
|
||||
"real_op:{} sec:{} acc:{} "
|
||||
"side:{} qty_arg:{} qty:{}",
|
||||
real_op, _sec_key, account_id,
|
||||
side, quantity_arg, quantity);
|
||||
SecurityLimitNode* node_ptr = _find_account(account_id.value());
|
||||
bts_assert(node_ptr != nullptr);
|
||||
node_ptr->add_working_order(side, quantity.value());
|
||||
node_ptr->add_working_order(side, quantity.value(),real_op);
|
||||
}
|
||||
void remove_working_order(AccountID account_id,
|
||||
api::Side side,
|
||||
Quantity quantity){
|
||||
Quantity quantity_arg,
|
||||
bool real_op=true){
|
||||
auto quantity = _convert_qty(quantity_arg);
|
||||
LOG(OrderMan, Info, "SecurityLimitTree::remove_working_order "
|
||||
"real_op:{} sec:{} acc:{} "
|
||||
"side:{} qty_arg:{} qty:{}",
|
||||
real_op, _sec_key, account_id,
|
||||
side, quantity_arg, quantity);
|
||||
SecurityLimitNode* account_ptr = _find_account(account_id.value());
|
||||
bts_assert(account_ptr != nullptr);
|
||||
account_ptr->remove_working_order(side, quantity.value());
|
||||
account_ptr->remove_working_order(side, quantity.value(),real_op);
|
||||
}
|
||||
void update_position (AccountID account_id,
|
||||
Quantity long_pos,
|
||||
Quantity short_pos){
|
||||
Quantity long_pos_arg,
|
||||
Quantity short_pos_arg){
|
||||
auto long_pos = _convert_qty(long_pos_arg);
|
||||
auto short_pos = _convert_qty(short_pos_arg);
|
||||
LOG(OrderMan, Info, "SecurityLimitTree::update_position "
|
||||
"sec:{} acc:{} "
|
||||
"long_pos_arg:{} long_pos:{} "
|
||||
"short_pos_arg:{} short_pos:{} ",
|
||||
_sec_key, account_id,
|
||||
long_pos_arg, long_pos,
|
||||
short_pos_arg, short_pos);
|
||||
SecurityLimitNode* account_ptr = _find_account(account_id.value());
|
||||
bts_assert(account_ptr != nullptr);
|
||||
account_ptr->update_position(account_id, long_pos, short_pos);
|
||||
@@ -66,6 +110,7 @@ public:
|
||||
Quantity quantity,
|
||||
bool enforce_clip = true)
|
||||
const{
|
||||
quantity = _convert_qty(quantity);
|
||||
const SecurityLimitNode* node_ptr = _find_account(account_id.value());
|
||||
bts_assert(node_ptr!=nullptr);
|
||||
return node_ptr->can_send_new_order(side, quantity, enforce_clip);
|
||||
@@ -76,6 +121,8 @@ public:
|
||||
Quantity to,
|
||||
bool enforce_clip = true)
|
||||
const{
|
||||
from = _convert_qty(from);
|
||||
to = _convert_qty(to);
|
||||
const SecurityLimitNode* node_ptr = _find_account(account_id.value());
|
||||
bts_assert(node_ptr!=nullptr);
|
||||
return node_ptr->can_update_order(side, from, to, enforce_clip);
|
||||
@@ -101,5 +148,8 @@ private:
|
||||
return &node;
|
||||
return nullptr;
|
||||
}
|
||||
Quantity _convert_qty (Quantity qty) const{
|
||||
return Quantity(qty.value() *_qty_norm.unit_risk_qty);
|
||||
}
|
||||
};
|
||||
}}
|
||||
@@ -79,6 +79,13 @@ struct ICENativeOrder::PImpl
|
||||
int buffered_tries = 0;
|
||||
};
|
||||
|
||||
// on outgoing order, converts the qty to exchange terms
|
||||
// (ex HNG that is represented as 1 in the system and
|
||||
// multiples of 2500 at ICE)
|
||||
Quantity to_exchange_qty (const api::Security& sec, Quantity internal_qty){
|
||||
return bts::securities::to_exchange_qty(internal_qty,sec);
|
||||
}
|
||||
|
||||
ICENativeOrder::ICENativeOrder() : me(new PImpl()) {}
|
||||
|
||||
const OrderDefinition& ICENativeOrder::definition() const
|
||||
@@ -349,10 +356,10 @@ void ICENativeOrder::warm_up_activate(OrderServicesData &data, OrderServices &se
|
||||
|
||||
OrderOpResult
|
||||
ICENativeOrder::_on_activate(log::Level reject_level, log::Level transition_level,
|
||||
Price p, Quantity q, bts::orderman::OrderServices &services)
|
||||
Price p, Quantity activate_q, bts::orderman::OrderServices &services)
|
||||
{
|
||||
me->state.set_price(p.value());
|
||||
me->state.set_quantity(q.value());
|
||||
me->state.set_quantity(activate_q.value());
|
||||
me->state.set_filled_quantity(0);
|
||||
me->state.set_last_price(0);
|
||||
me->state.set_avg_price(0);
|
||||
@@ -387,7 +394,7 @@ ICENativeOrder::_on_activate(log::Level reject_level, log::Level transition_leve
|
||||
}
|
||||
|
||||
// can we trade this security, with this size?
|
||||
lqr = me->services->limiter().can_send_new_order( AccountID(me->definition.account_id()), *me->sec, me->definition.side(), q );
|
||||
lqr = me->services->limiter().can_send_new_order( AccountID(me->definition.account_id()), *me->sec, me->definition.side(), activate_q);
|
||||
|
||||
if (!lqr.ok())
|
||||
{
|
||||
@@ -444,8 +451,8 @@ ICENativeOrder::_on_activate(log::Level reject_level, log::Level transition_leve
|
||||
bts_assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
me->create_quantity_bm = encoding.overwrite(me->create_quantity_bm, q.value(), quantity_sz());
|
||||
auto exchange_qty = to_exchange_qty(*me->sec,activate_q);
|
||||
me->create_quantity_bm = encoding.overwrite(me->create_quantity_bm, exchange_qty.value(), quantity_sz());
|
||||
const auto &ts = services.get_time_str();
|
||||
me->create_transact_time_bm = encoding.overwrite(me->create_transact_time_bm, &ts.value()[0], ts.value().size());
|
||||
return OrderOpResult{};
|
||||
@@ -550,7 +557,7 @@ OrderOpResult ICENativeOrder::on_update(Price p, Quantity q)
|
||||
OrderOpResult ICENativeOrder::on_update(api::orderman::Update const &update)
|
||||
{
|
||||
auto p = Price(update.price());
|
||||
auto q = Quantity(update.quantity());
|
||||
auto update_q = Quantity(update.quantity());
|
||||
auto scoped_log = me->services->log_scope("ICENativeOrder::on_update", key().value());
|
||||
auto result = OrderOpResult{};
|
||||
|
||||
@@ -563,8 +570,8 @@ OrderOpResult ICENativeOrder::on_update(api::orderman::Update const &update)
|
||||
previous_working_qty = me->state.quantity() - me->state.filled_quantity();
|
||||
|
||||
auto current_working_qty = 0;
|
||||
if (q.value() > me->state.filled_quantity())
|
||||
current_working_qty = q.value() - me->state.filled_quantity();
|
||||
if (update_q.value() > me->state.filled_quantity())
|
||||
current_working_qty = update_q.value() - me->state.filled_quantity();
|
||||
|
||||
auto lqr = me->services->limiter().can_update_order( AccountID(me->definition.account_id()), *me->sec,
|
||||
me->definition.side(), Quantity(previous_working_qty),
|
||||
@@ -606,7 +613,8 @@ OrderOpResult ICENativeOrder::on_update(api::orderman::Update const &update)
|
||||
}
|
||||
me->update_price_bm = encoding.overwrite(me->update_price_bm, securities::encode_exchange_price(*me->sec, p).value(),
|
||||
me->price_encoding_precision, price_sz());
|
||||
me->update_quantity_bm = encoding.overwrite(me->update_quantity_bm, q.value(), quantity_sz());
|
||||
auto exchange_qty = to_exchange_qty(*me->sec,update_q);
|
||||
me->update_quantity_bm = encoding.overwrite(me->update_quantity_bm, exchange_qty.value(), quantity_sz());
|
||||
|
||||
const auto &ts = me->services->get_time_str();
|
||||
me->update_transact_time_bm = encoding.overwrite( me->update_transact_time_bm, &ts.value()[0], ts.value().size() );
|
||||
@@ -622,8 +630,8 @@ OrderOpResult ICENativeOrder::on_update(api::orderman::Update const &update)
|
||||
|
||||
LOG(OrderMan, Info,
|
||||
"ICE may not honor this update if we recently issued an unacknowledged cancelreplace {}. Update: {}@{}. Buffering",
|
||||
key().value(), q.value(), p.value());
|
||||
me->buffered_update.emplace(p, q);
|
||||
key().value(), update_q.value(), p.value());
|
||||
me->buffered_update.emplace(p, update_q);
|
||||
|
||||
// early return so we skip report_state (for speed)
|
||||
return OrderOpResult{};
|
||||
@@ -633,8 +641,8 @@ OrderOpResult ICENativeOrder::on_update(api::orderman::Update const &update)
|
||||
case ORDER_STATUS_PENDING_WORKING:
|
||||
LOG(OrderMan, Warn,
|
||||
"Cannot apply update to order {}. Update: {}@{}. Buffering",
|
||||
key().value(), q.value(), p.value());
|
||||
me->buffered_update.emplace(p, q);
|
||||
key().value(), update_q.value(), p.value());
|
||||
me->buffered_update.emplace(p, update_q);
|
||||
break;
|
||||
|
||||
case ORDER_STATUS_REJECTED_BY_SPARK:
|
||||
|
||||
@@ -12,7 +12,7 @@ using namespace bts::md::ice;
|
||||
namespace bts {
|
||||
namespace md {
|
||||
namespace ice {
|
||||
|
||||
|
||||
template <>
|
||||
double double_of_price<onixs_ice::Rational>(const onixs_ice::Rational& price, int8_t denominator)
|
||||
{
|
||||
@@ -175,8 +175,8 @@ void ICEListener::onMarketSnapshot(const onixs_ice::MarketSnapshot& msg, const o
|
||||
data->handle.update_session_high( ts, double_of_price(msg.high, deal_denominator) );
|
||||
data->handle.update_session_low( ts, double_of_price(msg.low, deal_denominator) );
|
||||
|
||||
data->handle.set_last_trade( ts, pricesize_of_price_size(msg.lastTradePrice, deal_denominator, msg.lastTradeQuantity),
|
||||
ll_of_timestamp(msg.lastTradeDateTime), msg.volume );
|
||||
data->handle.set_last_trade( ts, pricesize_of_price_size(msg.lastTradePrice, deal_denominator, msg.lastTradeQuantity,data->handle.security()),
|
||||
ll_of_timestamp(msg.lastTradeDateTime), from_exchange_qty(msg.volume,data->handle.security()) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ void ICEListener::onMarketStatistics(const onixs_ice::MarketStatistics& msg, con
|
||||
bts_assert(data->id == msg.marketId);
|
||||
auto ts = utils::TimeStamp::now();
|
||||
auto deal_denominator = data->handle.security().ice_deal_denominator;
|
||||
data->handle.set_total_volume(msg.volume);
|
||||
data->handle.set_total_volume(from_exchange_qty(msg.volume,data->handle.security()));
|
||||
data->handle.update_session_high( ts, double_of_price(msg.high, deal_denominator) );
|
||||
data->handle.update_session_low( ts, double_of_price(msg.low, deal_denominator) );
|
||||
}
|
||||
@@ -237,17 +237,17 @@ void ICEListener::onOrderBookUpdated(const onixs_ice::OrderBook& onixs_book, con
|
||||
{
|
||||
auto& bid = onixs_book.bids()[x];
|
||||
if(x < COCONUT_MAX_DEPTH)
|
||||
dbids[x] = pricesize_of_price_size(bid.price, denominator, bid.quantity - bid.impliedQuantity);
|
||||
dbids[x] = pricesize_of_price_size(bid.price, denominator, bid.quantity - bid.impliedQuantity,data->handle.security());
|
||||
if(x < COCONUT_MAX_IMPLIED_DEPTH)
|
||||
ibids[x] = pricesize_of_price_size(bid.price, denominator, bid.impliedQuantity);
|
||||
ibids[x] = pricesize_of_price_size(bid.price, denominator, bid.impliedQuantity,data->handle.security());
|
||||
}
|
||||
for (size_t x = 0; x < asks_size; ++x)
|
||||
{
|
||||
auto& ask = onixs_book.offers()[x];
|
||||
if(x < COCONUT_MAX_DEPTH)
|
||||
dasks[x] = pricesize_of_price_size(ask.price, denominator, ask.quantity - ask.impliedQuantity);
|
||||
dasks[x] = pricesize_of_price_size(ask.price, denominator, ask.quantity - ask.impliedQuantity,data->handle.security());
|
||||
if(x < COCONUT_MAX_IMPLIED_DEPTH)
|
||||
iasks[x] = pricesize_of_price_size(ask.price, denominator, ask.impliedQuantity);
|
||||
iasks[x] = pricesize_of_price_size(ask.price, denominator, ask.impliedQuantity,data->handle.security());
|
||||
}
|
||||
data->handle.update_books(TimeStamp::now(), dbids, dasks, ibids, iasks, (uint32_t) msg_info.sequenceNumber);
|
||||
}
|
||||
@@ -272,7 +272,7 @@ void on_trade(ICESecurityData* data, const onixs_ice::Price& price, int size,
|
||||
onixs_ice::AggressorSide::Enum agside, const onixs_ice::DateTime& onixs_date, bool from_spread)
|
||||
{
|
||||
auto ts = utils::TimeStamp::now();
|
||||
PriceSize ps = pricesize_of_price_size(price, data->handle.security().ice_deal_denominator, size);
|
||||
PriceSize ps = pricesize_of_price_size(price, data->handle.security().ice_deal_denominator, size,data->handle.security());
|
||||
auto date = ll_of_timestamp(onixs_date);
|
||||
data->handle.add_trade(ts, ps, date, map_aggressor(agside), 0u, from_spread);
|
||||
}
|
||||
|
||||
@@ -184,13 +184,15 @@ void ICEListener::set_security_common(const Definition& def, FixedSecurity& fsec
|
||||
double min_tick = double_of_price(def.incrementPrice, fsec.ice_price_denominator);
|
||||
fsec.low_tick = fsec.high_tick = min_tick;
|
||||
fsec.price_break = 0.0;
|
||||
|
||||
fsec.is_ice_lots = def.marketDesc.find("ICE Lots") != std::string::npos;
|
||||
// TODO
|
||||
fsec.ice_exchange_qty_mult = fsec.is_ice_lots ? 1 : 2500;
|
||||
fsec.ice_market_description(def.marketDesc);
|
||||
fsec.ice_cleared_alias(def.clearedAlias);
|
||||
fsec.ice_product_name(def.productName);
|
||||
fsec.ice_hub_alias(def.hubAlias);
|
||||
fsec.ice_strip_name(def.stripName);
|
||||
fsec.mic_code(def.micCode);
|
||||
fsec.mic_code(def.micCode);
|
||||
}
|
||||
|
||||
template<typename Definition>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "lib/utils/helper_macros.h"
|
||||
#include "lib/md/ice/ICEUtils.h"
|
||||
#include "lib/md/coconut2.h"
|
||||
#include "lib/md/Shm/FixedSecurity.h"
|
||||
|
||||
#include "onixs.h"
|
||||
|
||||
@@ -14,7 +15,13 @@
|
||||
namespace bts {
|
||||
namespace md {
|
||||
namespace ice {
|
||||
|
||||
|
||||
|
||||
inline Size from_exchange_qty (Size qty, const bts::md::FixedSecurity& sec){
|
||||
Size qty_div = sec.ice_exchange_qty_mult;
|
||||
return qty/qty_div;
|
||||
}
|
||||
|
||||
template <typename OnixPrice>
|
||||
double double_of_price(const OnixPrice& price, int8_t denominator)
|
||||
{
|
||||
@@ -32,8 +39,10 @@ IntPrice convert_price(const OnixsPrice& price, int8_t denominator)
|
||||
}
|
||||
|
||||
template <typename OnixsPrice>
|
||||
inline PriceSize pricesize_of_price_size(const OnixsPrice& price, int8_t denominator, Size size)
|
||||
{
|
||||
inline PriceSize pricesize_of_price_size(const OnixsPrice& price, int8_t denominator, Size size,
|
||||
const bts::md::FixedSecurity& sec)
|
||||
{
|
||||
size = from_exchange_qty(size,sec);
|
||||
auto intpx = convert_price<OnixsPrice>(price, denominator);
|
||||
PriceSize result({intpx.mantissa(), (int8_t)(intpx.exponent())}, size);
|
||||
return result;
|
||||
|
||||
Reference in new issue
Block a user