Compare commits
4
Commits
ls
...
DJ_ICE_risk
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f127ecc18 | ||
|
|
3fc080d02c | ||
|
|
d86543261c | ||
|
|
1a7fcb2027 |
No files matched your search
@@ -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,17 @@ 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 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() &&
|
||||
sec.ice().strip_name().find("/") != std::string::npos;
|
||||
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 +243,60 @@ 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 double 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
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline double 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 double 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
|
||||
double exchange_qty_ratio = 180;
|
||||
}
|
||||
|
||||
message FutureContract {
|
||||
|
||||
@@ -35,6 +35,7 @@ FixedSecurity::FixedSecurity() :
|
||||
ice_deal_denominator(0),
|
||||
ice_settle_denominator(0),
|
||||
is_ice_strip(false),
|
||||
is_ice_lots(false),
|
||||
_symbol(),
|
||||
_cfi_code(),
|
||||
_security_desc(),
|
||||
@@ -98,6 +99,19 @@ void write_leg_descriptor(std::ostringstream& result, char separator, const api:
|
||||
<< leg.leg_delta() << (leg.side() == SIDE_BUY ? "BUY" : "SELL");
|
||||
}
|
||||
|
||||
// for ice sec's it populates exchange_qty_ratio and
|
||||
// net_
|
||||
void populate_derivable_fields (api::Security& s){
|
||||
if (s.has_ice()){
|
||||
auto& ice = *s.mutable_ice();
|
||||
if(!ice.is_ice_lots())
|
||||
// TODO : get this from config
|
||||
ice.set_exchange_qty_ratio(2500);
|
||||
else
|
||||
ice.set_exchange_qty_ratio(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
std::string security_descriptor(const api::Security& security) {
|
||||
const char separator = ' ';
|
||||
std::ostringstream result;
|
||||
@@ -197,6 +211,7 @@ 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);
|
||||
break;
|
||||
case Venue::VENUE_BTS:
|
||||
break;
|
||||
@@ -253,8 +268,8 @@ 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());
|
||||
populate_derivable_fields(*presult);
|
||||
}
|
||||
|
||||
SecurityType securitytype_of_contractcase(Security::ContractCase cc) {
|
||||
@@ -323,6 +338,7 @@ 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_market_description(src.ice().market_description());
|
||||
dest.ice_cleared_alias(src.ice().cleared_alias());
|
||||
|
||||
@@ -63,6 +63,7 @@ public:
|
||||
int8_t ice_deal_denominator;
|
||||
int8_t ice_settle_denominator;
|
||||
bool is_ice_strip;
|
||||
bool is_ice_lots;
|
||||
|
||||
STRING_PROPERTY(ice_market_description);
|
||||
STRING_PROPERTY(ice_cleared_alias);
|
||||
|
||||
@@ -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,26 @@ 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_ratio(),
|
||||
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)){
|
||||
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_ratio : 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 double exchange_qty_ratio = 1;
|
||||
const double 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,10 @@ private:
|
||||
return &node;
|
||||
return nullptr;
|
||||
}
|
||||
Quantity _convert_qty (Quantity qty) const{
|
||||
auto v = qty.value();
|
||||
auto v_out =(v/_qty_norm.exchange_qty_ratio)*_qty_norm.unit_risk_qty;
|
||||
return Quantity(v_out);
|
||||
}
|
||||
};
|
||||
}}
|
||||
@@ -184,13 +184,13 @@ 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;
|
||||
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>
|
||||
|
||||
Reference in new issue
Block a user