Files
spark/src/lib/domain_model/Security.cpp
T

583 lines
20 KiB
C++

#include "Security.h"
#include "lib/config/config.h"
#include "lib/config/protos/orders_fills_cleanup.pb.h"
#include "lib/domain_model/Security.pb.h"
#include "lib/io/JsonMacros.h"
#include "lib/md/common.h"
#include "lib/utils/pb_json_macros.h"
#include <boost/date_time/posix_time/posix_time.hpp>
#include <google/protobuf/text_format.h>
#include <nlohmann/json.hpp>
namespace bts {
namespace securities {
using bts::api::Security;
using std::optional;
api::SecurityType security_type(api::Security const &security) {
return security_type(security.Contract_case());
}
api::SecurityType security_type(api::Security::ContractCase contract_case) {
switch (contract_case) {
case api::Security::ContractCase::kFuture:
return api::SecurityType::SECURITYTYPE_FUTURE;
case api::Security::ContractCase::kOption:
return api::SecurityType::SECURITYTYPE_OPTION;
case api::Security::ContractCase::kSpot:
return api::SecurityType::SECURITYTYPE_SPOT;
case api::Security::ContractCase::kSpread:
return api::SecurityType::SECURITYTYPE_SPREAD;
case api::Security::ContractCase::kSynthetic:
return api::SecurityType::SECURITYTYPE_SYNTHETIC;
case api::Security::ContractCase::CONTRACT_NOT_SET:
return api::SecurityType::SECURITYTYPE_UNSET;
}
return api::SecurityType::SECURITYTYPE_UNSET;
}
std::optional<core::TimeStamp> smallest_leg(
::google::protobuf::RepeatedPtrField<::bts::api::ContractLeg> const &l) {
bts_assert(l.size());
auto smallest = expiration(l[0].security());
for (const auto &leg : l) {
auto this_date = expiration(leg.security());
if (this_date < smallest) {
smallest = this_date;
}
}
return smallest;
}
std::optional<core::TimeStamp> expiration(api::Security const &security) {
switch (security.Contract_case()) {
case api::Security::kFuture: {
const auto &fut = security.future();
core::TimeStamp exp;
if (core::TimeStamp::from_ymd(
fut.expiration_year(), fut.expiration_month(),
fut.expiration_day(), &exp)) {
return exp;
}
break;
}
case api::Security::kOption: {
const auto &opt = security.option();
core::TimeStamp exp;
if (core::TimeStamp::from_ymd(
opt.expiration_year(), opt.expiration_month(),
opt.expiration_day(), &exp)) {
return exp;
}
break;
}
case api::Security::kSpread:
return smallest_leg(security.spread().legs());
case api::Security::kSynthetic:
return smallest_leg(security.synthetic().legs());
default:
break;
}
return std::nullopt;
}
bool is_expired(
api::Security const &security,
core::TimeStamp now,
uint32_t sod_hh,
uint32_t sod_mm) {
auto maybe_exp = expiration(security);
if (!maybe_exp) {
return false;
}
const auto exp = *maybe_exp;
return exp < now && (exp.date() + core::TimeSpan::from_hours(sod_hh) +
core::TimeSpan::from_minutes(sod_mm)) < now;
}
bool is_expired(api::Security const &security, core::TimeStamp now) {
const auto &sod = config::get<config::SODTime>();
return is_expired(security, now, sod.sod_hh(), sod.sod_mm());
}
bool is_expired(api::Security const &security) {
return is_expired(security, core::TimeStamp::now());
}
void write_date_string(
uint32_t year, uint32_t month, uint32_t day, std::ostringstream &result) {
result << std::setw(2) << std::setfill('0') << month << "/" << std::setw(2)
<< std::setfill('0') << day << "/" << year - 2000;
}
std::ostringstream &write_leg_descriptor(
std::ostringstream &result, char separator, const api::ContractLeg &leg) {
bts_assert(leg.security().id() != md::invalid_security_key);
// Keep the string small, yet readable for diagnostic purposes.
result << (leg.side() == api::SIDE_BUY ? '+' : '-') << separator
<< leg.quantity() << "*(i=" << leg.security().id()
<< ",m=" << leg.price_multiplier() << ",d=" << leg.leg_delta() << ")";
return result;
}
std::ostringstream &write_future_descriptor(
std::ostringstream &result, const api::FutureContract &future) {
write_date_string(
future.expiration_year(), future.expiration_month(),
future.expiration_day(), result);
return result;
}
std::ostringstream &write_option_descriptor(
std::ostringstream &result,
char separator,
const api::OptionContract &option) {
write_date_string(
option.expiration_year(), option.expiration_month(),
option.expiration_day(), result);
result << separator << option.strike()
<< (option.call_put() == api::PUTORCALL_CALL ? 'C' : 'P');
return result;
}
std::ostringstream &write_legs_descriptor(
std::ostringstream &result,
char separator,
const google::protobuf::RepeatedPtrField<api::ContractLeg> &legs) {
bts_assert(legs.size() > 0);
auto it = legs.begin();
write_leg_descriptor(result, separator, *it);
while (++it != legs.end()) {
result << separator;
write_leg_descriptor(result, separator, *it);
}
return result;
}
std::string unique_descriptor(const api::Security &security) {
const char separator = ' ';
std::ostringstream result;
result << api::Venue_Name(security.exchange()).substr(6) << separator
<< api::SecurityType_Name(security_type(security)).substr(12)
<< descriptor(security);
switch (security.Contract_case()) {
case Security::ContractCase::kOption:
result << separator;
write_option_descriptor(result, separator, security.option());
break;
case Security::ContractCase::kFuture:
result << separator;
write_future_descriptor(result, security.future());
break;
case Security::ContractCase::kSpot:
break;
case Security::ContractCase::kSpread:
result << separator;
write_legs_descriptor(result, separator, security.spread().legs());
break;
case Security::ContractCase::kSynthetic: {
result << "pc="
<< api::SyntheticContract::PriceCalc_Name(
security.synthetic().price_calc());
write_legs_descriptor(result, separator, security.synthetic().legs());
break;
}
default:
bts_assert_msg(
false, "unique_descriptor failed due to invalid contract case");
break;
}
return result.str();
}
} // namespace securities
} // namespace bts
namespace bts::api {
using nlohmann::json;
using std::uint32_t;
void to_json(json &j, const api::ContractQuantityInfo &cqi) {
PB_JSON_WRITE_IF_NOT_ZERO(j, cqi, min_value);
PB_JSON_WRITE_IF_NOT_ZERO(j, cqi, max_value);
PB_JSON_WRITE_IF_NOT_ZERO(j, cqi, tick_unit);
PB_JSON_WRITE_IF_NOT_ZERO(j, cqi, tick_base);
PB_JSON_WRITE_IF_NOT_ZERO(j, cqi, multiplier);
PB_JSON_WRITE_IF_NOT_ZERO(j, cqi, max_price);
}
void from_json(const json &j, api::ContractQuantityInfo &cqi) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, cqi, min_value, it);
PB_JSON_READ_VALUE(j, cqi, max_value, it);
PB_JSON_READ_VALUE(j, cqi, max_value, it);
PB_JSON_READ_VALUE(j, cqi, tick_unit, it);
PB_JSON_READ_VALUE(j, cqi, tick_base, it);
PB_JSON_READ_VALUE(j, cqi, max_price, it);
}
void to_json(json &j, const api::ContractSpecification &spec) {
PB_JSON_WRITE_IF_NOT_ZERO(j, spec, low_tick);
PB_JSON_WRITE_IF_NOT_ZERO(j, spec, price_break);
PB_JSON_WRITE_IF_NOT_ZERO(j, spec, high_tick);
PB_JSON_WRITE_IF_NOT_ZERO(j, spec, cash_per_point);
PB_JSON_WRITE_IF_NOT_ZERO(j, spec, price_multiplier);
PB_JSON_WRITE_IF_NOT_ZERO(j, spec, main_fraction);
PB_JSON_WRITE_IF_NOT_ZERO(j, spec, sub_fraction);
PB_JSON_WRITE_IF_NOT_ZERO(j, spec, price_display_format);
PB_JSON_WRITE_IF_NOT_EMPTY(j, spec, quantity_infos);
PB_JSON_WRITE_IF_NOT_EMPTY(j, spec, notional_quantity_infos);
}
void from_json(const json &j, api::ContractSpecification &spec) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, spec, low_tick, it);
PB_JSON_READ_VALUE(j, spec, price_break, it);
PB_JSON_READ_VALUE(j, spec, high_tick, it);
PB_JSON_READ_VALUE(j, spec, cash_per_point, it);
PB_JSON_READ_VALUE(j, spec, price_multiplier, it);
PB_JSON_READ_VALUE(j, spec, main_fraction, it);
PB_JSON_READ_VALUE(j, spec, sub_fraction, it);
PB_JSON_READ_VALUE(j, spec, price_display_format, it);
PB_JSON_READ_ARRAY(j, spec, quantity_infos, it);
PB_JSON_READ_ARRAY(j, spec, notional_quantity_infos, it);
}
void to_json(json &, const SpotContract &) {}
void from_json(const json &, SpotContract &) {}
template <class ExpContract>
static void exp_to_json(json &j, const ExpContract &ec) {
j = bts::core::TimeStamp::from_ymd(
ec.expiration_year(), ec.expiration_month(), ec.expiration_day())
.to_iso_string();
}
template <class ExpContract>
static void exp_from_json(const json &j, ExpContract &ec) {
const auto dt = bts::core::TimeStamp::from_iso_string(j.get<std::string>());
uint32_t y, m, d;
dt.to_date(y, m, d);
ec.set_expiration_year(y);
ec.set_expiration_month(m);
ec.set_expiration_day(d);
}
void to_json(json &j, const FutureContract &f) {
exp_to_json(j["expiration"], f);
}
void from_json(const json &j, FutureContract &f) {
const auto it = j.find("expiration");
if (it != j.end()) {
exp_from_json(*it, f);
}
}
void to_json(json &j, const OptionContract &opt) {
exp_to_json(j["expiration"], opt);
PB_JSON_WRITE_VALUE(j, opt, strike);
PB_JSON_WRITE_VALUE(j, opt, call_put);
}
void from_json(const json &j, OptionContract &opt) {
json::const_iterator it;
it = j.find("expiration");
if (it != j.end()) {
exp_from_json(*it, opt);
}
PB_JSON_READ_VALUE(j, opt, strike, it);
PB_JSON_READ_VALUE(j, opt, call_put, it);
}
void to_json(json &j, const ContractLeg &leg) {
PB_JSON_WRITE_STRUCT(j, leg, security);
PB_JSON_WRITE_VALUE(j, leg, quantity);
PB_JSON_WRITE_VALUE(j, leg, side);
PB_JSON_WRITE_VALUE(j, leg, price_multiplier);
PB_JSON_WRITE_IF_NOT_ZERO(j, leg, leg_delta);
PB_JSON_WRITE_VALUE(j, leg, price_calc);
}
void from_json(const json &j, ContractLeg &leg) {
json::const_iterator it;
PB_JSON_READ_STRUCT(j, leg, security, it);
PB_JSON_READ_VALUE(j, leg, quantity, it);
PB_JSON_READ_VALUE(j, leg, side, it);
PB_JSON_READ_VALUE(j, leg, price_multiplier, it);
PB_JSON_READ_VALUE(j, leg, leg_delta, it);
PB_JSON_READ_VALUE(j, leg, price_calc, it);
}
void to_json(json &j, const SpreadContract &spd) {
PB_JSON_WRITE_IF_NOT_EMPTY(j, spd, legs);
}
void from_json(const json &j, SpreadContract &spd) {
json::const_iterator it;
PB_JSON_READ_ARRAY(j, spd, legs, it);
}
void to_json(json &j, const SyntheticContract &syn) {
PB_JSON_WRITE_IF_NOT_EMPTY(j, syn, name);
PB_JSON_WRITE_IF_NOT_ZERO(j, syn, user_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, syn, legs);
PB_JSON_WRITE_VALUE(j, syn, price_calc);
}
void from_json(const json &j, SyntheticContract &syn) {
json::const_iterator it;
PB_JSON_READ_STRING(j, syn, name, it);
PB_JSON_READ_VALUE(j, syn, user_id, it);
PB_JSON_READ_ARRAY(j, syn, legs, it);
PB_JSON_READ_VALUE(j, syn, price_calc, it);
}
void to_json(json &j, const GLBXExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, cfi_code);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, cme_symbol);
PB_JSON_WRITE_IF_NOT_ZERO(j, data, market_segment_id);
}
void from_json(const json &j, GLBXExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
PB_JSON_READ_STRING(j, data, cfi_code, it);
PB_JSON_READ_STRING(j, data, cme_symbol, it);
PB_JSON_READ_VALUE(j, data, market_segment_id, it);
}
void to_json(json &j, const CBOEExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
PB_JSON_WRITE_IF_NOT_ZERO(j, data, class_key);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, source);
}
void from_json(const json &j, CBOEExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
PB_JSON_READ_VALUE(j, data, class_key, it);
PB_JSON_READ_STRING(j, data, source, it);
}
void to_json(json &j, const CBOECFEExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
PB_JSON_WRITE_IF_NOT_ZERO(j, data, class_key);
}
void from_json(const json &j, CBOECFEExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
PB_JSON_READ_VALUE(j, data, class_key, it);
}
void to_json(json &j, const ICEExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
PB_JSON_WRITE_IF_NOT_ZERO(j, data, price_denominator);
PB_JSON_WRITE_IF_NOT_ZERO(j, data, deal_denominator);
PB_JSON_WRITE_IF_NOT_ZERO(j, data, settle_denominator);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, market_description);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, cleared_alias);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, product_name);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, hub_alias);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, strip_name);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, mic_code);
PB_JSON_WRITE_IF_TRUE(j, data, is_strip);
}
void from_json(const json &j, ICEExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
PB_JSON_READ_VALUE(j, data, price_denominator, it);
PB_JSON_READ_VALUE(j, data, deal_denominator, it);
PB_JSON_READ_VALUE(j, data, settle_denominator, it);
PB_JSON_READ_STRING(j, data, market_description, it);
PB_JSON_READ_STRING(j, data, cleared_alias, it);
PB_JSON_READ_STRING(j, data, product_name, it);
PB_JSON_READ_STRING(j, data, hub_alias, it);
PB_JSON_READ_STRING(j, data, strip_name, it);
PB_JSON_READ_STRING(j, data, mic_code, it);
PB_JSON_READ_VALUE(j, data, is_strip, it);
}
void to_json(json &j, const TAIFEXExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
PB_JSON_WRITE_IF_NOT_ZERO(j, data, class_key);
}
void from_json(const json &j, TAIFEXExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
PB_JSON_READ_VALUE(j, data, class_key, it);
}
void to_json(json &j, const DeribitExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
}
void from_json(const json &j, DeribitExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
}
void to_json(json &j, const FTXExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
}
void from_json(const json &j, FTXExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
}
void to_json(json &j, const BinanceExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
}
void from_json(const json &j, BinanceExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
}
void to_json(json &j, const BinanceUsdsFuturesExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
}
void from_json(const json &j, BinanceUsdsFuturesExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
}
void to_json(json &j, const BinanceCoinFuturesExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
}
void from_json(const json &j, BinanceCoinFuturesExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
}
void to_json(json &j, const BinanceUsExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
}
void from_json(const json &j, BinanceUsExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
}
void to_json(json &j, const OkxExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
}
void from_json(const json &j, OkxExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
}
void to_json(json &j, const CoinbaseExchangeData &data) {
PB_JSON_WRITE_IF_NOT_ZERO(j, data, exchange_id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, data, security_desc);
}
void from_json(const json &j, CoinbaseExchangeData &data) {
json::const_iterator it;
PB_JSON_READ_VALUE(j, data, exchange_id, it);
PB_JSON_READ_STRING(j, data, security_desc, it);
}
void to_json(json &j, const Security &s) {
// clang-format off
#define CONTRACT_ONEOF(oneof) \
oneof(j, s, Spot, spot) \
oneof(j, s, Future, future) \
oneof(j, s, Option, option) \
oneof(j, s, Spread, spread) \
oneof(j, s, Synthetic, synthetic)
#define EXCHANGE_DATA_ONEOF(oneof) \
oneof(j, s, Glbx, glbx) \
oneof(j, s, Cboe, cboe) \
oneof(j, s, Cfe, cfe) \
oneof(j, s, Ice, ice) \
oneof(j, s, Deribit, deribit) \
oneof(j, s, Ftx, ftx) \
oneof(j, s, Binance, binance) \
oneof(j, s, BinanceCoinFutures, binance_coin_futures) \
oneof(j, s, BinanceUsdsFutures, binance_usds_futures) \
oneof(j, s, BinanceUs, binance_us) \
oneof(j, s, Coinbase, coinbase) \
oneof(j, s, Okx, okx) \
oneof(j, s, Taifex, taifex)
// clang-format on
PB_JSON_WRITE_IF_NOT_ZERO(j, s, id);
PB_JSON_WRITE_IF_NOT_EMPTY(j, s, symbol);
PB_JSON_WRITE_VALUE(j, s, exchange);
PB_JSON_WRITE_STRUCT(j, s, contract_spec);
PB_JSON_WRITE_IF_NOT_EMPTY(j, s, currency);
PB_JSON_WRITE_ONEOF(j, s, Contract, CONTRACT_ONEOF);
PB_JSON_WRITE_ONEOF(j, s, ExchangeData, EXCHANGE_DATA_ONEOF);
}
void from_json(const json &j, Security &s) {
json::const_iterator it;
#define SEC_TYPE_ONEOF(oneof) \
oneof(j, s, future, it); \
oneof(j, s, option, it); \
oneof(j, s, spread, it); \
oneof(j, s, synthetic, it)
#define CONTRACT_CASE_ONEOF(oneof) \
oneof(j, s, glbx, it); \
oneof(j, s, cboe, it); \
oneof(j, s, cfe, it); \
oneof(j, s, ice, it); \
oneof(j, s, deribit, it); \
oneof(j, s, ftx, it); \
oneof(j, s, binance, it); \
oneof(j, s, binance_usds_futures, it); \
oneof(j, s, binance_coin_futures, it); \
oneof(j, s, binance_us, it); \
oneof(j, s, coinbase, it); \
oneof(j, s, okx, it); \
oneof(j, s, taifex, it);
PB_JSON_READ_VALUE(j, s, id, it);
PB_JSON_READ_STRING(j, s, symbol, it);
PB_JSON_READ_STRING(j, s, currency, it);
PB_JSON_READ_STRUCT(j, s, contract_spec, it);
PB_JSON_READ_ONEOFS(SEC_TYPE_ONEOF);
PB_JSON_READ_ONEOFS(CONTRACT_CASE_ONEOF);
}
} // namespace bts::api