Instead of domain::TradeRecord constructor. The domain types should have minimal dependency on protos.
293 lines
9.7 KiB
C++
293 lines
9.7 KiB
C++
#include "DomainSecurityUtils.h"
|
|
#include "FutureSecurity.h"
|
|
#include "OptionSecurity.h"
|
|
#include "SpreadSecurity.h"
|
|
#include "lib/api/domain/Venue.pb.h"
|
|
#include "lib/domain/TradeRecord.h"
|
|
#include "lib/domain_model/Security.pb.h"
|
|
#include "lib/md/IntPrice.h"
|
|
#include "lib/md/PriceSize.h"
|
|
#include "lib/md/protos/co2.pb.h"
|
|
#include "lib/utils/bts_assert.h"
|
|
|
|
namespace bts::domain {
|
|
|
|
#define DOMAIN_VENUE(x) bts::api::domain::VENUE_##x
|
|
#define DOMAIN_SEC_TYPE(x) bts::api::domain::SECURITY_TYPE_##x
|
|
|
|
api::domain::Venue DomainSecurityUtils::convert(bts::api::Venue venue) {
|
|
switch (venue) {
|
|
case bts::api::Venue::VENUE_GLBX:
|
|
return DOMAIN_VENUE(GLBX);
|
|
case bts::api::Venue::VENUE_DERIBIT:
|
|
return DOMAIN_VENUE(DERIBIT);
|
|
case bts::api::Venue::VENUE_CBOE:
|
|
return DOMAIN_VENUE(CBOE);
|
|
case bts::api::Venue::VENUE_TAIFEX:
|
|
return DOMAIN_VENUE(TAIFEX);
|
|
case bts::api::Venue::VENUE_BTS:
|
|
return DOMAIN_VENUE(BTS);
|
|
case bts::api::Venue::VENUE_SIM:
|
|
return DOMAIN_VENUE(SIM);
|
|
case bts::api::Venue::VENUE_BINANCE:
|
|
return DOMAIN_VENUE(BINANCE);
|
|
case bts::api::Venue::VENUE_BINANCE_COIN_FUTURES:
|
|
return DOMAIN_VENUE(BINANCE_COIN_FUTURES);
|
|
case bts::api::Venue::VENUE_BINANCE_US:
|
|
return DOMAIN_VENUE(BINANCE_US);
|
|
case bts::api::VENUE_BINANCE_USDS_FUTURES:
|
|
return DOMAIN_VENUE(BINANCE_USDS_FUTURES);
|
|
case api::VENUE_COINBASE:
|
|
return DOMAIN_VENUE(COINBASE);
|
|
case api::VENUE_ICE:
|
|
return DOMAIN_VENUE(ICE);
|
|
case bts::api::VENUE_OKX:
|
|
return DOMAIN_VENUE(OKX);
|
|
default: {
|
|
LOG(md, Error, "convert unknown venue: {}", static_cast<int>(venue));
|
|
break;
|
|
}
|
|
}
|
|
return DOMAIN_VENUE(UNSPECIFIED);
|
|
}
|
|
|
|
bts::api::Venue DomainSecurityUtils::convert(api::domain::Venue venue) {
|
|
switch (venue) {
|
|
case DOMAIN_VENUE(GLBX):
|
|
return api::Venue::VENUE_GLBX;
|
|
case DOMAIN_VENUE(DERIBIT):
|
|
return api::Venue::VENUE_DERIBIT;
|
|
case DOMAIN_VENUE(CBOE):
|
|
return api::Venue::VENUE_CBOE;
|
|
case DOMAIN_VENUE(TAIFEX):
|
|
return api::Venue::VENUE_TAIFEX;
|
|
case DOMAIN_VENUE(FTX):
|
|
return api::Venue::VENUE_FTX;
|
|
case DOMAIN_VENUE(BINANCE):
|
|
return api::Venue::VENUE_BINANCE;
|
|
case DOMAIN_VENUE(BINANCE_COIN_FUTURES):
|
|
return api::Venue::VENUE_BINANCE_COIN_FUTURES;
|
|
case DOMAIN_VENUE(BINANCE_US):
|
|
return api::Venue::VENUE_BINANCE_US;
|
|
case DOMAIN_VENUE(BINANCE_USDS_FUTURES):
|
|
return api::Venue::VENUE_BINANCE_USDS_FUTURES;
|
|
case DOMAIN_VENUE(OKX):
|
|
return api::Venue::VENUE_OKX;
|
|
case DOMAIN_VENUE(SIM):
|
|
return api::Venue::VENUE_SIM;
|
|
case DOMAIN_VENUE(COINBASE):
|
|
return api::Venue::VENUE_COINBASE;
|
|
case DOMAIN_VENUE(UNSPECIFIED):
|
|
return api::VENUE_UNSET;
|
|
default:
|
|
bts_assert(false);
|
|
return api::Venue::VENUE_UNSET;
|
|
}
|
|
}
|
|
|
|
bts::api::domain::SecurityType DomainSecurityUtils::convert(
|
|
bts::api::SecurityType sec_type) {
|
|
switch (sec_type) {
|
|
case bts::api::SecurityType::SECURITYTYPE_UNSET:
|
|
return DOMAIN_SEC_TYPE(UNSPECIFIED);
|
|
case bts::api::SecurityType::SECURITYTYPE_SPOT:
|
|
return DOMAIN_SEC_TYPE(SPOT);
|
|
case bts::api::SecurityType::SECURITYTYPE_OPTION:
|
|
return DOMAIN_SEC_TYPE(OPTION);
|
|
case bts::api::SecurityType::SECURITYTYPE_FUTURE:
|
|
return DOMAIN_SEC_TYPE(FUTURE);
|
|
case bts::api::SecurityType::SECURITYTYPE_SPREAD:
|
|
return DOMAIN_SEC_TYPE(SPREAD);
|
|
case bts::api::SecurityType::SECURITYTYPE_SYNTHETIC:
|
|
return DOMAIN_SEC_TYPE(SYNTHETIC);
|
|
default:
|
|
return DOMAIN_SEC_TYPE(UNSPECIFIED);
|
|
}
|
|
}
|
|
|
|
bts::domain::FutureSecurity DomainSecurityUtils::convert(
|
|
const bts::api::FutureContract &fut) {
|
|
bts::core::TimeStamp ts(
|
|
fut.expiration_year(), fut.expiration_month(), fut.expiration_day());
|
|
return {ts};
|
|
}
|
|
|
|
bts::domain::OptionSecurity DomainSecurityUtils::convert(
|
|
const bts::api::OptionContract &opt) {
|
|
bts::core::TimeStamp ts(
|
|
opt.expiration_year(), opt.expiration_month(), opt.expiration_day());
|
|
return {
|
|
ts, Price::from_double(opt.strike()),
|
|
opt.call_put() == bts::api::PUTORCALL_CALL
|
|
? bts::api::domain::CallPut::CALL
|
|
: bts::api::domain::CallPut::PUT};
|
|
}
|
|
|
|
bts::domain::SpreadSecurity DomainSecurityUtils::convert(
|
|
const bts::api::SpreadContract &spr) {
|
|
bts::domain::SpreadSecurity rv = {};
|
|
for (const auto &leg : spr.legs()) {
|
|
bts::domain::LegDefinition leg_d{
|
|
bts::domain::SecurityID(leg.security().id()),
|
|
bts::domain::Quantity(leg.quantity()),
|
|
leg.side() == bts::api::SIDE_BUY ? bts::domain::Side::Value::Buy
|
|
: bts::domain::Side::Value::Sell};
|
|
rv.legs.push_back(leg_d);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
bts::api::SecurityType DomainSecurityUtils::get_type(
|
|
const bts::api::Security &s) {
|
|
switch (s.Contract_case()) {
|
|
case bts::api::Security::ContractCase::CONTRACT_NOT_SET:
|
|
return bts::api::SecurityType::SECURITYTYPE_UNSET;
|
|
case bts::api::Security::ContractCase::kFuture:
|
|
return bts::api::SecurityType::SECURITYTYPE_FUTURE;
|
|
case bts::api::Security::ContractCase::kOption:
|
|
return bts::api::SecurityType::SECURITYTYPE_OPTION;
|
|
case bts::api::Security::ContractCase::kSpread:
|
|
return bts::api::SecurityType::SECURITYTYPE_SPREAD;
|
|
case bts::api::Security::ContractCase::kSynthetic:
|
|
return bts::api::SecurityType::SECURITYTYPE_SYNTHETIC;
|
|
case bts::api::Security::ContractCase::kSpot:
|
|
return bts::api::SECURITYTYPE_SPOT;
|
|
default:
|
|
bts_assert(false);
|
|
return bts::api::SecurityType::SECURITYTYPE_UNSET;
|
|
}
|
|
return bts::api::SecurityType::SECURITYTYPE_UNSET;
|
|
}
|
|
|
|
std::string DomainSecurityUtils::get_exchange_id(const bts::api::Security &s) {
|
|
std::string rv;
|
|
bts_assert(get_security_desc(s, rv));
|
|
return rv;
|
|
}
|
|
|
|
bool DomainSecurityUtils::get_security_desc(
|
|
const bts::api::Security &security, std::string &desc) {
|
|
switch (security.ExchangeData_case()) {
|
|
case bts::api::Security::ExchangeDataCase::kCboe:
|
|
desc = security.cboe().security_desc();
|
|
break;
|
|
case bts::api::Security::ExchangeDataCase::kTaifex:
|
|
desc = security.taifex().security_desc();
|
|
break;
|
|
case bts::api::Security::ExchangeDataCase::kCfe:
|
|
desc = security.cfe().security_desc();
|
|
break;
|
|
case bts::api::Security::ExchangeDataCase::kDeribit:
|
|
desc = security.deribit().security_desc();
|
|
break;
|
|
case bts::api::Security::ExchangeDataCase::kGlbx:
|
|
desc = security.glbx().security_desc();
|
|
break;
|
|
case bts::api::Security::ExchangeDataCase::kIce:
|
|
desc = security.ice().security_desc();
|
|
break;
|
|
case bts::api::Security::ExchangeDataCase::kBinance:
|
|
desc = security.binance().security_desc();
|
|
break;
|
|
case bts::api::Security::ExchangeDataCase::kBinanceCoinFutures:
|
|
desc = security.binance_coin_futures().security_desc();
|
|
break;
|
|
case bts::api::Security::ExchangeDataCase::kBinanceUs:
|
|
desc = security.binance_us().security_desc();
|
|
break;
|
|
case bts::api::Security::ExchangeDataCase::kCoinbase:
|
|
desc = security.coinbase().security_desc();
|
|
break;
|
|
case bts::api::Security::ExchangeDataCase::kBinanceUsdsFutures:
|
|
desc = security.binance_usds_futures().security_desc();
|
|
break;
|
|
case bts::api::Security::ExchangeDataCase::kOkx:
|
|
desc = security.okx().security_desc();
|
|
break;
|
|
case bts::api::Security::ExchangeDataCase::kFtx:
|
|
desc = security.ftx().security_desc();
|
|
break;
|
|
default:
|
|
if (security.has_synthetic()) {
|
|
desc = security.synthetic().name();
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
domain::Price DomainSecurityUtils::convert(md::IntPrice iprice) {
|
|
int64_t mantissa = iprice.mantissa();
|
|
int8_t exp_diff = iprice.exponent() - domain::Price::exponent();
|
|
if (exp_diff != 0) {
|
|
mantissa *= std::pow(10, exp_diff);
|
|
}
|
|
return domain::Price{mantissa};
|
|
}
|
|
|
|
BookLevel DomainSecurityUtils::convert(md::PriceSize ps) {
|
|
BookLevel rv;
|
|
convert(ps, rv);
|
|
return rv;
|
|
}
|
|
|
|
void DomainSecurityUtils::convert(md::PriceSize ps, BookLevel &res) {
|
|
res.price = convert(ps.int_price());
|
|
res.total_quantity.value() = ps.size();
|
|
res.direct_quantity.value() = ps.direct_size();
|
|
res.n_orders = 0;
|
|
}
|
|
|
|
void DomainSecurityUtils::convert(
|
|
const bts::domain::BookSide &book_side,
|
|
bts::md::PriceSize *res,
|
|
size_t res_size) {
|
|
convert(book_side.data(), book_side.size(), res, res_size);
|
|
}
|
|
|
|
void DomainSecurityUtils::convert(
|
|
const BookLevel *book_side,
|
|
size_t size,
|
|
md::PriceSize *res,
|
|
size_t res_size) {
|
|
size_t sz = std::min(size, res_size);
|
|
size_t i = 0;
|
|
for (; i < sz; i++) {
|
|
// WARNING: Implied size is discarded here. There would be data loss anyways, since
|
|
// bts::md::PriceSize's implied size is a uint, and domain::BookLevel's is a float.
|
|
res[i] = md::PriceSize{
|
|
md::IntPrice{
|
|
book_side[i].price.mantissa(), book_side[i].price.exponent()},
|
|
book_side[i].total_quantity.value()};
|
|
}
|
|
// Set remaining levels to empty.
|
|
for (; i < res_size; i++) {
|
|
res[i] = bts::md::PriceSize{};
|
|
}
|
|
}
|
|
|
|
void DomainSecurityUtils::convert(
|
|
const md::PriceSize *book_side, std::size_t len, BookSide &res) {
|
|
if (res.size() < len) {
|
|
len = res.size();
|
|
}
|
|
while (len-- != 0) {
|
|
convert(book_side[len], res[len]);
|
|
}
|
|
}
|
|
|
|
void DomainSecurityUtils::convert(
|
|
const co2::TradeMessage &tm, TradeRecord &tr) {
|
|
tr.sec_id.value() = tm.key();
|
|
tr.time_stamp = core::TimeStamp::from_nanos(tm.time());
|
|
tr.agg_side = tm.aggressor_side() == co2::AggressorSide::AGGRESSOR_SIDE_BUY
|
|
? AggressorSide::Buy
|
|
: AggressorSide::Sell;
|
|
tr.trade_quantity.value() = tm.size();
|
|
tr.trade_price =
|
|
convert(md::IntPrice{tm.mantissa(), static_cast<int8_t>(tm.exponent())});
|
|
}
|
|
|
|
} // namespace bts::domain
|