diff --git a/README.md b/README.md index 4ce3bce1..f66bc71e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# Announcement: We have upgraded bybit API from v3 to v5 due to bybit will discontinue v3 on August 31, 2024. We've moved derivatives support from bybit-derivatives to bybit and updated documentions accordingly (See #specify-instrument-type section). We've also added websocket balance/position realtime update support on bybit. In addition, please use bybit's Unified Trading Account (https://www.bybit.com/en/help-center/article/Introduction-to-Bybit-Unified-Trading-Account) for best API support. Thank you. + **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* @@ -35,6 +37,7 @@ - [Override exchange urls](#override-exchange-urls) - [Complex request parameters](#complex-request-parameters-1) - [Send request by Websocket API](#send-request-by-websocket-api) + - [Specify instrument type](#specify-instrument-type) - [FIX API](#fix-api) - [More Advanced Topics](#more-advanced-topics) - [Handle events in "immediate" vs. "batching" mode](#handle-events-in-immediate-vs-batching-mode) @@ -56,8 +59,8 @@ * Code closely follows Bloomberg's API: https://www.bloomberg.com/professional/support/api-library/. * It is ultra fast thanks to very careful optimizations: move semantics, regex optimization, locality of reference, lock contention minimization, etc. * Supported exchanges: - * Market Data: ascendex, binance, binance-usds-futures, binance-coin-futures, binance-us, bitfinex, bitget, bitget-futures, bitmart, bitmex, bitstamp, bybit, bybit-derivatives, coinbase, cryptocom, deribit, erisx (Cboe Digital), gateio, gateio-perpetual-futures, gemini, huobi, huobi-usdt-swap, huobi-coin-swap, kraken, kraken-futures, kucoin, kucoin-futures, mexc, mexc-futures, okx, whitebit. - * Execution Management: ascendex, binance, binance-usds-futures, binance-coin-futures, binance-us, bitfinex, bitget, bitget-futures, bitmart, bitmex, bitstamp, bybit, bybit-derivatives, coinbase, cryptocom, deribit, erisx (Cboe Digital), gateio, gateio-perpetual-futures, gemini, huobi, huobi-usdt-swap, huobi-coin-swap, kraken, kraken-futures, kucoin, kucoin-futures, mexc, okx. + * Market Data: ascendex, binance, binance-usds-futures, binance-coin-futures, binance-us, bitfinex, bitget, bitget-futures, bitmart, bitmex, bitstamp, bybit, coinbase, cryptocom, deribit, erisx (Cboe Digital), gateio, gateio-perpetual-futures, gemini, huobi, huobi-usdt-swap, huobi-coin-swap, kraken, kraken-futures, kucoin, kucoin-futures, mexc, mexc-futures, okx, whitebit. + * Execution Management: ascendex, binance, binance-usds-futures, binance-coin-futures, binance-us, bitfinex, bitget, bitget-futures, bitmart, bitmex, bitstamp, bybit, coinbase, cryptocom, deribit, erisx (Cboe Digital), gateio, gateio-perpetual-futures, gemini, huobi, huobi-usdt-swap, huobi-coin-swap, kraken, kraken-futures, kucoin, kucoin-futures, mexc, okx. * FIX: coinbase, gemini. * A spot market making application is provided as an end-to-end solution for liquidity providers. * A single order execution application is provided as an end-to-end solution for executing large orders. @@ -792,6 +795,14 @@ request.appendParam({ session.sendRequestByWebsocket(request); ``` +#### Specify instrument type +Some exchanges (i.e. bybit) might need instrument type for `Subscription`. Use `Subscription`'s `setInstrumentType` method. +``` +Subscription subscription("bybit", "BTCUSDT", "MARKET_DEPTH"); +subscription.setInstrumentType("spot"); +session.subscribe(subscription); +``` + ### FIX API **Objective:** diff --git a/include/ccapi_cpp/ccapi_macro.h b/include/ccapi_cpp/ccapi_macro.h index c48f3d3a..0aa3b476 100644 --- a/include/ccapi_cpp/ccapi_macro.h +++ b/include/ccapi_cpp/ccapi_macro.h @@ -136,9 +136,6 @@ #ifndef CCAPI_EXCHANGE_NAME_BYBIT #define CCAPI_EXCHANGE_NAME_BYBIT "bybit" #endif -#ifndef CCAPI_EXCHANGE_NAME_BYBIT_DERIVATIVES -#define CCAPI_EXCHANGE_NAME_BYBIT_DERIVATIVES "bybit-derivatives" -#endif #ifndef CCAPI_EXCHANGE_NAME_ASCENDEX #define CCAPI_EXCHANGE_NAME_ASCENDEX "ascendex" #endif @@ -355,13 +352,9 @@ #define CCAPI_WEBSOCKET_CRYPTOCOM_CHANNEL_TRADE "trade.{instrument_name}" #define CCAPI_WEBSOCKET_CRYPTOCOM_CHANNEL_BOOK "book.{instrument_name}.{depth}" #define CCAPI_WEBSOCKET_BYBIT_CHANNEL_TRADE "publicTrade.{symbol}" -#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH "orderbook.{depth}.{symbol}" +#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_ORDERBOOK "orderbook.{depth}.{symbol}" #define CCAPI_WEBSOCKET_BYBIT_CHANNEL_KLINE "kline.{interval}.{symbol}" #define CCAPI_WEBSOCKET_BYBIT_CHANNEL_KLINE_2 "kline" -#define CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_TRADE "publicTrade.{symbol}" -#define CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_ORDERBOOK "orderbook.{depth}.{symbol}" -#define CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_KLINE "kline.{interval}.{symbol}" -#define CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_KLINE_2 "kline" #define CCAPI_WEBSOCKET_ASCENDEX_CHANNEL_TRADES "trades" #define CCAPI_WEBSOCKET_ASCENDEX_CHANNEL_BBO "bbo" #define CCAPI_WEBSOCKET_ASCENDEX_CHANNEL_DEPTH "depth" @@ -766,9 +759,6 @@ #ifndef CCAPI_BYBIT_URL_REST_BASE #define CCAPI_BYBIT_URL_REST_BASE "https://api.bybit.com" #endif -#ifndef CCAPI_BYBIT_DERIVATIVES_URL_REST_BASE -#define CCAPI_BYBIT_DERIVATIVES_URL_REST_BASE "https://api.bybit.com" -#endif #ifndef CCAPI_ASCENDEX_URL_REST_BASE #define CCAPI_ASCENDEX_URL_REST_BASE "https://ascendex.com" #endif @@ -895,9 +885,6 @@ #ifndef CCAPI_BYBIT_URL_WS_BASE #define CCAPI_BYBIT_URL_WS_BASE "wss://stream.bybit.com" #endif -#ifndef CCAPI_BYBIT_DERIVATIVES_URL_WS_BASE -#define CCAPI_BYBIT_DERIVATIVES_URL_WS_BASE "wss://stream.bybit.com" -#endif #ifndef CCAPI_ASCENDEX_URL_WS_BASE #define CCAPI_ASCENDEX_URL_WS_BASE "wss://ascendex.com" #endif @@ -1123,12 +1110,6 @@ #ifndef CCAPI_BYBIT_API_SECRET #define CCAPI_BYBIT_API_SECRET "BYBIT_API_SECRET" #endif -#ifndef CCAPI_BYBIT_DERIVATIVES_API_KEY -#define CCAPI_BYBIT_DERIVATIVES_API_KEY "BYBIT_DERIVATIVES_API_KEY" -#endif -#ifndef CCAPI_BYBIT_DERIVATIVES_API_SECRET -#define CCAPI_BYBIT_DERIVATIVES_API_SECRET "BYBIT_DERIVATIVES_API_SECRET" -#endif #ifndef CCAPI_ASCENDEX_API_KEY #define CCAPI_ASCENDEX_API_KEY "ASCENDEX_API_KEY" #endif diff --git a/include/ccapi_cpp/ccapi_session.h b/include/ccapi_cpp/ccapi_session.h index 32e47ab3..3b4bb4b9 100644 --- a/include/ccapi_cpp/ccapi_session.h +++ b/include/ccapi_cpp/ccapi_session.h @@ -79,9 +79,6 @@ #ifdef CCAPI_ENABLE_EXCHANGE_BYBIT #include "ccapi_cpp/service/ccapi_market_data_service_bybit.h" #endif -#ifdef CCAPI_ENABLE_EXCHANGE_BYBIT_DERIVATIVES -#include "ccapi_cpp/service/ccapi_market_data_service_bybit_derivatives.h" -#endif #ifdef CCAPI_ENABLE_EXCHANGE_ASCENDEX #include "ccapi_cpp/service/ccapi_market_data_service_ascendex.h" #endif @@ -186,9 +183,6 @@ #ifdef CCAPI_ENABLE_EXCHANGE_BYBIT #include "ccapi_cpp/service/ccapi_execution_management_service_bybit.h" #endif -#ifdef CCAPI_ENABLE_EXCHANGE_BYBIT_DERIVATIVES -#include "ccapi_cpp/service/ccapi_execution_management_service_bybit_derivatives.h" -#endif #ifdef CCAPI_ENABLE_EXCHANGE_ASCENDEX #include "ccapi_cpp/service/ccapi_execution_management_service_ascendex.h" #endif @@ -412,10 +406,6 @@ class Session { this->serviceByServiceNameExchangeMap[CCAPI_MARKET_DATA][CCAPI_EXCHANGE_NAME_BYBIT] = std::make_shared(this->internalEventHandler, sessionOptions, sessionConfigs, this->serviceContextPtr); #endif -#ifdef CCAPI_ENABLE_EXCHANGE_BYBIT_DERIVATIVES - this->serviceByServiceNameExchangeMap[CCAPI_MARKET_DATA][CCAPI_EXCHANGE_NAME_BYBIT_DERIVATIVES] = - std::make_shared(this->internalEventHandler, sessionOptions, sessionConfigs, this->serviceContextPtr); -#endif #ifdef CCAPI_ENABLE_EXCHANGE_ASCENDEX this->serviceByServiceNameExchangeMap[CCAPI_MARKET_DATA][CCAPI_EXCHANGE_NAME_ASCENDEX] = std::make_shared(this->internalEventHandler, sessionOptions, sessionConfigs, this->serviceContextPtr); @@ -550,10 +540,6 @@ class Session { this->serviceByServiceNameExchangeMap[CCAPI_EXECUTION_MANAGEMENT][CCAPI_EXCHANGE_NAME_BYBIT] = std::make_shared(this->internalEventHandler, sessionOptions, sessionConfigs, this->serviceContextPtr); #endif -#ifdef CCAPI_ENABLE_EXCHANGE_BYBIT_DERIVATIVES - this->serviceByServiceNameExchangeMap[CCAPI_EXECUTION_MANAGEMENT][CCAPI_EXCHANGE_NAME_BYBIT_DERIVATIVES] = - std::make_shared(this->internalEventHandler, sessionOptions, sessionConfigs, this->serviceContextPtr); -#endif #ifdef CCAPI_ENABLE_EXCHANGE_ASCENDEX this->serviceByServiceNameExchangeMap[CCAPI_EXECUTION_MANAGEMENT][CCAPI_EXCHANGE_NAME_ASCENDEX] = std::make_shared(this->internalEventHandler, sessionOptions, sessionConfigs, this->serviceContextPtr); @@ -636,6 +622,22 @@ class Session { } virtual void subscribe(std::vector& subscriptionList) { CCAPI_LOGGER_FUNCTION_ENTER; + for (auto& subscription : subscriptionList) { + auto exchange = subscription.getExchange(); + if (exchange == CCAPI_EXCHANGE_NAME_BYBIT) { + auto instrumentType = subscription.getInstrumentType(); + if (instrumentType.empty()) { + instrumentType = "spot"; + } + std::vector instrumentTypeList = {"spot", "linear", "inverse", "option"}; + if (std::find(instrumentTypeList.begin(), instrumentTypeList.end(), instrumentType) == instrumentTypeList.end()) { + this->onError(Event::Type::SUBSCRIPTION_STATUS, Message::Type::SUBSCRIPTION_FAILURE, + "unsupported exchange instrument types: " + toString(instrumentType) + ". Allowed values: " + toString(instrumentTypeList) + "."); + return; + } + subscription.setInstrumentType(instrumentType); + } + } std::map > subscriptionListByServiceNameMap; for (const auto& subscription : subscriptionList) { auto serviceName = subscription.getServiceName(); @@ -650,19 +652,10 @@ class Session { return; } if (serviceName == CCAPI_MARKET_DATA) { - // std::set correlationIdSet; - // std::set duplicateCorrelationIdSet; std::unordered_set unsupportedExchangeFieldSet; - std::map > subscriptionListByExchangeMap; auto exchangeFieldMap = this->sessionConfigs.getExchangeFieldMap(); CCAPI_LOGGER_DEBUG("exchangeFieldMap = " + toString(exchangeFieldMap)); for (const auto& subscription : subscriptionList) { - // auto correlationId = subscription.getCorrelationId(); - // if (correlationIdSet.find(correlationId) != correlationIdSet.end()) { - // duplicateCorrelationIdSet.insert(correlationId); - // } else { - // correlationIdSet.insert(correlationId); - // } auto exchange = subscription.getExchange(); CCAPI_LOGGER_DEBUG("exchange = " + exchange); auto field = subscription.getField(); @@ -674,29 +667,16 @@ class Session { CCAPI_LOGGER_DEBUG("unsupported exchange " + exchange + ", field = " + field); unsupportedExchangeFieldSet.insert(exchange + "|" + field); } - subscriptionListByExchangeMap[exchange].push_back(subscription); } - // if (!duplicateCorrelationIdSet.empty()) { - // this->onError(Event::Type::SUBSCRIPTION_STATUS, Message::Type::SUBSCRIPTION_FAILURE, - // "duplicated correlation ids: " + toString(duplicateCorrelationIdSet)); - // return; - // } if (!unsupportedExchangeFieldSet.empty()) { this->onError(Event::Type::SUBSCRIPTION_STATUS, Message::Type::SUBSCRIPTION_FAILURE, "unsupported exchange fields: " + toString(unsupportedExchangeFieldSet)); return; } + std::map > subscriptionListByExchangeMap; for (const auto& subscription : subscriptionList) { auto exchange = subscription.getExchange(); - if (exchange == CCAPI_EXCHANGE_NAME_BYBIT_DERIVATIVES) { - const auto& instrumentType = subscription.getInstrumentType(); - std::vector instrumentTypeList = {"usdt-contract", "usdc-contract", "usdc-options"}; - if (std::find(instrumentTypeList.begin(), instrumentTypeList.end(), instrumentType) == instrumentTypeList.end()) { - this->onError(Event::Type::SUBSCRIPTION_STATUS, Message::Type::SUBSCRIPTION_FAILURE, - "unsupported exchange instrument types: " + toString(instrumentType) + ". Allowed values: " + toString(instrumentTypeList) + "."); - return; - } - } + subscriptionListByExchangeMap[exchange].push_back(subscription); } CCAPI_LOGGER_TRACE("subscriptionListByExchangeMap = " + toString(subscriptionListByExchangeMap)); for (auto& subscriptionListByExchange : subscriptionListByExchangeMap) { diff --git a/include/ccapi_cpp/ccapi_session_configs.h b/include/ccapi_cpp/ccapi_session_configs.h index 7afac633..7ae04041 100644 --- a/include/ccapi_cpp/ccapi_session_configs.h +++ b/include/ccapi_cpp/ccapi_session_configs.h @@ -146,14 +146,9 @@ class SessionConfigs CCAPI_FINAL { }; std::map fieldWebsocketChannelMapBybit = { {CCAPI_TRADE, CCAPI_WEBSOCKET_BYBIT_CHANNEL_TRADE}, - {CCAPI_MARKET_DEPTH, CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH}, + {CCAPI_MARKET_DEPTH, CCAPI_WEBSOCKET_BYBIT_CHANNEL_ORDERBOOK}, {CCAPI_CANDLESTICK, CCAPI_WEBSOCKET_BYBIT_CHANNEL_KLINE}, }; - std::map fieldWebsocketChannelMapBybitDerivatives = { - {CCAPI_TRADE, CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_TRADE}, - {CCAPI_MARKET_DEPTH, CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_ORDERBOOK}, - {CCAPI_CANDLESTICK, CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_KLINE}, - }; std::map fieldWebsocketChannelMapAscendex = { {CCAPI_TRADE, CCAPI_WEBSOCKET_ASCENDEX_CHANNEL_TRADES}, {CCAPI_MARKET_DEPTH, CCAPI_WEBSOCKET_ASCENDEX_CHANNEL_DEPTH}, @@ -258,9 +253,6 @@ class SessionConfigs CCAPI_FINAL { for (auto const& fieldWebsocketChannel : fieldWebsocketChannelMapBybit) { this->exchangeFieldMap[CCAPI_EXCHANGE_NAME_BYBIT].push_back(fieldWebsocketChannel.first); } - for (auto const& fieldWebsocketChannel : fieldWebsocketChannelMapBybitDerivatives) { - this->exchangeFieldMap[CCAPI_EXCHANGE_NAME_BYBIT_DERIVATIVES].push_back(fieldWebsocketChannel.first); - } for (auto const& fieldWebsocketChannel : fieldWebsocketChannelMapAscendex) { this->exchangeFieldMap[CCAPI_EXCHANGE_NAME_ASCENDEX].push_back(fieldWebsocketChannel.first); } @@ -312,7 +304,6 @@ class SessionConfigs CCAPI_FINAL { {CCAPI_EXCHANGE_NAME_GATEIO_PERPETUAL_FUTURES, fieldWebsocketChannelMapGateioPerpetualFutures}, {CCAPI_EXCHANGE_NAME_CRYPTOCOM, fieldWebsocketChannelMapCryptocom}, {CCAPI_EXCHANGE_NAME_BYBIT, fieldWebsocketChannelMapBybit}, - {CCAPI_EXCHANGE_NAME_BYBIT_DERIVATIVES, fieldWebsocketChannelMapBybitDerivatives}, {CCAPI_EXCHANGE_NAME_ASCENDEX, fieldWebsocketChannelMapAscendex}, {CCAPI_EXCHANGE_NAME_BITGET, fieldWebsocketChannelMapBitget}, {CCAPI_EXCHANGE_NAME_BITGET_FUTURES, fieldWebsocketChannelMapBitgetFutures}, @@ -350,7 +341,6 @@ class SessionConfigs CCAPI_FINAL { {CCAPI_EXCHANGE_NAME_GATEIO_PERPETUAL_FUTURES, CCAPI_GATEIO_PERPETUAL_FUTURES_URL_WS_BASE}, {CCAPI_EXCHANGE_NAME_CRYPTOCOM, CCAPI_CRYPTOCOM_URL_WS_BASE}, {CCAPI_EXCHANGE_NAME_BYBIT, CCAPI_BYBIT_URL_WS_BASE}, - {CCAPI_EXCHANGE_NAME_BYBIT_DERIVATIVES, CCAPI_BYBIT_DERIVATIVES_URL_WS_BASE}, {CCAPI_EXCHANGE_NAME_ASCENDEX, CCAPI_ASCENDEX_URL_WS_BASE}, {CCAPI_EXCHANGE_NAME_BITGET, CCAPI_BITGET_URL_WS_BASE}, {CCAPI_EXCHANGE_NAME_BITGET_FUTURES, CCAPI_BITGET_FUTURES_URL_WS_BASE}, @@ -389,7 +379,6 @@ class SessionConfigs CCAPI_FINAL { {CCAPI_EXCHANGE_NAME_GATEIO_PERPETUAL_FUTURES, CCAPI_GATEIO_PERPETUAL_FUTURES_URL_REST_BASE}, {CCAPI_EXCHANGE_NAME_CRYPTOCOM, CCAPI_CRYPTOCOM_URL_REST_BASE}, {CCAPI_EXCHANGE_NAME_BYBIT, CCAPI_BYBIT_URL_REST_BASE}, - {CCAPI_EXCHANGE_NAME_BYBIT_DERIVATIVES, CCAPI_BYBIT_DERIVATIVES_URL_REST_BASE}, {CCAPI_EXCHANGE_NAME_ASCENDEX, CCAPI_ASCENDEX_URL_REST_BASE}, {CCAPI_EXCHANGE_NAME_BITGET, CCAPI_BITGET_URL_REST_BASE}, {CCAPI_EXCHANGE_NAME_BITGET_FUTURES, CCAPI_BITGET_FUTURES_URL_REST_BASE}, diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service_bybit.h b/include/ccapi_cpp/service/ccapi_execution_management_service_bybit.h index 914eb7c5..da79bb5e 100644 --- a/include/ccapi_cpp/service/ccapi_execution_management_service_bybit.h +++ b/include/ccapi_cpp/service/ccapi_execution_management_service_bybit.h @@ -2,51 +2,134 @@ #define INCLUDE_CCAPI_CPP_SERVICE_CCAPI_EXECUTION_MANAGEMENT_SERVICE_BYBIT_H_ #ifdef CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT #ifdef CCAPI_ENABLE_EXCHANGE_BYBIT -#include "ccapi_cpp/service/ccapi_execution_management_service_bybit_base.h" +#include "ccapi_cpp/service/ccapi_execution_management_service.h" namespace ccapi { -class ExecutionManagementServiceBybit : public ExecutionManagementServiceBybitBase { +class ExecutionManagementServiceBybit : public ExecutionManagementService { public: ExecutionManagementServiceBybit(std::function*)> eventHandler, SessionOptions sessionOptions, SessionConfigs sessionConfigs, ServiceContextPtr serviceContextPtr) - : ExecutionManagementServiceBybitBase(eventHandler, sessionOptions, sessionConfigs, serviceContextPtr) { + : ExecutionManagementService(eventHandler, sessionOptions, sessionConfigs, serviceContextPtr) { this->exchangeName = CCAPI_EXCHANGE_NAME_BYBIT; - this->baseUrlWs = sessionConfigs.getUrlWebsocketBase().at(this->exchangeName) + "/spot/private/v3"; + this->baseUrlWs = sessionConfigs.getUrlWebsocketBase().at(this->exchangeName) + "/v5/private"; this->baseUrlRest = sessionConfigs.getUrlRestBase().at(this->exchangeName); this->setHostRestFromUrlRest(this->baseUrlRest); this->setHostWsFromUrlWs(this->baseUrlWs); - // try { - // this->tcpResolverResultsRest = this->resolver.resolve(this->hostRest, this->portRest); - // } catch (const std::exception& e) { - // CCAPI_LOGGER_FATAL(std::string("e.what() = ") + e.what()); - // } - // #ifdef CCAPI_LEGACY_USE_WEBSOCKETPP - // #else - // try { - // this->tcpResolverResultsWs = this->resolverWs.resolve(this->hostWs, this->portWs); - // } catch (const std::exception& e) { - // CCAPI_LOGGER_FATAL(std::string("e.what() = ") + e.what()); - // } - // #endif this->apiKeyName = CCAPI_BYBIT_API_KEY; this->apiSecretName = CCAPI_BYBIT_API_SECRET; this->setupCredential({this->apiKeyName, this->apiSecretName}); - this->createOrderTarget = "/spot/v3/private/order"; - this->cancelOrderTarget = "/spot/v3/private/cancel-order"; - this->getOrderTarget = "/spot/v3/private/order"; - this->getOpenOrdersTarget = "/spot/v3/private/open-orders"; - this->cancelOpenOrdersTarget = "/spot/v3/private/cancel-orders"; - this->getAccountBalancesTarget = "/spot/v3/private/account"; + this->createOrderTarget = "/v5/order/create"; + this->cancelOrderTarget = "/v5/order/cancel"; + this->getOrderTarget = "/v5/order/realtime"; + this->getOpenOrdersTarget = "/v5/order/realtime"; + this->cancelOpenOrdersTarget = "/v5/order/cancel-all"; + this->getAccountBalancesTarget = "/v5/account/wallet-balance"; + this->getAccountPositionsTarget = "/v5/position/list"; } virtual ~ExecutionManagementServiceBybit() {} #ifndef CCAPI_EXPOSE_INTERNAL protected: #endif + bool doesHttpBodyContainError(const std::string& body) override { return body.find(R"("retCode":0)") == std::string::npos; } + + void pingOnApplicationLevel(std::shared_ptr wsConnectionPtr, ErrorCode& ec) override { this->send(wsConnectionPtr, R"({"op":"ping"})", ec); } + + void signReqeustForRestGenericPrivateRequest(http::request& req, const Request& request, std::string& methodString, + std::string& headerString, std::string& path, std::string& queryString, std::string& body, const TimePoint& now, + const std::map& credential) override { + auto apiKey = mapGetWithDefault(credential, this->apiKeyName); + auto apiSecret = mapGetWithDefault(credential, this->apiSecretName); + auto preSignedText = req.base().at("X-BAPI-TIMESTAMP").to_string(); + preSignedText += apiKey; + preSignedText += req.base().at("X-BAPI-RECV-WINDOW").to_string(); + std::string aString; + if (methodString == "GET") { + aString = queryString; + } else if (methodString == "POST") { + aString = body; + } + preSignedText += aString; + auto signature = Hmac::hmac(Hmac::ShaVersion::SHA256, apiSecret, preSignedText, true); + req.set("X-BAPI-SIGN", signature); + } + void signRequest(http::request& req, const std::string aString, const TimePoint& now, + const std::map& credential) { + auto apiKey = mapGetWithDefault(credential, this->apiKeyName); + auto apiSecret = mapGetWithDefault(credential, this->apiSecretName); + auto preSignedText = req.base().at("X-BAPI-TIMESTAMP").to_string(); + preSignedText += apiKey; + preSignedText += req.base().at("X-BAPI-RECV-WINDOW").to_string(); + preSignedText += aString; + auto signature = Hmac::hmac(Hmac::ShaVersion::SHA256, apiSecret, preSignedText, true); + req.set("X-BAPI-SIGN", signature); + } + void appendParamToQueryString(std::string& queryString, const std::map& param, + const std::map standardizationMap = { + {CCAPI_EM_CLIENT_ORDER_ID, "orderLinkId"}, + {CCAPI_EM_ORDER_ID, "orderId"}, + {CCAPI_SETTLE_ASSET, "settleCoin"}, + {CCAPI_EM_ACCOUNT_TYPE, "accountType"}, + }) { + for (const auto& kv : param) { + queryString += standardizationMap.find(kv.first) != standardizationMap.end() ? standardizationMap.at(kv.first) : kv.first; + queryString += "="; + queryString += Url::urlEncode(kv.second); + queryString += "&"; + } + } + + void prepareReq(http::request& req, const TimePoint& now, const std::map& credential) { + auto apiKey = mapGetWithDefault(credential, this->apiKeyName); + req.set("X-BAPI-API-KEY", apiKey); + req.set("X-BAPI-TIMESTAMP", std::to_string(std::chrono::duration_cast(now.time_since_epoch()).count())); + req.set("X-BAPI-RECV-WINDOW", std::to_string(CCAPI_BYBIT_BASE_API_RECEIVE_WINDOW_MILLISECONDS)); + req.set("X-Referer", CCAPI_BYBIT_API_BROKER_ID); + } + std::vector createSendStringListFromSubscription(const WsConnection& wsConnection, const Subscription& subscription, const TimePoint& now, + const std::map& credential) override { + std::vector sendStringList; + rj::Document document; + document.SetObject(); + auto& allocator = document.GetAllocator(); + document.AddMember("op", rj::Value("auth").Move(), allocator); + auto apiKey = mapGetWithDefault(credential, this->apiKeyName); + auto expires = std::chrono::duration_cast( + (now + std::chrono::milliseconds(CCAPI_BYBIT_BASE_API_RECEIVE_WINDOW_MILLISECONDS)).time_since_epoch()) + .count(); + auto apiSecret = mapGetWithDefault(credential, this->apiSecretName); + std::string preSignedText = "GET"; + preSignedText += "/realtime"; + preSignedText += std::to_string(expires); + auto signature = Hmac::hmac(Hmac::ShaVersion::SHA256, apiSecret, preSignedText, true); + rj::Value args(rj::kArrayType); + args.PushBack(rj::Value(apiKey.c_str(), allocator).Move(), allocator); + args.PushBack(rj::Value(expires).Move(), allocator); + args.PushBack(rj::Value(signature.c_str(), allocator).Move(), allocator); + document.AddMember("args", args, allocator); + rj::StringBuffer stringBuffer; + rj::Writer writer(stringBuffer); + document.Accept(writer); + std::string sendString = stringBuffer.GetString(); + sendStringList.push_back(sendString); + return sendStringList; + } + + void onTextMessage(std::shared_ptr wsConnectionPtr, const Subscription& subscription, boost::beast::string_view textMessageView, + const TimePoint& timeReceived) override { + std::string textMessage(textMessageView); + rj::Document document; + document.Parse(textMessage.c_str()); + Event event = this->createEvent(wsConnectionPtr, subscription, textMessageView, document, timeReceived); + if (!event.getMessageList().empty()) { + this->eventHandler(event, nullptr); + } + } + void appendParam(rj::Value& rjValue, rj::Document::AllocatorType& allocator, const std::map& param, const std::map standardizationMap = { {CCAPI_EM_ORDER_SIDE, "side"}, - {CCAPI_EM_ORDER_QUANTITY, "orderQty"}, - {CCAPI_EM_ORDER_LIMIT_PRICE, "orderPrice"}, + {CCAPI_EM_ORDER_QUANTITY, "qty"}, + {CCAPI_EM_ORDER_LIMIT_PRICE, "price"}, {CCAPI_EM_CLIENT_ORDER_ID, "orderLinkId"}, {CCAPI_EM_ORDER_ID, "orderId"}, {CCAPI_LIMIT, "limit"}, @@ -58,11 +141,7 @@ class ExecutionManagementServiceBybit : public ExecutionManagementServiceBybitBa value = value == CCAPI_EM_ORDER_SIDE_BUY ? "Buy" : "Sell"; } if (value != "null") { - if (key == "orderCategory") { - rjValue.AddMember(rj::Value(key.c_str(), allocator).Move(), std::stoi(value), allocator); - } else { - rjValue.AddMember(rj::Value(key.c_str(), allocator).Move(), rj::Value(value.c_str(), allocator).Move(), allocator); - } + rjValue.AddMember(rj::Value(key.c_str(), allocator).Move(), rj::Value(value.c_str(), allocator).Move(), allocator); } } } @@ -82,8 +161,11 @@ class ExecutionManagementServiceBybit : public ExecutionManagementServiceBybitBa document.SetObject(); rj::Document::AllocatorType& allocator = document.GetAllocator(); this->appendParam(document, allocator, param); + if (param.find("category") == param.end()) { + document.AddMember("category", rj::Value("spot").Move(), allocator); + } if (param.find("orderType") == param.end()) { - document.AddMember("orderType", rj::Value("LIMIT").Move(), allocator); + document.AddMember("orderType", rj::Value("Limit").Move(), allocator); } if (!symbolId.empty()) { this->appendSymbolId(document, allocator, symbolId, "symbol"); @@ -105,6 +187,12 @@ class ExecutionManagementServiceBybit : public ExecutionManagementServiceBybitBa document.SetObject(); rj::Document::AllocatorType& allocator = document.GetAllocator(); this->appendParam(document, allocator, param); + if (param.find("category") == param.end()) { + document.AddMember("category", rj::Value("spot").Move(), allocator); + } + if (!symbolId.empty()) { + this->appendSymbolId(document, allocator, symbolId, "symbol"); + } rj::StringBuffer stringBuffer; rj::Writer writer(stringBuffer); document.Accept(writer); @@ -118,6 +206,9 @@ class ExecutionManagementServiceBybit : public ExecutionManagementServiceBybitBa std::string queryString; const std::map param = request.getFirstParamWithDefault(); this->appendParamToQueryString(queryString, param); + if (param.find("category") == param.end()) { + queryString += "category=spot&"; + } req.target(this->getOrderTarget + "?" + queryString); this->signRequest(req, queryString, now, credential); } break; @@ -126,6 +217,9 @@ class ExecutionManagementServiceBybit : public ExecutionManagementServiceBybitBa std::string queryString; const std::map param = request.getFirstParamWithDefault(); this->appendParamToQueryString(queryString, param); + if (param.find("category") == param.end()) { + queryString += "category=spot&"; + } if (!symbolId.empty()) { this->appendSymbolId(queryString, symbolId, "symbol"); } @@ -141,6 +235,9 @@ class ExecutionManagementServiceBybit : public ExecutionManagementServiceBybitBa document.SetObject(); rj::Document::AllocatorType& allocator = document.GetAllocator(); this->appendParam(document, allocator, param); + if (param.find("category") == param.end()) { + document.AddMember("category", rj::Value("spot").Move(), allocator); + } if (!symbolId.empty()) { this->appendSymbolId(document, allocator, symbolId, "symbol"); } @@ -155,8 +252,20 @@ class ExecutionManagementServiceBybit : public ExecutionManagementServiceBybitBa case Request::Operation::GET_ACCOUNT_BALANCES: { req.method(http::verb::get); std::string queryString; - this->appendParamToQueryString(queryString, {}); - req.target(this->getAccountBalancesTarget); + const std::map param = request.getFirstParamWithDefault(); + this->appendParamToQueryString(queryString, param); + if (param.find("accountType") == param.end()) { + queryString += "accountType=UNIFIED&"; + } + req.target(this->getAccountBalancesTarget + "?" + queryString); + this->signRequest(req, queryString, now, credential); + } break; + case Request::Operation::GET_ACCOUNT_POSITIONS: { + req.method(http::verb::get); + std::string queryString; + const std::map param = request.getFirstParamWithDefault(); + this->appendParamToQueryString(queryString, param); + req.target(this->getAccountPositionsTarget + "?" + queryString); this->signRequest(req, queryString, now, credential); } break; default: @@ -169,14 +278,14 @@ class ExecutionManagementServiceBybit : public ExecutionManagementServiceBybitBa {CCAPI_EM_ORDER_ID, std::make_pair("orderId", JsonDataType::STRING)}, {CCAPI_EM_CLIENT_ORDER_ID, std::make_pair("orderLinkId", JsonDataType::STRING)}, {CCAPI_EM_ORDER_SIDE, std::make_pair("side", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_QUANTITY, std::make_pair("orderQty", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_LIMIT_PRICE, std::make_pair("orderPrice", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, std::make_pair("execQty", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_CUMULATIVE_FILLED_PRICE_TIMES_QUANTITY, std::make_pair("cummulativeQuoteQty", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_STATUS, std::make_pair("status", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_QUANTITY, std::make_pair("qty", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_LIMIT_PRICE, std::make_pair("price", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, std::make_pair("cumExecQty", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_CUMULATIVE_FILLED_PRICE_TIMES_QUANTITY, std::make_pair("cumExecValue", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_STATUS, std::make_pair("orderStatus", JsonDataType::STRING)}, {CCAPI_EM_ORDER_INSTRUMENT, std::make_pair("symbol", JsonDataType::STRING)}, }; - if (operation == Request::Operation::GET_OPEN_ORDERS) { + if (operation == Request::Operation::GET_OPEN_ORDERS || operation == Request::Operation::GET_ORDER || operation == Request::Operation::CANCEL_OPEN_ORDERS) { for (const auto& x : document["result"]["list"].GetArray()) { Element element; this->extractOrderInfo(element, x, extractionFieldNameMap); @@ -192,11 +301,22 @@ class ExecutionManagementServiceBybit : public ExecutionManagementServiceBybitBa const rj::Document& document) override { switch (request.getOperation()) { case Request::Operation::GET_ACCOUNT_BALANCES: { - for (const auto& x : document["result"]["balances"].GetArray()) { + for (const auto& x : document["result"]["list"][0]["coin"].GetArray()) { Element element; element.insert(CCAPI_EM_ASSET, x["coin"].GetString()); - element.insert(CCAPI_EM_QUANTITY_TOTAL, x["total"].GetString()); - element.insert(CCAPI_EM_QUANTITY_AVAILABLE_FOR_TRADING, x["free"].GetString()); + element.insert(CCAPI_EM_QUANTITY_TOTAL, x["walletBalance"].GetString()); + element.insert(CCAPI_EM_QUANTITY_AVAILABLE_FOR_TRADING, x["equity"].GetString()); + elementList.emplace_back(std::move(element)); + } + } break; + case Request::Operation::GET_ACCOUNT_POSITIONS: { + for (const auto& x : document["result"]["list"].GetArray()) { + Element element; + element.insert(CCAPI_INSTRUMENT, x["symbol"].GetString()); + element.insert(CCAPI_EM_POSITION_SIDE, x["side"].GetString()); + element.insert(CCAPI_EM_POSITION_QUANTITY, x["size"].GetString()); + element.insert(CCAPI_EM_POSITION_ENTRY_PRICE, x["avgPrice"].GetString()); + element.insert(CCAPI_EM_POSITION_LEVERAGE, x["leverage"].GetString()); elementList.emplace_back(std::move(element)); } } break; @@ -204,32 +324,57 @@ class ExecutionManagementServiceBybit : public ExecutionManagementServiceBybitBa CCAPI_LOGGER_FATAL(CCAPI_UNSUPPORTED_VALUE); } } -#ifdef CCAPI_LEGACY_USE_WEBSOCKETPP - Event createEvent(const WsConnection& wsConnection, wspp::connection_hdl hdl, const Subscription& subscription, const std::string& textMessage, - const rj::Document& document, const TimePoint& timeReceived) override{ -#else + Event createEvent(const std::shared_ptr wsConnectionPtr, const Subscription& subscription, boost::beast::string_view textMessageView, - const rj::Document& document, const TimePoint& timeReceived) override { + const rj::Document& document, const TimePoint& timeReceived) { std::string textMessage(textMessageView); -#endif - Event event; - std::vector messageList; - Message message; - message.setTimeReceived(timeReceived); - message.setCorrelationIdList({subscription.getCorrelationId()}); - const auto& fieldSet = subscription.getFieldSet(); - const auto& instrumentSet = subscription.getInstrumentSet(); - if (document.HasMember("topic")) { - std::string topic = document["topic"].GetString(); - if (topic == "order") { - event.setType(Event::Type::SUBSCRIPTION_DATA); - const rj::Value& data = document["data"]; - for (const auto& x : data.GetArray()) { - std::string instrument = x["s"].GetString(); - if (instrumentSet.empty() || instrumentSet.find(instrument) != instrumentSet.end()) { - auto time = TimePoint(std::chrono::milliseconds(std::stoll(x["E"].GetString()))); - auto itTradeId = x.FindMember("t"); - if (itTradeId != x.MemberEnd() && !itTradeId->value.IsNull() && fieldSet.find(CCAPI_EM_PRIVATE_TRADE) != fieldSet.end()) { + Event event; + std::vector messageList; + Message message; + message.setTimeReceived(timeReceived); + message.setCorrelationIdList({subscription.getCorrelationId()}); + const auto& fieldSet = subscription.getFieldSet(); + const auto& instrumentSet = subscription.getInstrumentSet(); + if (document.HasMember("topic")) { + std::string topic = document["topic"].GetString(); + if (topic.rfind("order", 0) == 0) { + event.setType(Event::Type::SUBSCRIPTION_DATA); + const rj::Value& data = document["data"]; + for (const auto& x : data.GetArray()) { + std::string instrument = x["symbol"].GetString(); + if (instrumentSet.empty() || instrumentSet.find(instrument) != instrumentSet.end()) { + auto time = TimePoint(std::chrono::milliseconds(std::stoll(x["updatedTime"].GetString()))); + Message message; + message.setTimeReceived(timeReceived); + message.setCorrelationIdList({subscription.getCorrelationId()}); + message.setTime(time); + message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_ORDER_UPDATE); + const std::map >& extractionFieldNameMap = { + {CCAPI_EM_ORDER_ID, std::make_pair("orderId", JsonDataType::STRING)}, + {CCAPI_EM_CLIENT_ORDER_ID, std::make_pair("orderLinkId", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_SIDE, std::make_pair("side", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_LIMIT_PRICE, std::make_pair("price", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_QUANTITY, std::make_pair("qty", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, std::make_pair("cumExecQty", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_CUMULATIVE_FILLED_PRICE_TIMES_QUANTITY, std::make_pair("cumExecValue", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_STATUS, std::make_pair("orderStatus", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_INSTRUMENT, std::make_pair("symbol", JsonDataType::STRING)}, + }; + Element info; + this->extractOrderInfo(info, x, extractionFieldNameMap); + std::vector elementList; + elementList.emplace_back(std::move(info)); + message.setElementList(elementList); + messageList.emplace_back(std::move(message)); + } + } + } else if (topic.rfind("execution", 0) == 0) { + event.setType(Event::Type::SUBSCRIPTION_DATA); + const rj::Value& data = document["data"]; + for (const auto& x : data.GetArray()) { + std::string instrument = x["symbol"].GetString(); + if (instrumentSet.empty() || instrumentSet.find(instrument) != instrumentSet.end()) { + auto time = TimePoint(std::chrono::milliseconds(std::stoll(x["execTime"].GetString()))); Message message; message.setTimeReceived(timeReceived); message.setCorrelationIdList({subscription.getCorrelationId()}); @@ -237,106 +382,113 @@ class ExecutionManagementServiceBybit : public ExecutionManagementServiceBybitBa message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_PRIVATE_TRADE); std::vector elementList; Element element; - element.insert(CCAPI_TRADE_ID, itTradeId->value.GetString()); - element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_PRICE, std::string(x["L"].GetString())); - element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_SIZE, std::string(x["l"].GetString())); - element.insert(CCAPI_EM_ORDER_SIDE, std::string(x["S"].GetString()) == "BUY" ? CCAPI_EM_ORDER_SIDE_BUY : CCAPI_EM_ORDER_SIDE_SELL); - element.insert(CCAPI_IS_MAKER, x["m"].GetBool() ? "1" : "0"); - element.insert(CCAPI_EM_ORDER_ID, std::string(x["i"].GetString())); - element.insert(CCAPI_EM_CLIENT_ORDER_ID, std::string(x["c"].GetString())); + element.insert(CCAPI_TRADE_ID, x["execId"].GetString()); + element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_PRICE, std::string(x["execPrice"].GetString())); + element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_SIZE, std::string(x["execQty"].GetString())); + element.insert(CCAPI_EM_ORDER_SIDE, std::string(x["side"].GetString()) == "Buy" ? CCAPI_EM_ORDER_SIDE_BUY : CCAPI_EM_ORDER_SIDE_SELL); + element.insert(CCAPI_IS_MAKER, x["isMaker"].GetBool() ? "1" : "0"); + element.insert(CCAPI_EM_ORDER_ID, std::string(x["orderId"].GetString())); + element.insert(CCAPI_EM_CLIENT_ORDER_ID, std::string(x["orderLinkId"].GetString())); element.insert(CCAPI_EM_ORDER_INSTRUMENT, instrument); { - auto it = x.FindMember("n"); + auto it = x.FindMember("execFee"); if (it != x.MemberEnd() && !it->value.IsNull()) { element.insert(CCAPI_EM_ORDER_FEE_QUANTITY, std::string(it->value.GetString())); } } - { - auto it = x.FindMember("N"); - if (it != x.MemberEnd() && !it->value.IsNull()) { - element.insert(CCAPI_EM_ORDER_FEE_ASSET, std::string(it->value.GetString())); - } - } elementList.emplace_back(std::move(element)); message.setElementList(elementList); messageList.emplace_back(std::move(message)); } - if (fieldSet.find(CCAPI_EM_ORDER_UPDATE) != fieldSet.end()) { + } + } else if (topic == "wallet") { + event.setType(Event::Type::SUBSCRIPTION_DATA); + const rj::Value& data = document["data"]; + for (const auto& x : data[0]["coin"].GetArray()) { + Message message; + message.setTimeReceived(timeReceived); + message.setCorrelationIdList({subscription.getCorrelationId()}); + message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_BALANCE_UPDATE); + std::vector elementList; + Element element; + element.insert(CCAPI_EM_ASSET, x["coin"].GetString()); + element.insert(CCAPI_EM_QUANTITY_TOTAL, x["walletBalance"].GetString()); + element.insert(CCAPI_EM_QUANTITY_AVAILABLE_FOR_TRADING, x["equity"].GetString()); + elementList.emplace_back(std::move(element)); + message.setElementList(elementList); + messageList.emplace_back(std::move(message)); + } + } else if (topic.rfind("position", 0) == 0) { + event.setType(Event::Type::SUBSCRIPTION_DATA); + const rj::Value& data = document["data"]; + for (const auto& x : data.GetArray()) { + std::string instrument = x["symbol"].GetString(); + if (instrumentSet.empty() || instrumentSet.find(instrument) != instrumentSet.end()) { + auto time = TimePoint(std::chrono::milliseconds(std::stoll(x["updatedTime"].GetString()))); Message message; message.setTimeReceived(timeReceived); message.setCorrelationIdList({subscription.getCorrelationId()}); message.setTime(time); - message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_ORDER_UPDATE); - const std::map >& extractionFieldNameMap = { - {CCAPI_EM_ORDER_ID, std::make_pair("i", JsonDataType::STRING)}, - {CCAPI_EM_CLIENT_ORDER_ID, std::make_pair("c", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_SIDE, std::make_pair("S", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_LIMIT_PRICE, std::make_pair("p", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_QUANTITY, std::make_pair("q", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, std::make_pair("z", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_CUMULATIVE_FILLED_PRICE_TIMES_QUANTITY, std::make_pair("Z", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_STATUS, std::make_pair("X", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_INSTRUMENT, std::make_pair("s", JsonDataType::STRING)}, - }; - Element info; - this->extractOrderInfo(info, x, extractionFieldNameMap); + message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_POSITION_UPDATE); std::vector elementList; - elementList.emplace_back(std::move(info)); + Element element; + element.insert(CCAPI_INSTRUMENT, x["symbol"].GetString()); + element.insert(CCAPI_EM_POSITION_SIDE, x["side"].GetString()); + element.insert(CCAPI_EM_POSITION_QUANTITY, x["size"].GetString()); + element.insert(CCAPI_EM_POSITION_ENTRY_PRICE, x["entryPrice"].GetString()); + element.insert(CCAPI_EM_POSITION_LEVERAGE, x["leverage"].GetString()); + elementList.emplace_back(std::move(element)); message.setElementList(elementList); messageList.emplace_back(std::move(message)); } } } - } - } else if (document.HasMember("op")) { - std::string op = document["op"].GetString(); - if (op == "auth") { - bool success = document["success"].GetBool(); - if (success) { - rj::Document document; - document.SetObject(); - rj::Document::AllocatorType& allocator = document.GetAllocator(); - document.AddMember("op", rj::Value("subscribe").Move(), allocator); - rj::Value args(rj::kArrayType); - args.PushBack(rj::Value("order").Move(), allocator); - document.AddMember("args", args, allocator); - rj::StringBuffer stringBuffer; - rj::Writer writer(stringBuffer); - document.Accept(writer); - std::string sendString = stringBuffer.GetString(); - ErrorCode ec; -#ifdef CCAPI_LEGACY_USE_WEBSOCKETPP - this->send(hdl, sendString, wspp::frame::opcode::text, ec); -#else + } else if (document.HasMember("op")) { + std::string op = document["op"].GetString(); + if (op == "auth") { + bool success = document["success"].GetBool(); + if (success) { + rj::Document document; + document.SetObject(); + rj::Document::AllocatorType& allocator = document.GetAllocator(); + document.AddMember("op", rj::Value("subscribe").Move(), allocator); + rj::Value args(rj::kArrayType); + if (fieldSet.find(CCAPI_EM_ORDER_UPDATE) != fieldSet.end()) { + args.PushBack(rj::Value(("order." + subscription.getInstrumentType()).c_str(), allocator).Move(), allocator); + } + if (fieldSet.find(CCAPI_EM_PRIVATE_TRADE) != fieldSet.end()) { + args.PushBack(rj::Value(("execution." + subscription.getInstrumentType()).c_str(), allocator).Move(), allocator); + } + if (fieldSet.find(CCAPI_EM_POSITION_UPDATE) != fieldSet.end() && subscription.getInstrumentType() != "spot") { + args.PushBack(rj::Value(("position." + subscription.getInstrumentType()).c_str(), allocator).Move(), allocator); + } + if (fieldSet.find(CCAPI_EM_BALANCE_UPDATE) != fieldSet.end()) { + args.PushBack(rj::Value("wallet").Move(), allocator); + } + document.AddMember("args", args, allocator); + rj::StringBuffer stringBuffer; + rj::Writer writer(stringBuffer); + document.Accept(writer); + std::string sendString = stringBuffer.GetString(); + ErrorCode ec; this->send(wsConnectionPtr, sendString, ec); -#endif - if (ec) { - this->onError(Event::Type::SUBSCRIPTION_STATUS, Message::Type::SUBSCRIPTION_FAILURE, ec, "subscribe"); + if (ec) { + this->onError(Event::Type::SUBSCRIPTION_STATUS, Message::Type::SUBSCRIPTION_FAILURE, ec, "subscribe"); + } } + } else if (op == "subscribe") { + bool success = document["success"].GetBool(); + event.setType(Event::Type::SUBSCRIPTION_STATUS); + message.setType(success ? Message::Type::SUBSCRIPTION_STARTED : Message::Type::SUBSCRIPTION_FAILURE); + Element element; + element.insert(success ? CCAPI_INFO_MESSAGE : CCAPI_ERROR_MESSAGE, textMessage); + message.setElementList({element}); + messageList.emplace_back(std::move(message)); } - } else if (op == "subscribe") { - bool success = document["success"].GetBool(); - event.setType(Event::Type::SUBSCRIPTION_STATUS); - message.setType(success ? Message::Type::SUBSCRIPTION_STARTED : Message::Type::SUBSCRIPTION_FAILURE); - Element element; - element.insert(success ? CCAPI_INFO_MESSAGE : CCAPI_ERROR_MESSAGE, textMessage); - message.setElementList({element}); - messageList.emplace_back(std::move(message)); - } - } else if (document.HasMember("ret_code")) { - std::string ret_code = document["ret_code"].GetString(); - if (ret_code != "0") { - event.setType(Event::Type::SUBSCRIPTION_STATUS); - message.setType(Message::Type::SUBSCRIPTION_FAILURE); - Element element; - element.insert(CCAPI_ERROR_MESSAGE, textMessage); - message.setElementList({element}); - messageList.emplace_back(std::move(message)); } + event.setMessageList(messageList); + return event; } - event.setMessageList(messageList); - return event; -} }; // namespace ccapi } /* namespace ccapi */ #endif diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service_bybit_base.h b/include/ccapi_cpp/service/ccapi_execution_management_service_bybit_base.h deleted file mode 100644 index 3314a959..00000000 --- a/include/ccapi_cpp/service/ccapi_execution_management_service_bybit_base.h +++ /dev/null @@ -1,140 +0,0 @@ -#ifndef INCLUDE_CCAPI_CPP_SERVICE_CCAPI_EXECUTION_MANAGEMENT_SERVICE_BYBIT_BASE_H_ -#define INCLUDE_CCAPI_CPP_SERVICE_CCAPI_EXECUTION_MANAGEMENT_SERVICE_BYBIT_BASE_H_ -#ifdef CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT -#if defined(CCAPI_ENABLE_EXCHANGE_BYBIT) || defined(CCAPI_ENABLE_EXCHANGE_BYBIT_DERIVATIVES) -#include "ccapi_cpp/service/ccapi_execution_management_service.h" -namespace ccapi { -class ExecutionManagementServiceBybitBase : public ExecutionManagementService { - public: - ExecutionManagementServiceBybitBase(std::function*)> eventHandler, SessionOptions sessionOptions, SessionConfigs sessionConfigs, - ServiceContextPtr serviceContextPtr) - : ExecutionManagementService(eventHandler, sessionOptions, sessionConfigs, serviceContextPtr) {} - virtual ~ExecutionManagementServiceBybitBase() {} -#ifndef CCAPI_EXPOSE_INTERNAL - - protected: -#endif - bool doesHttpBodyContainError(const std::string& body) override { return body.find(R"("retCode":0)") == std::string::npos; } -#ifdef CCAPI_LEGACY_USE_WEBSOCKETPP - void pingOnApplicationLevel(wspp::connection_hdl hdl, ErrorCode& ec) override { this->send(hdl, R"({"op":"ping"})", wspp::frame::opcode::text, ec); } -#else - void pingOnApplicationLevel(std::shared_ptr wsConnectionPtr, ErrorCode& ec) override { this->send(wsConnectionPtr, R"({"op":"ping"})", ec); } -#endif - void signReqeustForRestGenericPrivateRequest(http::request& req, const Request& request, std::string& methodString, - std::string& headerString, std::string& path, std::string& queryString, std::string& body, const TimePoint& now, - const std::map& credential) override { - auto apiKey = mapGetWithDefault(credential, this->apiKeyName); - auto apiSecret = mapGetWithDefault(credential, this->apiSecretName); - auto preSignedText = req.base().at("X-BAPI-TIMESTAMP").to_string(); - preSignedText += apiKey; - preSignedText += req.base().at("X-BAPI-RECV-WINDOW").to_string(); - std::string aString; - if (methodString == "GET") { - aString = queryString; - } else if (methodString == "POST") { - aString = body; - } - preSignedText += aString; - auto signature = Hmac::hmac(Hmac::ShaVersion::SHA256, apiSecret, preSignedText, true); - req.set("X-BAPI-SIGN", signature); - } - void signRequest(http::request& req, const std::string aString, const TimePoint& now, - const std::map& credential) { - auto apiKey = mapGetWithDefault(credential, this->apiKeyName); - auto apiSecret = mapGetWithDefault(credential, this->apiSecretName); - auto preSignedText = req.base().at("X-BAPI-TIMESTAMP").to_string(); - preSignedText += apiKey; - preSignedText += req.base().at("X-BAPI-RECV-WINDOW").to_string(); - preSignedText += aString; - auto signature = Hmac::hmac(Hmac::ShaVersion::SHA256, apiSecret, preSignedText, true); - req.set("X-BAPI-SIGN", signature); - } - void appendParamToQueryString(std::string& queryString, const std::map& param, - const std::map standardizationMap = {}) { - for (const auto& kv : param) { - queryString += standardizationMap.find(kv.first) != standardizationMap.end() ? standardizationMap.at(kv.first) : kv.first; - queryString += "="; - queryString += Url::urlEncode(kv.second); - queryString += "&"; - } - } - // void appendSymbolId(std::string& queryString, const std::string& symbolId) { - // queryString += "symbol="; - // queryString += Url::urlEncode(symbolId); - // queryString += "&"; - // } - // void appendSymbolId(rj::Value& rjValue, rj::Document::AllocatorType& allocator, const std::string& symbolId) { - // rjValue.AddMember("symbol", rj::Value(symbolId.c_str(), allocator).Move(), allocator); - // } - void prepareReq(http::request& req, const TimePoint& now, const std::map& credential) { - auto apiKey = mapGetWithDefault(credential, this->apiKeyName); - req.set("X-BAPI-SIGN-TYPE", "2"); - req.set("X-BAPI-API-KEY", apiKey); - req.set("X-BAPI-TIMESTAMP", std::to_string(std::chrono::duration_cast(now.time_since_epoch()).count())); - req.set("X-BAPI-RECV-WINDOW", std::to_string(CCAPI_BYBIT_BASE_API_RECEIVE_WINDOW_MILLISECONDS)); - req.set("Referer", CCAPI_BYBIT_API_BROKER_ID); - } - std::vector createSendStringListFromSubscription(const WsConnection& wsConnection, const Subscription& subscription, const TimePoint& now, - const std::map& credential) override { - std::vector sendStringList; - rj::Document document; - document.SetObject(); - auto& allocator = document.GetAllocator(); - document.AddMember("op", rj::Value("auth").Move(), allocator); - auto apiKey = mapGetWithDefault(credential, this->apiKeyName); - auto expires = std::chrono::duration_cast( - (now + std::chrono::milliseconds(CCAPI_BYBIT_BASE_API_RECEIVE_WINDOW_MILLISECONDS)).time_since_epoch()) - .count(); - auto apiSecret = mapGetWithDefault(credential, this->apiSecretName); - std::string preSignedText = "GET"; - preSignedText += "/realtime"; - preSignedText += std::to_string(expires); - auto signature = Hmac::hmac(Hmac::ShaVersion::SHA256, apiSecret, preSignedText, true); - rj::Value args(rj::kArrayType); - args.PushBack(rj::Value(apiKey.c_str(), allocator).Move(), allocator); - args.PushBack(rj::Value(expires).Move(), allocator); - args.PushBack(rj::Value(signature.c_str(), allocator).Move(), allocator); - document.AddMember("args", args, allocator); - rj::StringBuffer stringBuffer; - rj::Writer writer(stringBuffer); - document.Accept(writer); - std::string sendString = stringBuffer.GetString(); - sendStringList.push_back(sendString); - return sendStringList; - } -#ifdef CCAPI_LEGACY_USE_WEBSOCKETPP - void onTextMessage(wspp::connection_hdl hdl, const std::string& textMessage, const TimePoint& timeReceived) override { - WsConnection& wsConnection = this->getWsConnectionFromConnectionPtr(this->serviceContextPtr->tlsClientPtr->get_con_from_hdl(hdl)); - auto subscription = wsConnection.subscriptionList.at(0); - rj::Document document; - document.Parse(textMessage.c_str()); - Event event = this->createEvent(wsConnection, hdl, subscription, textMessage, document, timeReceived); - if (!event.getMessageList().empty()) { - this->eventHandler(event, nullptr); - } - } - virtual Event createEvent(const WsConnection& wsConnection, wspp::connection_hdl hdl, const Subscription& subscription, const std::string& textMessage, - const rj::Document& document, const TimePoint& timeReceived) { - return {}; - } -#else - void onTextMessage(std::shared_ptr wsConnectionPtr, const Subscription& subscription, boost::beast::string_view textMessageView, - const TimePoint& timeReceived) override { - std::string textMessage(textMessageView); - rj::Document document; - document.Parse(textMessage.c_str()); - Event event = this->createEvent(wsConnectionPtr, subscription, textMessageView, document, timeReceived); - if (!event.getMessageList().empty()) { - this->eventHandler(event, nullptr); - } - } - virtual Event createEvent(std::shared_ptr wsConnectionPtr, const Subscription& subscription, boost::beast::string_view textMessageView, - const rj::Document& document, const TimePoint& timeReceived) { - return {}; - } -#endif -}; -} /* namespace ccapi */ -#endif -#endif -#endif // INCLUDE_CCAPI_CPP_SERVICE_CCAPI_EXECUTION_MANAGEMENT_SERVICE_BYBIT_BASE_H_ diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service_bybit_derivatives.h b/include/ccapi_cpp/service/ccapi_execution_management_service_bybit_derivatives.h deleted file mode 100644 index 35584e91..00000000 --- a/include/ccapi_cpp/service/ccapi_execution_management_service_bybit_derivatives.h +++ /dev/null @@ -1,390 +0,0 @@ -#ifndef INCLUDE_CCAPI_CPP_SERVICE_CCAPI_EXECUTION_MANAGEMENT_SERVICE_BYBIT_DERIVATIVES_H_ -#define INCLUDE_CCAPI_CPP_SERVICE_CCAPI_EXECUTION_MANAGEMENT_SERVICE_BYBIT_DERIVATIVES_H_ -#ifdef CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT -#ifdef CCAPI_ENABLE_EXCHANGE_BYBIT_DERIVATIVES -#include "ccapi_cpp/service/ccapi_execution_management_service_bybit_base.h" -namespace ccapi { -class ExecutionManagementServiceBybitDerivatives : public ExecutionManagementServiceBybitBase { - public: - ExecutionManagementServiceBybitDerivatives(std::function*)> eventHandler, SessionOptions sessionOptions, - SessionConfigs sessionConfigs, ServiceContextPtr serviceContextPtr) - : ExecutionManagementServiceBybitBase(eventHandler, sessionOptions, sessionConfigs, serviceContextPtr) { - this->exchangeName = CCAPI_EXCHANGE_NAME_BYBIT_DERIVATIVES; - this->baseUrlWs = sessionConfigs.getUrlWebsocketBase().at(this->exchangeName) + "/unified/private/v3"; - this->baseUrlRest = sessionConfigs.getUrlRestBase().at(this->exchangeName); - this->setHostRestFromUrlRest(this->baseUrlRest); - this->setHostWsFromUrlWs(this->baseUrlWs); - // try { - // this->tcpResolverResultsRest = this->resolver.resolve(this->hostRest, this->portRest); - // } catch (const std::exception& e) { - // CCAPI_LOGGER_FATAL(std::string("e.what() = ") + e.what()); - // } - // #ifdef CCAPI_LEGACY_USE_WEBSOCKETPP - // #else - // try { - // this->tcpResolverResultsWs = this->resolverWs.resolve(this->hostWs, this->portWs); - // } catch (const std::exception& e) { - // CCAPI_LOGGER_FATAL(std::string("e.what() = ") + e.what()); - // } - // #endif - this->apiKeyName = CCAPI_BYBIT_DERIVATIVES_API_KEY; - this->apiSecretName = CCAPI_BYBIT_DERIVATIVES_API_SECRET; - this->setupCredential({this->apiKeyName, this->apiSecretName}); - this->createOrderTarget = "/unified/v3/private/order/create"; - this->cancelOrderTarget = "/unified/v3/private/order/cancel"; - this->getOrderTarget = "/unified/v3/private/order/list"; - this->getOpenOrdersTarget = "/unified/v3/private/order/unfilled-orders"; - this->cancelOpenOrdersTarget = "/unified/v3/private/order/cancel-all"; - this->getAccountBalancesTarget = "/unified/v3/private/account/wallet/balance"; - this->getAccountPositionsTarget = "/unified/v3/private/position/list"; - } - virtual ~ExecutionManagementServiceBybitDerivatives() {} -#ifndef CCAPI_EXPOSE_INTERNAL - - protected: -#endif - void appendParam(rj::Value& rjValue, rj::Document::AllocatorType& allocator, const std::map& param, - const std::map standardizationMap = { - {CCAPI_EM_ORDER_SIDE, "side"}, - {CCAPI_EM_ORDER_QUANTITY, "qty"}, - {CCAPI_EM_ORDER_LIMIT_PRICE, "price"}, - {CCAPI_EM_CLIENT_ORDER_ID, "orderLinkId"}, - {CCAPI_EM_ORDER_ID, "orderId"}, - {CCAPI_LIMIT, "limit"}, - {CCAPI_INSTRUMENT_TYPE, "category"}, - }) { - for (const auto& kv : param) { - auto key = standardizationMap.find(kv.first) != standardizationMap.end() ? standardizationMap.at(kv.first) : kv.first; - auto value = kv.second; - if (key == "side") { - value = value == CCAPI_EM_ORDER_SIDE_BUY ? "Buy" : "Sell"; - } - if (value != "null") { - if (key == "reduceOnly" || key == "closeOnTrigger" || key == "mmp") { - rjValue.AddMember(rj::Value(key.c_str(), allocator).Move(), value == "true", allocator); - } else { - rjValue.AddMember(rj::Value(key.c_str(), allocator).Move(), rj::Value(value.c_str(), allocator).Move(), allocator); - } - } - } - } - void convertRequestForRest(http::request& req, const Request& request, const TimePoint& now, const std::string& symbolId, - const std::map& credential) override { - this->prepareReq(req, now, credential); - switch (request.getOperation()) { - case Request::Operation::GENERIC_PRIVATE_REQUEST: { - ExecutionManagementService::convertRequestForRestGenericPrivateRequest(req, request, now, symbolId, credential); - } break; - case Request::Operation::CREATE_ORDER: { - req.method(http::verb::post); - req.set(beast::http::field::content_type, "application/json"); - const std::map param = request.getFirstParamWithDefault(); - req.target(this->createOrderTarget); - rj::Document document; - document.SetObject(); - rj::Document::AllocatorType& allocator = document.GetAllocator(); - this->appendParam(document, allocator, param); - if (param.find("orderType") == param.end()) { - document.AddMember("orderType", rj::Value("Limit").Move(), allocator); - } - if (!symbolId.empty()) { - this->appendSymbolId(document, allocator, symbolId, "symbol"); - } - rj::StringBuffer stringBuffer; - rj::Writer writer(stringBuffer); - document.Accept(writer); - auto body = stringBuffer.GetString(); - req.body() = body; - req.prepare_payload(); - this->signRequest(req, body, now, credential); - } break; - case Request::Operation::CANCEL_ORDER: { - req.method(http::verb::post); - req.set(beast::http::field::content_type, "application/json"); - const std::map param = request.getFirstParamWithDefault(); - req.target(this->cancelOrderTarget); - rj::Document document; - document.SetObject(); - rj::Document::AllocatorType& allocator = document.GetAllocator(); - this->appendParam(document, allocator, param); - this->appendSymbolId(document, allocator, symbolId, "symbol"); - rj::StringBuffer stringBuffer; - rj::Writer writer(stringBuffer); - document.Accept(writer); - auto body = stringBuffer.GetString(); - req.body() = body; - req.prepare_payload(); - this->signRequest(req, body, now, credential); - } break; - case Request::Operation::GET_ORDER: { - req.method(http::verb::get); - std::string queryString; - const std::map param = request.getFirstParamWithDefault(); - this->appendParamToQueryString(queryString, param, - { - {CCAPI_EM_CLIENT_ORDER_ID, "orderLinkId"}, - {CCAPI_EM_ORDER_ID, "orderId"}, - {CCAPI_LIMIT, "limit"}, - {CCAPI_INSTRUMENT_TYPE, "category"}, - }); - req.target(this->getOrderTarget + "?" + queryString); - this->signRequest(req, queryString, now, credential); - } break; - case Request::Operation::GET_OPEN_ORDERS: { - req.method(http::verb::get); - std::string queryString; - const std::map param = request.getFirstParamWithDefault(); - this->appendParamToQueryString(queryString, param, - { - {CCAPI_EM_CLIENT_ORDER_ID, "orderLinkId"}, - {CCAPI_EM_ORDER_ID, "orderId"}, - {CCAPI_LIMIT, "limit"}, - {CCAPI_INSTRUMENT_TYPE, "category"}, - }); - if (!symbolId.empty()) { - this->appendSymbolId(queryString, symbolId, "symbol"); - } - req.target(this->getOpenOrdersTarget + "?" + queryString); - this->signRequest(req, queryString, now, credential); - } break; - case Request::Operation::CANCEL_OPEN_ORDERS: { - req.method(http::verb::post); - req.set(beast::http::field::content_type, "application/json"); - const std::map param = request.getFirstParamWithDefault(); - req.target(this->cancelOpenOrdersTarget); - rj::Document document; - document.SetObject(); - rj::Document::AllocatorType& allocator = document.GetAllocator(); - this->appendParam(document, allocator, param); - if (!symbolId.empty()) { - this->appendSymbolId(document, allocator, symbolId, "symbol"); - } - rj::StringBuffer stringBuffer; - rj::Writer writer(stringBuffer); - document.Accept(writer); - auto body = stringBuffer.GetString(); - req.body() = body; - req.prepare_payload(); - this->signRequest(req, body, now, credential); - } break; - case Request::Operation::GET_ACCOUNT_BALANCES: { - req.method(http::verb::get); - std::string queryString; - this->appendParamToQueryString(queryString, {}); - req.target(queryString.empty() ? this->getAccountBalancesTarget : this->getAccountBalancesTarget + "?" + queryString); - this->signRequest(req, queryString, now, credential); - } break; - case Request::Operation::GET_ACCOUNT_POSITIONS: { - req.method(http::verb::get); - std::string queryString; - const std::map param = request.getFirstParamWithDefault(); - this->appendParamToQueryString(queryString, param); - req.target(this->getAccountPositionsTarget + "?" + queryString); - this->signRequest(req, queryString, now, credential); - } break; - default: - this->convertRequestForRestCustom(req, request, now, symbolId, credential); - } - } - void extractOrderInfoFromRequest(std::vector& elementList, const Request& request, const Request::Operation operation, - const rj::Document& document) override { - std::map > extractionFieldNameMap = { - {CCAPI_EM_ORDER_ID, std::make_pair("orderId", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_SIDE, std::make_pair("side", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_QUANTITY, std::make_pair("qty", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_LIMIT_PRICE, std::make_pair("price", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, std::make_pair("cumExecQty", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_CUMULATIVE_FILLED_PRICE_TIMES_QUANTITY, std::make_pair("cumExecValue", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_REMAINING_QUANTITY, std::make_pair("leavesQty", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_STATUS, std::make_pair("orderStatus", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_INSTRUMENT, std::make_pair("symbol", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_CUMULATIVE_FEE_QUANTITY, std::make_pair("cumExecFee", JsonDataType::STRING)}, - }; - if (operation == Request::Operation::GET_ORDER || operation == Request::Operation::GET_OPEN_ORDERS || operation == Request::Operation::CANCEL_OPEN_ORDERS) { - for (const auto& x : document["result"]["list"].GetArray()) { - Element element; - this->extractOrderInfo(element, x, extractionFieldNameMap); - elementList.emplace_back(std::move(element)); - } - } else { - Element element; - this->extractOrderInfo(element, document["result"], extractionFieldNameMap); - elementList.emplace_back(std::move(element)); - } - } - void extractAccountInfoFromRequest(std::vector& elementList, const Request& request, const Request::Operation operation, - const rj::Document& document) override { - switch (request.getOperation()) { - case Request::Operation::GET_ACCOUNT_BALANCES: { - for (const auto& x : document["result"]["coin"].GetArray()) { - Element element; - element.insert(CCAPI_EM_ASSET, x["currencyCoin"].GetString()); - element.insert(CCAPI_EM_QUANTITY_TOTAL, x["walletBalance"].GetString()); - element.insert(CCAPI_EM_QUANTITY_AVAILABLE_FOR_TRADING, x["availableBalance"].GetString()); - elementList.emplace_back(std::move(element)); - } - } break; - case Request::Operation::GET_ACCOUNT_POSITIONS: { - for (const auto& x : document["result"]["list"].GetArray()) { - Element element; - element.insert(CCAPI_INSTRUMENT, x["symbol"].GetString()); - element.insert(CCAPI_EM_POSITION_SIDE, x["side"].GetString()); - element.insert(CCAPI_EM_POSITION_QUANTITY, x["size"].GetString()); - element.insert(CCAPI_EM_POSITION_ENTRY_PRICE, x["entryPrice"].GetString()); - element.insert(CCAPI_EM_POSITION_LEVERAGE, x["leverage"].GetString()); - elementList.emplace_back(std::move(element)); - } - } break; - default: - CCAPI_LOGGER_FATAL(CCAPI_UNSUPPORTED_VALUE); - } - } -#ifdef CCAPI_LEGACY_USE_WEBSOCKETPP - Event createEvent(const WsConnection& wsConnection, wspp::connection_hdl hdl, const Subscription& subscription, const std::string& textMessage, - const rj::Document& document, const TimePoint& timeReceived) override{ -#else - Event createEvent(const std::shared_ptr wsConnectionPtr, const Subscription& subscription, boost::beast::string_view textMessageView, - const rj::Document& document, const TimePoint& timeReceived) override { - std::string textMessage(textMessageView); -#endif - Event event; - std::vector messageList; - Message message; - message.setTimeReceived(timeReceived); - message.setCorrelationIdList({subscription.getCorrelationId()}); - const auto& fieldSet = subscription.getFieldSet(); - const auto& instrumentSet = subscription.getInstrumentSet(); - if (document.HasMember("topic")) { - std::string topic = document["topic"].GetString(); - if (topic == "user.order.unifiedAccount") { - if (fieldSet.find(CCAPI_EM_ORDER_UPDATE) != fieldSet.end()) { - event.setType(Event::Type::SUBSCRIPTION_DATA); - const rj::Value& data = document["data"]["result"]; - for (const auto& x : data.GetArray()) { - std::string instrument = x["symbol"].GetString(); - if (instrumentSet.empty() || instrumentSet.find(instrument) != instrumentSet.end()) { - Message message; - message.setTimeReceived(timeReceived); - message.setCorrelationIdList({subscription.getCorrelationId()}); - message.setTime(TimePoint(std::chrono::milliseconds(std::stoll(x["updatedTime"].GetString())))); - message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_ORDER_UPDATE); - const std::map >& extractionFieldNameMap = { - {CCAPI_EM_ORDER_ID, std::make_pair("orderId", JsonDataType::INTEGER)}, - {CCAPI_EM_CLIENT_ORDER_ID, std::make_pair("orderLinkId", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_SIDE, std::make_pair("side", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_LIMIT_PRICE, std::make_pair("price", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_QUANTITY, std::make_pair("qty", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, std::make_pair("cumExecQty", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_CUMULATIVE_FILLED_PRICE_TIMES_QUANTITY, std::make_pair("cumExecValue", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_STATUS, std::make_pair("orderStatus", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_INSTRUMENT, std::make_pair("symbol", JsonDataType::STRING)}, - }; - Element info; - this->extractOrderInfo(info, x, extractionFieldNameMap); - std::vector elementList; - elementList.emplace_back(std::move(info)); - message.setElementList(elementList); - messageList.emplace_back(std::move(message)); - } - } - } - } else if (topic == "user.execution.unifiedAccount") { - if (fieldSet.find(CCAPI_EM_PRIVATE_TRADE) != fieldSet.end()) { - event.setType(Event::Type::SUBSCRIPTION_DATA); - const rj::Value& data = document["data"]["result"]; - for (const auto& x : data.GetArray()) { - std::string execType = x["execType"].GetString(); - if (execType == "TRADE") { - std::string instrument = x["symbol"].GetString(); - if (instrumentSet.empty() || instrumentSet.find(instrument) != instrumentSet.end()) { - Message message; - message.setTimeReceived(timeReceived); - message.setCorrelationIdList({subscription.getCorrelationId()}); - message.setTime(TimePoint(std::chrono::milliseconds(std::stoll(x["execTime"].GetString())))); - message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_PRIVATE_TRADE); - std::vector elementList; - Element element; - element.insert(CCAPI_TRADE_ID, x["execId"].GetString()); - element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_PRICE, x["execPrice"].GetString()); - element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_SIZE, x["execQty"].GetString()); - element.insert(CCAPI_EM_ORDER_SIDE, std::string(x["side"].GetString()) == "Buy" ? CCAPI_EM_ORDER_SIDE_BUY : CCAPI_EM_ORDER_SIDE_SELL); - element.insert(CCAPI_IS_MAKER, std::string(x["lastLiquidityInd"].GetString()) == "MAKER" ? "1" : "0"); - element.insert(CCAPI_EM_ORDER_ID, x["orderId"].GetString()); - element.insert(CCAPI_EM_CLIENT_ORDER_ID, x["orderLinkId"].GetString()); - element.insert(CCAPI_EM_ORDER_INSTRUMENT, instrument); - { - auto it = x.FindMember("execFee"); - if (it != x.MemberEnd() && !it->value.IsNull()) { - element.insert(CCAPI_EM_ORDER_FEE_QUANTITY, std::string(it->value.GetString())); - } - } - elementList.emplace_back(std::move(element)); - message.setElementList(elementList); - messageList.emplace_back(std::move(message)); - } - } - } - } - } - } else if (document.HasMember("type")) { - std::string type = document["type"].GetString(); - if (type == "AUTH_RESP") { - bool success = document["success"].GetBool(); - if (success) { - rj::Document document; - document.SetObject(); - rj::Document::AllocatorType& allocator = document.GetAllocator(); - document.AddMember("op", rj::Value("subscribe").Move(), allocator); - rj::Value args(rj::kArrayType); - for (const auto& field : subscription.getFieldSet()) { - std::string channelId; - if (field == CCAPI_EM_ORDER_UPDATE) { - channelId = "user.order.unifiedAccount"; - } else if (field == CCAPI_EM_PRIVATE_TRADE) { - channelId = "user.execution.unifiedAccount"; - } - args.PushBack(rj::Value(channelId.c_str(), allocator).Move(), allocator); - } - document.AddMember("args", args, allocator); - rj::StringBuffer stringBuffer; - rj::Writer writer(stringBuffer); - document.Accept(writer); - std::string sendString = stringBuffer.GetString(); - ErrorCode ec; -#ifdef CCAPI_LEGACY_USE_WEBSOCKETPP - this->send(hdl, sendString, wspp::frame::opcode::text, ec); -#else - this->send(wsConnectionPtr, sendString, ec); -#endif - if (ec) { - this->onError(Event::Type::SUBSCRIPTION_STATUS, Message::Type::SUBSCRIPTION_FAILURE, ec, "subscribe"); - } - } - } else if (type == "COMMAND_RESP") { - bool success = document["success"].GetBool(); - event.setType(Event::Type::SUBSCRIPTION_STATUS); - message.setType(success ? Message::Type::SUBSCRIPTION_STARTED : Message::Type::SUBSCRIPTION_FAILURE); - Element element; - element.insert(success ? CCAPI_INFO_MESSAGE : CCAPI_ERROR_MESSAGE, textMessage); - message.setElementList({element}); - messageList.emplace_back(std::move(message)); - } - } else if (document.HasMember("ret_code")) { - std::string ret_code = document["ret_code"].GetString(); - if (ret_code != "0") { - event.setType(Event::Type::SUBSCRIPTION_STATUS); - message.setType(Message::Type::SUBSCRIPTION_FAILURE); - Element element; - element.insert(CCAPI_ERROR_MESSAGE, textMessage); - message.setElementList({element}); - messageList.emplace_back(std::move(message)); - } - } - event.setMessageList(messageList); - return event; -} -}; // namespace ccapi -} /* namespace ccapi */ -#endif -#endif -#endif // INCLUDE_CCAPI_CPP_SERVICE_CCAPI_EXECUTION_MANAGEMENT_SERVICE_BYBIT_DERIVATIVES_H_ diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service_okx.h b/include/ccapi_cpp/service/ccapi_execution_management_service_okx.h index 6650ffc2..54d7ec97 100644 --- a/include/ccapi_cpp/service/ccapi_execution_management_service_okx.h +++ b/include/ccapi_cpp/service/ccapi_execution_management_service_okx.h @@ -411,22 +411,36 @@ class ExecutionManagementServiceOkx : public ExecutionManagementService { rj::Value args(rj::kArrayType); const auto& fieldSet = subscription.getFieldSet(); const auto& instrumentSet = subscription.getInstrumentSet(); - - for (const auto& instrument : instrumentSet) { + if (instrumentSet.empty()) { if (fieldSet.find(CCAPI_EM_ORDER_UPDATE) != fieldSet.end() || fieldSet.find(CCAPI_EM_PRIVATE_TRADE) != fieldSet.end()) { rj::Value arg(rj::kObjectType); arg.AddMember("channel", rj::Value("orders").Move(), allocator); - arg.AddMember("instId", rj::Value(instrument.c_str(), allocator).Move(), allocator); arg.AddMember("instType", rj::Value("ANY").Move(), allocator); args.PushBack(arg, allocator); } if (fieldSet.find(CCAPI_EM_POSITION_UPDATE) != fieldSet.end()) { rj::Value arg(rj::kObjectType); arg.AddMember("channel", rj::Value("positions").Move(), allocator); - arg.AddMember("instId", rj::Value(instrument.c_str(), allocator).Move(), allocator); arg.AddMember("instType", rj::Value("ANY").Move(), allocator); args.PushBack(arg, allocator); } + } else { + for (const auto& instrument : instrumentSet) { + if (fieldSet.find(CCAPI_EM_ORDER_UPDATE) != fieldSet.end() || fieldSet.find(CCAPI_EM_PRIVATE_TRADE) != fieldSet.end()) { + rj::Value arg(rj::kObjectType); + arg.AddMember("channel", rj::Value("orders").Move(), allocator); + arg.AddMember("instId", rj::Value(instrument.c_str(), allocator).Move(), allocator); + arg.AddMember("instType", rj::Value("ANY").Move(), allocator); + args.PushBack(arg, allocator); + } + if (fieldSet.find(CCAPI_EM_POSITION_UPDATE) != fieldSet.end()) { + rj::Value arg(rj::kObjectType); + arg.AddMember("channel", rj::Value("positions").Move(), allocator); + arg.AddMember("instId", rj::Value(instrument.c_str(), allocator).Move(), allocator); + arg.AddMember("instType", rj::Value("ANY").Move(), allocator); + args.PushBack(arg, allocator); + } + } } if (fieldSet.find(CCAPI_EM_BALANCE_UPDATE) != fieldSet.end()) { rj::Value arg(rj::kObjectType); @@ -505,65 +519,62 @@ class ExecutionManagementServiceOkx : public ExecutionManagementService { std::string channel = std::string(arg["channel"].GetString()); event.setType(Event::Type::SUBSCRIPTION_DATA); if (channel == "orders") { - std::string instrument = arg["instId"].GetString(); - if (instrumentSet.empty() || instrumentSet.find(instrument) != instrumentSet.end()) { - if (fieldSet.find(CCAPI_EM_PRIVATE_TRADE) != fieldSet.end()) { - for (const auto& x : data.GetArray()) { - std::string tradeId = x["tradeId"].GetString(); - if (!tradeId.empty()) { - Message message; - message.setTimeReceived(timeReceived); - message.setCorrelationIdList({subscription.getCorrelationId()}); - message.setTime(UtilTime::makeTimePointFromMilliseconds(std::stoll(std::string(x["fillTime"].GetString())))); - message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_PRIVATE_TRADE); - std::vector elementList; - Element element; - element.insert(CCAPI_TRADE_ID, tradeId); - element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_PRICE, std::string(x["fillPx"].GetString())); - element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_SIZE, std::string(x["fillSz"].GetString())); - element.insert(CCAPI_EM_ORDER_SIDE, std::string(x["side"].GetString()) == "buy" ? CCAPI_EM_ORDER_SIDE_BUY : CCAPI_EM_ORDER_SIDE_SELL); - element.insert(CCAPI_EM_POSITION_SIDE, std::string(x["posSide"].GetString())); - element.insert(CCAPI_IS_MAKER, std::string(x["execType"].GetString()) == "M" ? "1" : "0"); - element.insert(CCAPI_EM_ORDER_ID, std::string(x["ordId"].GetString())); - element.insert(CCAPI_EM_CLIENT_ORDER_ID, std::string(x["clOrdId"].GetString())); - element.insert(CCAPI_EM_ORDER_INSTRUMENT, instrument); - element.insert(CCAPI_EM_ORDER_FEE_QUANTITY, std::string(x["fillFee"].GetString())); - element.insert(CCAPI_EM_ORDER_FEE_ASSET, std::string(x["fillFeeCcy"].GetString())); - elementList.emplace_back(std::move(element)); - message.setElementList(elementList); - messageList.emplace_back(std::move(message)); - } - } - } - if (fieldSet.find(CCAPI_EM_ORDER_UPDATE) != fieldSet.end()) { - for (const auto& x : data.GetArray()) { + if (fieldSet.find(CCAPI_EM_PRIVATE_TRADE) != fieldSet.end()) { + for (const auto& x : data.GetArray()) { + std::string tradeId = x["tradeId"].GetString(); + if (!tradeId.empty()) { Message message; message.setTimeReceived(timeReceived); message.setCorrelationIdList({subscription.getCorrelationId()}); - message.setTime(UtilTime::makeTimePointFromMilliseconds(std::stoll(std::string(x["uTime"].GetString())))); - message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_ORDER_UPDATE); - const std::map>& extractionFieldNameMap = { - {CCAPI_EM_ORDER_ID, std::make_pair("ordId", JsonDataType::STRING)}, - {CCAPI_EM_CLIENT_ORDER_ID, std::make_pair("clOrdId", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_SIDE, std::make_pair("side", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_LIMIT_PRICE, std::make_pair("px", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_QUANTITY, std::make_pair("sz", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, std::make_pair("accFillSz", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_AVERAGE_FILLED_PRICE, std::make_pair("avgPx", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_STATUS, std::make_pair("state", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_CUMULATIVE_FEE_QUANTITY, std::make_pair("fee", JsonDataType::STRING)}, - {CCAPI_EM_ORDER_FEE_ASSET, std::make_pair("feeCcy", JsonDataType::STRING)}, - }; - Element info; - this->extractOrderInfo(info, x, extractionFieldNameMap); - info.insert(CCAPI_EM_ORDER_INSTRUMENT, instrument); + message.setTime(UtilTime::makeTimePointFromMilliseconds(std::stoll(std::string(x["fillTime"].GetString())))); + message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_PRIVATE_TRADE); std::vector elementList; - elementList.emplace_back(std::move(info)); + Element element; + element.insert(CCAPI_TRADE_ID, tradeId); + element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_PRICE, std::string(x["fillPx"].GetString())); + element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_SIZE, std::string(x["fillSz"].GetString())); + element.insert(CCAPI_EM_ORDER_SIDE, std::string(x["side"].GetString()) == "buy" ? CCAPI_EM_ORDER_SIDE_BUY : CCAPI_EM_ORDER_SIDE_SELL); + element.insert(CCAPI_EM_POSITION_SIDE, std::string(x["posSide"].GetString())); + element.insert(CCAPI_IS_MAKER, std::string(x["execType"].GetString()) == "M" ? "1" : "0"); + element.insert(CCAPI_EM_ORDER_ID, std::string(x["ordId"].GetString())); + element.insert(CCAPI_EM_CLIENT_ORDER_ID, std::string(x["clOrdId"].GetString())); + element.insert(CCAPI_EM_ORDER_INSTRUMENT, x["instId"].GetString()); + element.insert(CCAPI_EM_ORDER_FEE_QUANTITY, std::string(x["fillFee"].GetString())); + element.insert(CCAPI_EM_ORDER_FEE_ASSET, std::string(x["fillFeeCcy"].GetString())); + elementList.emplace_back(std::move(element)); message.setElementList(elementList); messageList.emplace_back(std::move(message)); } } } + if (fieldSet.find(CCAPI_EM_ORDER_UPDATE) != fieldSet.end()) { + for (const auto& x : data.GetArray()) { + Message message; + message.setTimeReceived(timeReceived); + message.setCorrelationIdList({subscription.getCorrelationId()}); + message.setTime(UtilTime::makeTimePointFromMilliseconds(std::stoll(std::string(x["uTime"].GetString())))); + message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_ORDER_UPDATE); + const std::map>& extractionFieldNameMap = { + {CCAPI_EM_ORDER_ID, std::make_pair("ordId", JsonDataType::STRING)}, + {CCAPI_EM_CLIENT_ORDER_ID, std::make_pair("clOrdId", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_SIDE, std::make_pair("side", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_LIMIT_PRICE, std::make_pair("px", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_QUANTITY, std::make_pair("sz", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, std::make_pair("accFillSz", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_AVERAGE_FILLED_PRICE, std::make_pair("avgPx", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_STATUS, std::make_pair("state", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_CUMULATIVE_FEE_QUANTITY, std::make_pair("fee", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_FEE_ASSET, std::make_pair("feeCcy", JsonDataType::STRING)}, + {CCAPI_EM_ORDER_INSTRUMENT, std::make_pair("instId", JsonDataType::STRING)}, + }; + Element info; + this->extractOrderInfo(info, x, extractionFieldNameMap); + std::vector elementList; + elementList.emplace_back(std::move(info)); + message.setElementList(elementList); + messageList.emplace_back(std::move(message)); + } + } } else if (channel == "balance_and_position") { for (const auto& x : data.GetArray()) { Message message; diff --git a/include/ccapi_cpp/service/ccapi_market_data_service_bybit.h b/include/ccapi_cpp/service/ccapi_market_data_service_bybit.h index 64204dfc..a06859d1 100644 --- a/include/ccapi_cpp/service/ccapi_market_data_service_bybit.h +++ b/include/ccapi_cpp/service/ccapi_market_data_service_bybit.h @@ -2,36 +2,24 @@ #define INCLUDE_CCAPI_CPP_SERVICE_CCAPI_MARKET_DATA_SERVICE_BYBIT_H_ #ifdef CCAPI_ENABLE_SERVICE_MARKET_DATA #ifdef CCAPI_ENABLE_EXCHANGE_BYBIT -#include "ccapi_cpp/service/ccapi_market_data_service_bybit_base.h" +#include "ccapi_cpp/service/ccapi_market_data_service.h" namespace ccapi { -class MarketDataServiceBybit : public MarketDataServiceBybitBase { +class MarketDataServiceBybit : public MarketDataService { public: MarketDataServiceBybit(std::function*)> eventHandler, SessionOptions sessionOptions, SessionConfigs sessionConfigs, ServiceContext* serviceContextPtr) - : MarketDataServiceBybitBase(eventHandler, sessionOptions, sessionConfigs, serviceContextPtr) { + : MarketDataService(eventHandler, sessionOptions, sessionConfigs, serviceContextPtr) { this->exchangeName = CCAPI_EXCHANGE_NAME_BYBIT; - this->baseUrlWs = sessionConfigs.getUrlWebsocketBase().at(this->exchangeName) + "/v5/public/spot"; + this->baseUrlWs = sessionConfigs.getUrlWebsocketBase().at(this->exchangeName) + "/v5/public/{instrumentTypeSubstitute}"; this->baseUrlRest = sessionConfigs.getUrlRestBase().at(this->exchangeName); this->setHostRestFromUrlRest(this->baseUrlRest); this->setHostWsFromUrlWs(this->baseUrlWs); - // try { - // this->tcpResolverResultsRest = this->resolver.resolve(this->hostRest, this->portRest); - // } catch (const std::exception& e) { - // CCAPI_LOGGER_FATAL(std::string("e.what() = ") + e.what()); - // } - // #ifdef CCAPI_LEGACY_USE_WEBSOCKETPP - // #else - // try { - // this->tcpResolverResultsWs = this->resolverWs.resolve(this->hostWs, this->portWs); - // } catch (const std::exception& e) { - // CCAPI_LOGGER_FATAL(std::string("e.what() = ") + e.what()); - // } - // #endif this->getRecentTradesTarget = "/v5/market/recent-trade"; - // this->getHistoricalTradesTarget = "/spot/v3/public/quote/trades"; + this->getHistoricalTradesTarget = "/v5/market/recent-trade"; this->getRecentCandlesticksTarget = "/v5/market/kline"; - // this->getHistoricalCandlesticksTarget = "/spot/v3/public/quote/kline"; + this->getHistoricalCandlesticksTarget = "/v5/market/kline"; this->getMarketDepthTarget = "/v5/market/orderbook"; + this->getInstrumentTarget = "/v5/market/instruments-info"; this->getInstrumentsTarget = "/v5/market/instruments-info"; } virtual ~MarketDataServiceBybit() {} @@ -39,6 +27,13 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { protected: #endif + std::string getInstrumentGroup(const Subscription& subscription) override { + const auto& instrumentTypeSubstitute = subscription.getInstrumentType(); + std::string url = MarketDataService::getInstrumentGroup(subscription); + std::string toReplace("{instrumentTypeSubstitute}"); + url.replace(url.find(toReplace), toReplace.length(), instrumentTypeSubstitute); + return url; + } std::string convertCandlestickIntervalSecondsToInterval(int intervalSeconds) { std::string interval; if (intervalSeconds < 86400) { @@ -52,15 +47,21 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { } void prepareSubscriptionDetail(std::string& channelId, std::string& symbolId, const std::string& field, const WsConnection& wsConnection, const Subscription& subscription, const std::map optionMap) override { - auto marketDepthRequested = std::stoi(optionMap.at(CCAPI_MARKET_DEPTH_MAX)); + const auto& marketDepthRequested = std::stoi(optionMap.at(CCAPI_MARKET_DEPTH_MAX)); + const auto& instrumentType = subscription.getInstrumentType(); if (field == CCAPI_MARKET_DEPTH) { - channelId = CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH; - std::vector depths = {1, 50, 200}; - int marketDepthSubscribedToExchange = 1; - marketDepthSubscribedToExchange = this->calculateMarketDepthAllowedByExchange(marketDepthRequested, depths); - channelId += std::string("?") + CCAPI_MARKET_DEPTH_SUBSCRIBED_TO_EXCHANGE + "=" + std::to_string(marketDepthSubscribedToExchange); - this->marketDepthSubscribedToExchangeByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId] = marketDepthSubscribedToExchange; - + std::vector depths; + if (instrumentType == "spot") { + depths = {1, 50, 200}; + } else if (instrumentType == "linear" || instrumentType == "inverse") { + depths = {1, 50, 200, 500}; + } else if (instrumentType == "option") { + depths = {25, 100}; + } + int marketDepthSubscribedToExchange = 1; + marketDepthSubscribedToExchange = this->calculateMarketDepthAllowedByExchange(marketDepthRequested, depths); + channelId += std::string("?") + CCAPI_MARKET_DEPTH_SUBSCRIBED_TO_EXCHANGE + "=" + std::to_string(marketDepthSubscribedToExchange); + this->marketDepthSubscribedToExchangeByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId] = marketDepthSubscribedToExchange; } else if (field == CCAPI_CANDLESTICK) { int intervalSeconds = std::stoi(optionMap.at(CCAPI_CANDLESTICK_INTERVAL_SECONDS)); std::string interval = this->convertCandlestickIntervalSecondsToInterval(intervalSeconds); @@ -80,17 +81,14 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { auto channelId = subscriptionListByChannelIdSymbolId.first; for (const auto& subscriptionListByInstrument : subscriptionListByChannelIdSymbolId.second) { auto symbolId = subscriptionListByInstrument.first; - auto exchangeSubscriptionId = channelId; - if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH, 0) == 0) { + std::string exchangeSubscriptionId = channelId; + if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_CHANNEL_ORDERBOOK, 0) == 0) { int marketDepthSubscribedToExchange = this->marketDepthSubscribedToExchangeByConnectionIdChannelIdSymbolIdMap.at(wsConnection.id).at(channelId).at(symbolId); - this->l2UpdateIsReplaceByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId] = true; - exchangeSubscriptionId = CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH; + exchangeSubscriptionId = CCAPI_WEBSOCKET_BYBIT_CHANNEL_ORDERBOOK; std::string toReplace = "{depth}"; exchangeSubscriptionId.replace(exchangeSubscriptionId.find(toReplace), toReplace.length(), std::to_string(marketDepthSubscribedToExchange)); } - if (channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH) { - } std::string toReplace = "{symbol}"; exchangeSubscriptionId.replace(exchangeSubscriptionId.find(toReplace), toReplace.length(), symbolId); this->channelIdSymbolIdByConnectionIdExchangeSubscriptionIdMap[wsConnection.id][exchangeSubscriptionId][CCAPI_CHANNEL_ID] = channelId; @@ -111,19 +109,10 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { sendStringList.push_back(sendString); return sendStringList; } - void processTextMessage( -#ifdef CCAPI_LEGACY_USE_WEBSOCKETPP - WsConnection& wsConnection, wspp::connection_hdl hdl, const std::string& textMessage -#else - std::shared_ptr wsConnectionPtr, boost::beast::string_view textMessageView -#endif - , - const TimePoint& timeReceived, Event& event, std::vector& marketDataMessageList) override { -#ifdef CCAPI_LEGACY_USE_WEBSOCKETPP -#else + void processTextMessage(std::shared_ptr wsConnectionPtr, boost::beast::string_view textMessageView, const TimePoint& timeReceived, Event& event, + std::vector& marketDataMessageList) override { WsConnection& wsConnection = *wsConnectionPtr; std::string textMessage(textMessageView); -#endif rj::Document document; document.Parse(textMessage.c_str()); if (document.IsObject() && document.HasMember("op")) { @@ -166,15 +155,15 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { event.setMessageList(messageList); } } else if (document.IsObject() && document.HasMember("data")) { - MarketDataMessage marketDataMessage; std::string exchangeSubscriptionId = document["topic"].GetString(); std::string channelId = this->channelIdSymbolIdByConnectionIdExchangeSubscriptionIdMap[wsConnection.id][exchangeSubscriptionId][CCAPI_CHANNEL_ID]; std::string symbolId = this->channelIdSymbolIdByConnectionIdExchangeSubscriptionIdMap[wsConnection.id][exchangeSubscriptionId][CCAPI_SYMBOL_ID]; auto optionMap = this->optionMapByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId]; - marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(document["ts"].GetString()))); - marketDataMessage.exchangeSubscriptionId = exchangeSubscriptionId; - if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH, 0) == 0) { - const rj::Value& data = document["data"]; + const rj::Value& data = document["data"]; + if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_CHANNEL_ORDERBOOK, 0) == 0) { + MarketDataMessage marketDataMessage; + marketDataMessage.exchangeSubscriptionId = exchangeSubscriptionId; + marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(document["ts"].GetString()))); marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_MARKET_DEPTH; std::string type = document["type"].GetString(); marketDataMessage.recapType = type == "snapshot" ? MarketDataMessage::RecapType::SOLICITED : MarketDataMessage::RecapType::NONE; @@ -192,31 +181,37 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { } marketDataMessageList.emplace_back(std::move(marketDataMessage)); } else if (channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_TRADE) { - const rj::Value& data = document["data"][0]; - marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_TRADE; - marketDataMessage.recapType = MarketDataMessage::RecapType::NONE; - MarketDataMessage::TypeForDataPoint dataPoint; - dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(std::string(data["p"].GetString()))}); - dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(std::string(data["v"].GetString()))}); - dataPoint.insert({MarketDataMessage::DataFieldType::TRADE_ID, std::string(data["i"].GetString())}); - dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string(data["S"].GetString()) == "Buy" ? "1" : "0"}); - marketDataMessage.data[MarketDataMessage::DataType::TRADE].emplace_back(std::move(dataPoint)); - marketDataMessageList.emplace_back(std::move(marketDataMessage)); + for (const auto& x : data.GetArray()) { + MarketDataMessage marketDataMessage; + marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(x["T"].GetString()))); + marketDataMessage.exchangeSubscriptionId = exchangeSubscriptionId; + marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_TRADE; + marketDataMessage.recapType = MarketDataMessage::RecapType::NONE; + MarketDataMessage::TypeForDataPoint dataPoint; + dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(std::string(x["p"].GetString()))}); + dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(std::string(x["v"].GetString()))}); + dataPoint.insert({MarketDataMessage::DataFieldType::TRADE_ID, std::string(x["i"].GetString())}); + dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string(x["S"].GetString()) == "Buy" ? "0" : "1"}); + marketDataMessage.data[MarketDataMessage::DataType::TRADE].emplace_back(std::move(dataPoint)); + marketDataMessageList.emplace_back(std::move(marketDataMessage)); + } } else if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_CHANNEL_KLINE_2, 0) == 0) { - const rj::Value& data = document["data"][0]; - MarketDataMessage marketDataMessage; - marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_CANDLESTICK; - marketDataMessage.recapType = MarketDataMessage::RecapType::NONE; - marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(document["ts"].GetString()))); - marketDataMessage.exchangeSubscriptionId = exchangeSubscriptionId; - MarketDataMessage::TypeForDataPoint dataPoint; - dataPoint.insert({MarketDataMessage::DataFieldType::OPEN_PRICE, data["open"].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::HIGH_PRICE, data["high"].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::LOW_PRICE, data["low"].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::CLOSE_PRICE, data["close"].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::VOLUME, data["volume"].GetString()}); - marketDataMessage.data[MarketDataMessage::DataType::CANDLESTICK].emplace_back(std::move(dataPoint)); - marketDataMessageList.emplace_back(std::move(marketDataMessage)); + for (const auto& x : data.GetArray()) { + MarketDataMessage marketDataMessage; + marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_CANDLESTICK; + marketDataMessage.recapType = MarketDataMessage::RecapType::NONE; + marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(x["start"].GetString()))); + marketDataMessage.exchangeSubscriptionId = exchangeSubscriptionId; + MarketDataMessage::TypeForDataPoint dataPoint; + dataPoint.insert({MarketDataMessage::DataFieldType::OPEN_PRICE, x["open"].GetString()}); + dataPoint.insert({MarketDataMessage::DataFieldType::HIGH_PRICE, x["high"].GetString()}); + dataPoint.insert({MarketDataMessage::DataFieldType::LOW_PRICE, x["low"].GetString()}); + dataPoint.insert({MarketDataMessage::DataFieldType::CLOSE_PRICE, x["close"].GetString()}); + dataPoint.insert({MarketDataMessage::DataFieldType::VOLUME, x["volume"].GetString()}); + dataPoint.insert({MarketDataMessage::DataFieldType::QUOTE_VOLUME, x["turnover"].GetString()}); + marketDataMessage.data[MarketDataMessage::DataType::CANDLESTICK].emplace_back(std::move(dataPoint)); + marketDataMessageList.emplace_back(std::move(marketDataMessage)); + } } } } @@ -235,7 +230,6 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { this->appendParam(queryString, param, { {CCAPI_LIMIT, "limit"}, - {CCAPI_INSTRUMENT_TYPE, "category"}, }); this->appendSymbolId(queryString, symbolId, "symbol"); req.target(target + "?" + queryString); @@ -252,7 +246,6 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { {CCAPI_LIMIT, "limit"}, {CCAPI_START_TIME_SECONDS, "start"}, {CCAPI_END_TIME_SECONDS, "end"}, - {CCAPI_INSTRUMENT_TYPE, "category"}, }, { {CCAPI_CANDLESTICK_INTERVAL_SECONDS, @@ -275,37 +268,60 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { this->appendParam(queryString, param, { {CCAPI_LIMIT, "limit"}, - {CCAPI_INSTRUMENT_TYPE, "category"}, }); this->appendSymbolId(queryString, symbolId, "symbol"); req.target(target + "?" + queryString); } break; - case Request::Operation::GET_INSTRUMENT: + case Request::Operation::GET_INSTRUMENT: { + req.method(http::verb::get); + auto target = this->getInstrumentTarget; + std::string queryString; + const std::map param = request.getFirstParamWithDefault(); + this->appendParam(queryString, param, + { + {CCAPI_INSTRUMENT_TYPE, "category"}, + }); + if (!symbolId.empty()) { + this->appendSymbolId(queryString, symbolId, "symbol"); + } + req.target(target + "?" + queryString); + } break; case Request::Operation::GET_INSTRUMENTS: { req.method(http::verb::get); auto target = this->getInstrumentsTarget; std::string queryString; const std::map param = request.getFirstParamWithDefault(); this->appendParam(queryString, param, - { - {CCAPI_LIMIT, "limit"}, - {CCAPI_INSTRUMENT_TYPE, "category"}, - }); - this->appendSymbolId(queryString, symbolId, "symbol"); + { + {CCAPI_INSTRUMENT_TYPE, "category"}, + }); req.target(target + "?" + queryString); } break; default: this->convertRequestForRestCustom(req, request, now, symbolId, credential); } } - void extractInstrumentInfo(Element& element, const rj::Value& x) { + void extractInstrumentInfo(Element& element, const rj::Value& x, const std::string& category) { element.insert(CCAPI_INSTRUMENT, x["symbol"].GetString()); element.insert(CCAPI_BASE_ASSET, x["baseCoin"].GetString()); element.insert(CCAPI_QUOTE_ASSET, x["quoteCoin"].GetString()); + auto it = x.FindMember("settleCoin"); + if (it != x.MemberEnd()) { + element.insert(CCAPI_MARGIN_ASSET, it->value.GetString()); + } element.insert(CCAPI_ORDER_PRICE_INCREMENT, x["priceFilter"]["tickSize"].GetString()); - element.insert(CCAPI_ORDER_QUANTITY_INCREMENT, x["lotSizeFilter"]["basePrecision"].GetString()); - element.insert(CCAPI_ORDER_QUANTITY_MIN, x["lotSizeFilter"]["minOrderQty"].GetString()); - element.insert(CCAPI_ORDER_PRICE_TIMES_QUANTITY_MIN, x["lotSizeFilter"]["minOrderAmt"].GetString()); + if (category == "spot") { + element.insert(CCAPI_ORDER_QUANTITY_INCREMENT, x["lotSizeFilter"]["basePrecision"].GetString()); + element.insert(CCAPI_ORDER_QUANTITY_MIN, x["lotSizeFilter"]["minOrderQty"].GetString()); + element.insert(CCAPI_ORDER_PRICE_TIMES_QUANTITY_MIN, x["lotSizeFilter"]["minOrderAmt"].GetString()); + } else if (category == "linear" || category == "inverse") { + element.insert(CCAPI_ORDER_QUANTITY_INCREMENT, x["lotSizeFilter"]["qtyStep"].GetString()); + element.insert(CCAPI_ORDER_QUANTITY_MIN, x["lotSizeFilter"]["minOrderQty"].GetString()); + element.insert(CCAPI_ORDER_PRICE_TIMES_QUANTITY_MIN, x["lotSizeFilter"]["minNotionalValue"].GetString()); + } else if (category == "option") { + element.insert(CCAPI_ORDER_QUANTITY_INCREMENT, x["lotSizeFilter"]["qtyStep"].GetString()); + element.insert(CCAPI_ORDER_QUANTITY_MIN, x["lotSizeFilter"]["minOrderQty"].GetString()); + } } void convertTextMessageToMarketDataMessage(const Request& request, const std::string& textMessage, const TimePoint& timeReceived, Event& event, std::vector& marketDataMessageList) override { @@ -321,8 +337,8 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { MarketDataMessage::TypeForDataPoint dataPoint; dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(std::string(x["price"].GetString()))}); dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(std::string(x["size"].GetString()))}); - dataPoint.insert({MarketDataMessage::DataFieldType::TRADE_ID, std::string(x["execId"].GetString())}); - dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string(x["side"].GetString()) == "Buy" ? "1" : "0"}); + dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string(x["side"].GetString()) == "sell" ? "0" : "1"}); + dataPoint.insert({MarketDataMessage::DataFieldType::TRADE_ID, x["execId"].GetString()}); marketDataMessage.data[MarketDataMessage::DataType::TRADE].emplace_back(std::move(dataPoint)); marketDataMessageList.emplace_back(std::move(marketDataMessage)); } @@ -338,8 +354,8 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { dataPoint.insert({MarketDataMessage::DataFieldType::HIGH_PRICE, x[2].GetString()}); dataPoint.insert({MarketDataMessage::DataFieldType::LOW_PRICE, x[3].GetString()}); dataPoint.insert({MarketDataMessage::DataFieldType::CLOSE_PRICE, x[4].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::VOLUME, x[5].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::QUOTE_VOLUME, x[6].GetString()}); + dataPoint.insert({MarketDataMessage::DataFieldType::VOLUME, x[5].GetString()}); + dataPoint.insert({MarketDataMessage::DataFieldType::QUOTE_VOLUME, x[6].GetString()}); marketDataMessage.data[MarketDataMessage::DataType::CANDLESTICK].emplace_back(std::move(dataPoint)); marketDataMessageList.emplace_back(std::move(marketDataMessage)); } @@ -367,10 +383,11 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { Message message; message.setTimeReceived(timeReceived); message.setType(this->requestOperationToMessageTypeMap.at(request.getOperation())); + std::string category = document["result"]["category"].GetString(); for (const auto& x : document["result"]["list"].GetArray()) { if (std::string(x["symbol"].GetString()) == request.getInstrument()) { Element element; - this->extractInstrumentInfo(element, x); + this->extractInstrumentInfo(element, x, category); message.setElementList({element}); break; } @@ -382,10 +399,11 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { Message message; message.setTimeReceived(timeReceived); message.setType(this->requestOperationToMessageTypeMap.at(request.getOperation())); + std::string category = document["result"]["category"].GetString(); std::vector elementList; for (const auto& x : document["result"]["list"].GetArray()) { Element element; - this->extractInstrumentInfo(element, x); + this->extractInstrumentInfo(element, x, category); elementList.push_back(element); } message.setElementList(elementList); diff --git a/include/ccapi_cpp/service/ccapi_market_data_service_bybit_base.h b/include/ccapi_cpp/service/ccapi_market_data_service_bybit_base.h deleted file mode 100644 index d7791406..00000000 --- a/include/ccapi_cpp/service/ccapi_market_data_service_bybit_base.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef INCLUDE_CCAPI_CPP_SERVICE_CCAPI_MARKET_DATA_SERVICE_BYBIT_BASE_H_ -#define INCLUDE_CCAPI_CPP_SERVICE_CCAPI_MARKET_DATA_SERVICE_BYBIT_BASE_H_ -#ifdef CCAPI_ENABLE_SERVICE_MARKET_DATA -#if defined(CCAPI_ENABLE_EXCHANGE_BYBIT) || defined(CCAPI_ENABLE_EXCHANGE_BYBIT_DERIVATIVES) -#include "ccapi_cpp/service/ccapi_market_data_service.h" -namespace ccapi { -class MarketDataServiceBybitBase : public MarketDataService { - public: - MarketDataServiceBybitBase(std::function*)> eventHandler, SessionOptions sessionOptions, SessionConfigs sessionConfigs, - ServiceContext* serviceContextPtr) - : MarketDataService(eventHandler, sessionOptions, sessionConfigs, serviceContextPtr) {} - virtual ~MarketDataServiceBybitBase() {} -#ifndef CCAPI_EXPOSE_INTERNAL - - protected: -#endif - bool doesHttpBodyContainError(const std::string& body) override { return body.find(R"("retCode":0)") == std::string::npos; } -#ifdef CCAPI_LEGACY_USE_WEBSOCKETPP - void pingOnApplicationLevel(wspp::connection_hdl hdl, ErrorCode& ec) override { this->send(hdl, R"({"op":"ping"})", wspp::frame::opcode::text, ec); } -#else - void pingOnApplicationLevel(std::shared_ptr wsConnectionPtr, ErrorCode& ec) override { this->send(wsConnectionPtr, R"({"op":"ping"})", ec); } -#endif -}; -} /* namespace ccapi */ -#endif -#endif -#endif // INCLUDE_CCAPI_CPP_SERVICE_CCAPI_MARKET_DATA_SERVICE_BYBIT_BASE_H_ diff --git a/include/ccapi_cpp/service/ccapi_market_data_service_bybit_derivatives.h b/include/ccapi_cpp/service/ccapi_market_data_service_bybit_derivatives.h deleted file mode 100644 index 0cd3f464..00000000 --- a/include/ccapi_cpp/service/ccapi_market_data_service_bybit_derivatives.h +++ /dev/null @@ -1,446 +0,0 @@ -#ifndef INCLUDE_CCAPI_CPP_SERVICE_CCAPI_MARKET_DATA_SERVICE_BYBIT_DERIVATIVES_H_ -#define INCLUDE_CCAPI_CPP_SERVICE_CCAPI_MARKET_DATA_SERVICE_BYBIT_DERIVATIVES_H_ -#ifdef CCAPI_ENABLE_SERVICE_MARKET_DATA -#ifdef CCAPI_ENABLE_EXCHANGE_BYBIT_DERIVATIVES -#include "ccapi_cpp/service/ccapi_market_data_service_bybit_base.h" -namespace ccapi { -class MarketDataServiceBybitDerivatives : public MarketDataServiceBybitBase { - public: - MarketDataServiceBybitDerivatives(std::function*)> eventHandler, SessionOptions sessionOptions, SessionConfigs sessionConfigs, - ServiceContext* serviceContextPtr) - : MarketDataServiceBybitBase(eventHandler, sessionOptions, sessionConfigs, serviceContextPtr) { - this->exchangeName = CCAPI_EXCHANGE_NAME_BYBIT_DERIVATIVES; - this->baseUrlWs = sessionConfigs.getUrlWebsocketBase().at(this->exchangeName) + "/{instrumentTypeSubstitute}/public/v3"; - this->baseUrlRest = sessionConfigs.getUrlRestBase().at(this->exchangeName); - this->setHostRestFromUrlRest(this->baseUrlRest); - this->setHostWsFromUrlWs(this->baseUrlWs); - // try { - // this->tcpResolverResultsRest = this->resolver.resolve(this->hostRest, this->portRest); - // } catch (const std::exception& e) { - // CCAPI_LOGGER_FATAL(std::string("e.what() = ") + e.what()); - // } - // #ifdef CCAPI_LEGACY_USE_WEBSOCKETPP - // #else - // try { - // this->tcpResolverResultsWs = this->resolverWs.resolve(this->hostWs, this->portWs); - // } catch (const std::exception& e) { - // CCAPI_LOGGER_FATAL(std::string("e.what() = ") + e.what()); - // } - // #endif - this->getRecentTradesTarget = "/derivatives/v3/public/recent-trade"; - this->getHistoricalTradesTarget = "/derivatives/v3/public/recent-trade"; - this->getRecentCandlesticksTarget = "/derivatives/v3/public/kline"; - this->getHistoricalCandlesticksTarget = "/derivatives/v3/public/kline"; - this->getMarketDepthTarget = "/derivatives/v3/public/order-book/L2"; - this->getInstrumentTarget = "/derivatives/v3/public/instruments-info"; - this->getInstrumentsTarget = "/derivatives/v3/public/instruments-info"; - } - virtual ~MarketDataServiceBybitDerivatives() {} -#ifndef CCAPI_EXPOSE_INTERNAL - - protected: -#endif - std::string getInstrumentGroup(const Subscription& subscription) override { - const auto& instrumentType = subscription.getInstrumentType(); - std::string instrumentTypeSubstitute; - if (instrumentType == "usdt-contract") { - instrumentTypeSubstitute = "contract/usdt"; - } else if (instrumentType == "usdc-contract") { - instrumentTypeSubstitute = "contract/usdc"; - } else if (instrumentType == "usdc-options") { - instrumentTypeSubstitute = "option/usdc"; - } - std::string url = MarketDataService::getInstrumentGroup(subscription); - std::string toReplace("{instrumentTypeSubstitute}"); - url.replace(url.find(toReplace), toReplace.length(), instrumentTypeSubstitute); - return url; - } - std::string convertCandlestickIntervalSecondsToInterval(int intervalSeconds) { - std::string interval; - if (intervalSeconds < 86400) { - interval = std::to_string(intervalSeconds / 60); - } else if (intervalSeconds == 86400) { - interval = "D"; - } else { - interval = "W"; - } - return interval; - } - void prepareSubscriptionDetail(std::string& channelId, std::string& symbolId, const std::string& field, const WsConnection& wsConnection, - const Subscription& subscription, const std::map optionMap) override { - const auto& marketDepthRequested = std::stoi(optionMap.at(CCAPI_MARKET_DEPTH_MAX)); - const auto& instrumentType = subscription.getInstrumentType(); - if (field == CCAPI_MARKET_DEPTH) { - std::vector depths; - if (instrumentType == "usdc-options") { - depths = {25, 100}; - } else { - depths = {1, 50, 200}; - } - int marketDepthSubscribedToExchange = 1; - marketDepthSubscribedToExchange = this->calculateMarketDepthAllowedByExchange(marketDepthRequested, depths); - channelId += std::string("?") + CCAPI_MARKET_DEPTH_SUBSCRIBED_TO_EXCHANGE + "=" + std::to_string(marketDepthSubscribedToExchange); - this->marketDepthSubscribedToExchangeByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId] = marketDepthSubscribedToExchange; - } else if (field == CCAPI_CANDLESTICK) { - int intervalSeconds = std::stoi(optionMap.at(CCAPI_CANDLESTICK_INTERVAL_SECONDS)); - std::string interval = this->convertCandlestickIntervalSecondsToInterval(intervalSeconds); - std::string toReplace = "{interval}"; - channelId.replace(channelId.find(toReplace), toReplace.length(), interval); - } - } - std::vector createSendStringList(const WsConnection& wsConnection) override { - std::vector sendStringList; - std::vector exchangeSubscriptionIdList; - rj::Document document; - document.SetObject(); - rj::Document::AllocatorType& allocator = document.GetAllocator(); - document.AddMember("op", rj::Value("subscribe").Move(), allocator); - rj::Value args(rj::kArrayType); - for (const auto& subscriptionListByChannelIdSymbolId : this->subscriptionListByConnectionIdChannelIdSymbolIdMap.at(wsConnection.id)) { - auto channelId = subscriptionListByChannelIdSymbolId.first; - for (const auto& subscriptionListByInstrument : subscriptionListByChannelIdSymbolId.second) { - auto symbolId = subscriptionListByInstrument.first; - std::string exchangeSubscriptionId = channelId; - if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_ORDERBOOK, 0) == 0) { - int marketDepthSubscribedToExchange = - this->marketDepthSubscribedToExchangeByConnectionIdChannelIdSymbolIdMap.at(wsConnection.id).at(channelId).at(symbolId); - exchangeSubscriptionId = CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_ORDERBOOK; - std::string toReplace = "{depth}"; - exchangeSubscriptionId.replace(exchangeSubscriptionId.find(toReplace), toReplace.length(), std::to_string(marketDepthSubscribedToExchange)); - } - std::string toReplace = "{symbol}"; - exchangeSubscriptionId.replace(exchangeSubscriptionId.find(toReplace), toReplace.length(), symbolId); - this->channelIdSymbolIdByConnectionIdExchangeSubscriptionIdMap[wsConnection.id][exchangeSubscriptionId][CCAPI_CHANNEL_ID] = channelId; - this->channelIdSymbolIdByConnectionIdExchangeSubscriptionIdMap[wsConnection.id][exchangeSubscriptionId][CCAPI_SYMBOL_ID] = symbolId; - args.PushBack(rj::Value(exchangeSubscriptionId.c_str(), allocator).Move(), allocator); - exchangeSubscriptionIdList.push_back(exchangeSubscriptionId); - } - } - document.AddMember("args", args, allocator); - document.AddMember("req_id", rj::Value(std::to_string(this->exchangeJsonPayloadIdByConnectionIdMap[wsConnection.id]).c_str(), allocator).Move(), allocator); - this->exchangeSubscriptionIdListByConnectionIdExchangeJsonPayloadIdMap[wsConnection.id][this->exchangeJsonPayloadIdByConnectionIdMap[wsConnection.id]] = - exchangeSubscriptionIdList; - this->exchangeJsonPayloadIdByConnectionIdMap[wsConnection.id] += 1; - rj::StringBuffer stringBuffer; - rj::Writer writer(stringBuffer); - document.Accept(writer); - std::string sendString = stringBuffer.GetString(); - sendStringList.push_back(sendString); - return sendStringList; - } - void processTextMessage( -#ifdef CCAPI_LEGACY_USE_WEBSOCKETPP - WsConnection& wsConnection, wspp::connection_hdl hdl, const std::string& textMessage -#else - std::shared_ptr wsConnectionPtr, boost::beast::string_view textMessageView -#endif - , - const TimePoint& timeReceived, Event& event, std::vector& marketDataMessageList) override { -#ifdef CCAPI_LEGACY_USE_WEBSOCKETPP -#else - WsConnection& wsConnection = *wsConnectionPtr; - std::string textMessage(textMessageView); -#endif - rj::Document document; - document.Parse(textMessage.c_str()); - if (document.IsObject() && document.HasMember("op")) { - std::string op = document["op"].GetString(); - if (op == "subscribe") { - bool success = document["success"].GetBool(); - event.setType(Event::Type::SUBSCRIPTION_STATUS); - std::vector messageList; - Message message; - message.setTimeReceived(timeReceived); - std::vector correlationIdList; - if (this->correlationIdListByConnectionIdChannelIdSymbolIdMap.find(wsConnection.id) != - this->correlationIdListByConnectionIdChannelIdSymbolIdMap.end()) { - int id = std::stoi(document["req_id"].GetString()); - if (this->exchangeSubscriptionIdListByConnectionIdExchangeJsonPayloadIdMap.find(wsConnection.id) != - this->exchangeSubscriptionIdListByConnectionIdExchangeJsonPayloadIdMap.end() && - this->exchangeSubscriptionIdListByConnectionIdExchangeJsonPayloadIdMap.at(wsConnection.id).find(id) != - this->exchangeSubscriptionIdListByConnectionIdExchangeJsonPayloadIdMap.at(wsConnection.id).end()) { - for (const auto& exchangeSubscriptionId : this->exchangeSubscriptionIdListByConnectionIdExchangeJsonPayloadIdMap.at(wsConnection.id).at(id)) { - std::string channelId = this->channelIdSymbolIdByConnectionIdExchangeSubscriptionIdMap[wsConnection.id][exchangeSubscriptionId][CCAPI_CHANNEL_ID]; - std::string symbolId = this->channelIdSymbolIdByConnectionIdExchangeSubscriptionIdMap[wsConnection.id][exchangeSubscriptionId][CCAPI_SYMBOL_ID]; - if (this->correlationIdListByConnectionIdChannelIdSymbolIdMap.at(wsConnection.id).find(channelId) != - this->correlationIdListByConnectionIdChannelIdSymbolIdMap.at(wsConnection.id).end()) { - if (this->correlationIdListByConnectionIdChannelIdSymbolIdMap.at(wsConnection.id).at(channelId).find(symbolId) != - this->correlationIdListByConnectionIdChannelIdSymbolIdMap.at(wsConnection.id).at(channelId).end()) { - std::vector correlationIdList_2 = - this->correlationIdListByConnectionIdChannelIdSymbolIdMap.at(wsConnection.id).at(channelId).at(symbolId); - correlationIdList.insert(correlationIdList.end(), correlationIdList_2.begin(), correlationIdList_2.end()); - } - } - } - } - } - message.setCorrelationIdList(correlationIdList); - message.setType(success ? Message::Type::SUBSCRIPTION_STARTED : Message::Type::SUBSCRIPTION_FAILURE); - Element element; - element.insert(success ? CCAPI_INFO_MESSAGE : CCAPI_ERROR_MESSAGE, textMessage); - message.setElementList({element}); - messageList.emplace_back(std::move(message)); - event.setMessageList(messageList); - } - } else if (document.IsObject() && document.HasMember("data")) { - std::string exchangeSubscriptionId = document["topic"].GetString(); - std::string channelId = this->channelIdSymbolIdByConnectionIdExchangeSubscriptionIdMap[wsConnection.id][exchangeSubscriptionId][CCAPI_CHANNEL_ID]; - std::string symbolId = this->channelIdSymbolIdByConnectionIdExchangeSubscriptionIdMap[wsConnection.id][exchangeSubscriptionId][CCAPI_SYMBOL_ID]; - auto optionMap = this->optionMapByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId]; - const rj::Value& data = document["data"]; - if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_ORDERBOOK, 0) == 0) { - MarketDataMessage marketDataMessage; - marketDataMessage.exchangeSubscriptionId = exchangeSubscriptionId; - marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(document["ts"].GetString()))); - marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_MARKET_DEPTH; - std::string type = document["type"].GetString(); - marketDataMessage.recapType = type == "snapshot" ? MarketDataMessage::RecapType::SOLICITED : MarketDataMessage::RecapType::NONE; - for (const auto& x : data["b"].GetArray()) { - MarketDataMessage::TypeForDataPoint dataPoint; - dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(x[0].GetString())}); - dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(x[1].GetString())}); - marketDataMessage.data[MarketDataMessage::DataType::BID].emplace_back(std::move(dataPoint)); - } - for (const auto& x : data["a"].GetArray()) { - MarketDataMessage::TypeForDataPoint dataPoint; - dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(x[0].GetString())}); - dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(x[1].GetString())}); - marketDataMessage.data[MarketDataMessage::DataType::ASK].emplace_back(std::move(dataPoint)); - } - marketDataMessageList.emplace_back(std::move(marketDataMessage)); - } else if (channelId == CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_TRADE) { - for (const auto& x : data.GetArray()) { - MarketDataMessage marketDataMessage; - marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(x["T"].GetString()))); - marketDataMessage.exchangeSubscriptionId = exchangeSubscriptionId; - marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_TRADE; - marketDataMessage.recapType = MarketDataMessage::RecapType::NONE; - MarketDataMessage::TypeForDataPoint dataPoint; - dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(std::string(x["p"].GetString()))}); - dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(std::string(x["v"].GetString()))}); - dataPoint.insert({MarketDataMessage::DataFieldType::TRADE_ID, std::string(x["i"].GetString())}); - dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string(x["S"].GetString()) == "Buy" ? "0" : "1"}); - marketDataMessage.data[MarketDataMessage::DataType::TRADE].emplace_back(std::move(dataPoint)); - marketDataMessageList.emplace_back(std::move(marketDataMessage)); - } - } else if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_KLINE_2, 0) == 0) { - for (const auto& x : data.GetArray()) { - MarketDataMessage marketDataMessage; - marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_CANDLESTICK; - marketDataMessage.recapType = MarketDataMessage::RecapType::NONE; - marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(x["start"].GetString()))); - marketDataMessage.exchangeSubscriptionId = exchangeSubscriptionId; - MarketDataMessage::TypeForDataPoint dataPoint; - dataPoint.insert({MarketDataMessage::DataFieldType::OPEN_PRICE, x["open"].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::HIGH_PRICE, x["high"].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::LOW_PRICE, x["low"].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::CLOSE_PRICE, x["close"].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::VOLUME, x["volume"].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::QUOTE_VOLUME, x["turnover"].GetString()}); - marketDataMessage.data[MarketDataMessage::DataType::CANDLESTICK].emplace_back(std::move(dataPoint)); - marketDataMessageList.emplace_back(std::move(marketDataMessage)); - } - } - } - } - void convertRequestForRest(http::request& req, const Request& request, const TimePoint& now, const std::string& symbolId, - const std::map& credential) override { - switch (request.getOperation()) { - case Request::Operation::GENERIC_PUBLIC_REQUEST: { - MarketDataService::convertRequestForRestGenericPublicRequest(req, request, now, symbolId, credential); - } break; - case Request::Operation::GET_RECENT_TRADES: - case Request::Operation::GET_HISTORICAL_TRADES: { - req.method(http::verb::get); - auto target = this->getRecentTradesTarget; - std::string queryString; - const std::map param = request.getFirstParamWithDefault(); - this->appendParam(queryString, param, - { - {CCAPI_LIMIT, "limit"}, - }); - this->appendSymbolId(queryString, symbolId, "symbol"); - req.target(target + "?" + queryString); - } break; - case Request::Operation::GET_RECENT_CANDLESTICKS: - case Request::Operation::GET_HISTORICAL_CANDLESTICKS: { - req.method(http::verb::get); - auto target = this->getRecentCandlesticksTarget; - std::string queryString; - const std::map param = request.getFirstParamWithDefault(); - this->appendParam(queryString, param, - { - {CCAPI_CANDLESTICK_INTERVAL_SECONDS, "interval"}, - {CCAPI_LIMIT, "limit"}, - {CCAPI_START_TIME_SECONDS, "start"}, - {CCAPI_END_TIME_SECONDS, "end"}, - }, - { - {CCAPI_CANDLESTICK_INTERVAL_SECONDS, - [that = shared_from_base()](const std::string& input) { - return that->convertCandlestickIntervalSecondsToInterval(std::stoi(input)); - }}, - {CCAPI_START_TIME_SECONDS, [that = shared_from_base()]( - const std::string& input) { return that->convertParamTimeSecondsToTimeMilliseconds(input); }}, - {CCAPI_END_TIME_SECONDS, [that = shared_from_base()]( - const std::string& input) { return that->convertParamTimeSecondsToTimeMilliseconds(input); }}, - }); - this->appendSymbolId(queryString, symbolId, "symbol"); - req.target(target + "?" + queryString); - } break; - case Request::Operation::GET_MARKET_DEPTH: { - req.method(http::verb::get); - auto target = this->getMarketDepthTarget; - std::string queryString; - const std::map param = request.getFirstParamWithDefault(); - this->appendParam(queryString, param, - { - {CCAPI_LIMIT, "limit"}, - }); - this->appendSymbolId(queryString, symbolId, "symbol"); - req.target(target + "?" + queryString); - } break; - case Request::Operation::GET_INSTRUMENT: { - req.method(http::verb::get); - auto target = this->getInstrumentTarget; - std::string queryString; - const std::map param = request.getFirstParamWithDefault(); - this->appendParam(queryString, param, - { - {CCAPI_INSTRUMENT_TYPE, "category"}, - }); - if (!symbolId.empty()) { - this->appendSymbolId(queryString, symbolId, "symbol"); - } - req.target(target + "?" + queryString); - } break; - case Request::Operation::GET_INSTRUMENTS: { - req.method(http::verb::get); - auto target = this->getInstrumentsTarget; - std::string queryString; - const std::map param = request.getFirstParamWithDefault(); - this->appendParam(queryString, param, - { - {CCAPI_INSTRUMENT_TYPE, "category"}, - }); - req.target(target + "?" + queryString); - } break; - default: - this->convertRequestForRestCustom(req, request, now, symbolId, credential); - } - } - void extractInstrumentInfo(Element& element, const rj::Value& x) { - element.insert(CCAPI_INSTRUMENT, x["symbol"].GetString()); - element.insert(CCAPI_BASE_ASSET, x["baseCoin"].GetString()); - element.insert(CCAPI_QUOTE_ASSET, x["quoteCoin"].GetString()); - auto it = x.FindMember("settleCoin"); - if (it != x.MemberEnd()) { - element.insert(CCAPI_MARGIN_ASSET, it->value.GetString()); - } - element.insert(CCAPI_ORDER_PRICE_INCREMENT, x["priceFilter"]["tickSize"].GetString()); - element.insert(CCAPI_ORDER_QUANTITY_INCREMENT, x["lotSizeFilter"]["qtyStep"].GetString()); - element.insert(CCAPI_ORDER_QUANTITY_MIN, x["lotSizeFilter"]["minTradingQty"].GetString()); - } - void convertTextMessageToMarketDataMessage(const Request& request, const std::string& textMessage, const TimePoint& timeReceived, Event& event, - std::vector& marketDataMessageList) override { - rj::Document document; - document.Parse(textMessage.c_str()); - switch (request.getOperation()) { - case Request::Operation::GET_RECENT_TRADES: - case Request::Operation::GET_HISTORICAL_TRADES: { - for (const auto& x : document["result"]["list"].GetArray()) { - MarketDataMessage marketDataMessage; - marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_TRADE; - marketDataMessage.tp = UtilTime::makeTimePointFromMilliseconds(std::stoll(x["time"].GetString())); - MarketDataMessage::TypeForDataPoint dataPoint; - dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(std::string(x["price"].GetString()))}); - dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(std::string(x["size"].GetString()))}); - dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string(x["side"].GetString()) == "sell" ? "0" : "1"}); - dataPoint.insert({MarketDataMessage::DataFieldType::TRADE_ID, x["execId"].GetString()}); - marketDataMessage.data[MarketDataMessage::DataType::TRADE].emplace_back(std::move(dataPoint)); - marketDataMessageList.emplace_back(std::move(marketDataMessage)); - } - } break; - case Request::Operation::GET_RECENT_CANDLESTICKS: - case Request::Operation::GET_HISTORICAL_CANDLESTICKS: { - for (const auto& x : document["result"]["list"].GetArray()) { - MarketDataMessage marketDataMessage; - marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_CANDLESTICK; - marketDataMessage.tp = UtilTime::makeTimePointFromMilliseconds(std::stoll(x[0].GetString())); - MarketDataMessage::TypeForDataPoint dataPoint; - dataPoint.insert({MarketDataMessage::DataFieldType::OPEN_PRICE, x[1].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::HIGH_PRICE, x[2].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::LOW_PRICE, x[3].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::CLOSE_PRICE, x[4].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::VOLUME, x[5].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::QUOTE_VOLUME, x[6].GetString()}); - marketDataMessage.data[MarketDataMessage::DataType::CANDLESTICK].emplace_back(std::move(dataPoint)); - marketDataMessageList.emplace_back(std::move(marketDataMessage)); - } - } break; - case Request::Operation::GET_MARKET_DEPTH: { - MarketDataMessage marketDataMessage; - marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_MARKET_DEPTH; - CCAPI_LOGGER_TRACE(""); - const rj::Value& result = document["result"]; - CCAPI_LOGGER_TRACE(""); - marketDataMessage.tp = UtilTime::makeTimePointFromMilliseconds(std::stoll(result["ts"].GetString())); - CCAPI_LOGGER_TRACE(""); - for (const auto& x : result["b"].GetArray()) { - CCAPI_LOGGER_TRACE(""); - MarketDataMessage::TypeForDataPoint dataPoint; - dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, x[0].GetString()}); - CCAPI_LOGGER_TRACE(""); - dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, x[1].GetString()}); - CCAPI_LOGGER_TRACE(""); - marketDataMessage.data[MarketDataMessage::DataType::BID].emplace_back(std::move(dataPoint)); - } - for (const auto& x : result["a"].GetArray()) { - CCAPI_LOGGER_TRACE(""); - MarketDataMessage::TypeForDataPoint dataPoint; - dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, x[0].GetString()}); - CCAPI_LOGGER_TRACE(""); - dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, x[1].GetString()}); - CCAPI_LOGGER_TRACE(""); - marketDataMessage.data[MarketDataMessage::DataType::ASK].emplace_back(std::move(dataPoint)); - } - marketDataMessageList.emplace_back(std::move(marketDataMessage)); - } break; - case Request::Operation::GET_INSTRUMENT: { - Message message; - message.setTimeReceived(timeReceived); - message.setType(this->requestOperationToMessageTypeMap.at(request.getOperation())); - for (const auto& x : document["result"]["list"].GetArray()) { - if (std::string(x["symbol"].GetString()) == request.getInstrument()) { - Element element; - this->extractInstrumentInfo(element, x); - message.setElementList({element}); - break; - } - } - message.setCorrelationIdList({request.getCorrelationId()}); - event.addMessages({message}); - } break; - case Request::Operation::GET_INSTRUMENTS: { - Message message; - message.setTimeReceived(timeReceived); - message.setType(this->requestOperationToMessageTypeMap.at(request.getOperation())); - std::vector elementList; - for (const auto& x : document["result"]["list"].GetArray()) { - Element element; - this->extractInstrumentInfo(element, x); - elementList.push_back(element); - } - message.setElementList(elementList); - message.setCorrelationIdList({request.getCorrelationId()}); - event.addMessages({message}); - } break; - default: - CCAPI_LOGGER_FATAL(CCAPI_UNSUPPORTED_VALUE); - } - } -}; -} /* namespace ccapi */ -#endif -#endif -#endif // INCLUDE_CCAPI_CPP_SERVICE_CCAPI_MARKET_DATA_SERVICE_BYBIT_DERIVATIVES_H_ diff --git a/include/ccapi_cpp/service/ccapi_market_data_service_huobi.h b/include/ccapi_cpp/service/ccapi_market_data_service_huobi.h index 8eea345e..ce595bc9 100644 --- a/include/ccapi_cpp/service/ccapi_market_data_service_huobi.h +++ b/include/ccapi_cpp/service/ccapi_market_data_service_huobi.h @@ -36,16 +36,11 @@ class MarketDataServiceHuobi : public MarketDataServiceHuobiBase { const Subscription& subscription, const std::map optionMap) override { auto marketDepthRequested = std::stoi(optionMap.at(CCAPI_MARKET_DEPTH_MAX)); auto conflateIntervalMilliseconds = std::stoi(optionMap.at(CCAPI_CONFLATE_INTERVAL_MILLISECONDS)); - CCAPI_LOGGER_TRACE(""); if (field == CCAPI_MARKET_DEPTH) { - CCAPI_LOGGER_TRACE(""); if (conflateIntervalMilliseconds < 100) { - CCAPI_LOGGER_TRACE(""); if (marketDepthRequested == 1) { - CCAPI_LOGGER_TRACE(""); channelId = CCAPI_WEBSOCKET_HUOBI_CHANNEL_MARKET_BBO; } else { - CCAPI_LOGGER_TRACE(""); channelId = CCAPI_WEBSOCKET_HUOBI_CHANNEL_MARKET_BY_PRICE_REFRESH_UPDATE; int marketDepthSubscribedToExchange = 1; marketDepthSubscribedToExchange = this->calculateMarketDepthAllowedByExchange(marketDepthRequested, std::vector({5, 10, 20})); diff --git a/test/test_build/CMakeLists.txt b/test/test_build/CMakeLists.txt index 759b22a0..c172f835 100644 --- a/test/test_build/CMakeLists.txt +++ b/test/test_build/CMakeLists.txt @@ -2,8 +2,8 @@ set(NAME build_test) project(${NAME}) add_compile_definitions(CCAPI_ENABLE_LOG_TRACE) set(SERVICE_LIST "MARKET_DATA" "EXECUTION_MANAGEMENT" "FIX") -set(MARKET_DATA_EXCHANGE_LIST "COINBASE" "GEMINI" "KRAKEN" "KRAKEN_FUTURES" "BITSTAMP" "BITFINEX" "BITMEX" "BINANCE_US" "BINANCE" "BINANCE_USDS_FUTURES" "BINANCE_COIN_FUTURES" "HUOBI" "HUOBI_USDT_SWAP" "HUOBI_COIN_SWAP" "OKX" "ERISX" "KUCOIN" "KUCOIN_FUTURES" "DERIBIT" "GATEIO" "GATEIO_PERPETUAL_FUTURES" "CRYPTOCOM" "ASCENDEX" "BYBIT" "BYBIT_DERIVATIVES" "BITGET" "BITGET_FUTURES" "BITMART" "MEXC" "MEXC_FUTURES" "WHITEBIT") -set(EXECUTION_MANAGEMENT_EXCHANGE_LIST "COINBASE" "GEMINI" "KRAKEN" "KRAKEN_FUTURES" "BITSTAMP" "BITFINEX" "BITMEX" "BINANCE_US" "BINANCE" "BINANCE_USDS_FUTURES" "BINANCE_COIN_FUTURES" "HUOBI" "HUOBI_USDT_SWAP" "HUOBI_COIN_SWAP" "OKX" "ERISX" "KUCOIN" "KUCOIN_FUTURES" "DERIBIT" "GATEIO" "GATEIO_PERPETUAL_FUTURES" "CRYPTOCOM" "ASCENDEX" "BYBIT" "BYBIT_DERIVATIVES" "BITGET" "BITGET_FUTURES" "BITMART" "MEXC" "WHITEBIT") +set(MARKET_DATA_EXCHANGE_LIST "COINBASE" "GEMINI" "KRAKEN" "KRAKEN_FUTURES" "BITSTAMP" "BITFINEX" "BITMEX" "BINANCE_US" "BINANCE" "BINANCE_USDS_FUTURES" "BINANCE_COIN_FUTURES" "HUOBI" "HUOBI_USDT_SWAP" "HUOBI_COIN_SWAP" "OKX" "ERISX" "KUCOIN" "KUCOIN_FUTURES" "DERIBIT" "GATEIO" "GATEIO_PERPETUAL_FUTURES" "CRYPTOCOM" "ASCENDEX" "BYBIT" "BITGET" "BITGET_FUTURES" "BITMART" "MEXC" "MEXC_FUTURES" "WHITEBIT") +set(EXECUTION_MANAGEMENT_EXCHANGE_LIST "COINBASE" "GEMINI" "KRAKEN" "KRAKEN_FUTURES" "BITSTAMP" "BITFINEX" "BITMEX" "BINANCE_US" "BINANCE" "BINANCE_USDS_FUTURES" "BINANCE_COIN_FUTURES" "HUOBI" "HUOBI_USDT_SWAP" "HUOBI_COIN_SWAP" "OKX" "ERISX" "KUCOIN" "KUCOIN_FUTURES" "DERIBIT" "GATEIO" "GATEIO_PERPETUAL_FUTURES" "CRYPTOCOM" "ASCENDEX" "BYBIT" "BITGET" "BITGET_FUTURES" "BITMART" "MEXC" "WHITEBIT") set(FIX_EXCHANGE_LIST "COINBASE" "GEMINI") set(HFFIX_INCLUDE_DIR ${CCAPI_PROJECT_DIR}/dependency/hffix/include) foreach(SERVICE IN LISTS SERVICE_LIST)