From 8795192ce4bbff671e327526942bd9533ebdf429 Mon Sep 17 00:00:00 2001 From: hk Date: Mon, 22 Sep 2025 04:12:56 +0000 Subject: [PATCH 01/15] add subscription proxyUrl --- README.md | 13 +++-- include/ccapi_cpp/ccapi_subscription.h | 14 ++++-- include/ccapi_cpp/ccapi_ws_connection.h | 10 ++-- .../ccapi_execution_management_service.h | 6 ++- ...pi_execution_management_service_ascendex.h | 3 +- ...agement_service_gateio_perpetual_futures.h | 49 ++++++++++--------- .../service/ccapi_market_data_service.h | 24 +++++---- include/ccapi_cpp/service/ccapi_service.h | 14 +++++- 8 files changed, 83 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 9b29797b..de800d94 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ - [Make Session::sendRequest blocking](#make-sessionsendrequest-blocking) - [Provide API credentials for an exchange](#provide-api-credentials-for-an-exchange) - [Override exchange urls](#override-exchange-urls) + - [Connect to a proxy](#connect-to-a-proxy) - [Complex request parameters](#complex-request-parameters-1) - [Send request by Websocket API](#send-request-by-websocket-api) - [Specify instrument type](#specify-instrument-type) @@ -700,11 +701,11 @@ Bye #### Specify correlation id -Instantiate `Request` with the desired correlationId. The `correlationId` should be unique. +Instantiate `Request` with the desired `correlationId`. The `correlationId` should be unique. ``` Request request(Request::Operation::CREATE_ORDER, "okx", "BTC-USDT", "cool correlation id"); ``` -Instantiate `Subscription` with the desired correlationId. +Instantiate `Subscription` with the desired `correlationId`. ``` Subscription subscription("okx", "BTC-USDT", "ORDER_UPDATE", "", "cool correlation id"); ``` @@ -768,7 +769,13 @@ Subscription subscription("okx", "BTC-USDT", "ORDER_UPDATE", "", "", { ``` #### Override exchange urls -You can override exchange urls at compile time by using macros. See section "exchange REST urls", "exchange WS urls", and "exchange FIX urls" in [`include/ccapi_cpp/ccapi_macro.h`](include/ccapi_cpp/ccapi_macro.h). You can also override exchange urls at runtime. See [this example](example/src/override_exchange_url_at_runtime/main.cpp). These can be useful if you need to connect to test accounts (e.g. https://www.okx.com/docs-v5/en/#overview-demo-trading-services) or connect to an IP address (e.g. ws://172.30.0.146:9000). +You can override exchange urls at compile time by using macros. See section "exchange REST urls", "exchange WS urls", and "exchange FIX urls" in [`include/ccapi_cpp/ccapi_macro.h`](include/ccapi_cpp/ccapi_macro.h). You can also override exchange urls at runtime. See [this example](example/src/override_exchange_url_at_runtime/main.cpp). These can be useful if you need to connect to test accounts (e.g. https://www.okx.com/docs-v5/en/#overview-demo-trading-services). + +#### Connect to a proxy +Instantiate `Subscription` with the desired `proxyUrl`. +``` +Subscription subscription("okx", "BTC-USDT", "MARKET_DEPTH", "", "", {}, "172.30.0.146:9000"); +``` #### Complex request parameters Please follow the exchange's API documentations: e.g. https://www.okx.com/docs-v5/en/#order-book-trading-trade-post-place-order. diff --git a/include/ccapi_cpp/ccapi_subscription.h b/include/ccapi_cpp/ccapi_subscription.h index ac1b49d8..5eb8fff0 100644 --- a/include/ccapi_cpp/ccapi_subscription.h +++ b/include/ccapi_cpp/ccapi_subscription.h @@ -16,8 +16,8 @@ namespace ccapi { class Subscription { public: explicit Subscription(const std::string& exchange = "", const std::string& instrument = "", const std::string& field = "", const std::string& options = "", - const std::string& correlationId = "", const std::map& credential = {}) - : exchange(exchange), instrument(instrument), field(field), correlationId(correlationId), credential(credential) { + const std::string& correlationId = "", const std::map& credential = {}, const std::string& proxyUrl = "") + : exchange(exchange), instrument(instrument), field(field), correlationId(correlationId), credential(credential), proxyUrl(proxyUrl) { auto originalInstrumentSet = UtilString::splitToSet(instrument, ","); std::copy_if(originalInstrumentSet.begin(), originalInstrumentSet.end(), std::inserter(this->instrumentSet, this->instrumentSet.end()), [](const std::string& value) { return !value.empty(); }); @@ -68,8 +68,8 @@ class Subscription { } std::string output = "Subscription [exchange = " + exchange + ", marginType = " + marginType + ", instrumentType = " + instrumentType + ", instrument = " + instrument + ", field = " + field + ", optionMap = " + ccapi::toString(optionMap) + - ", correlationId = " + correlationId + ", credential = " + ccapi::toString(shortCredential) + ", serviceName = " + serviceName + - ", timeSent = " + UtilTime::getISOTimestamp(timeSent) + "]"; + ", correlationId = " + correlationId + ", credential = " + ccapi::toString(shortCredential) + ", proxyUrl = " + proxyUrl + + ", serviceName = " + serviceName + ", timeSent = " + UtilTime::getISOTimestamp(timeSent) + "]"; return output; } @@ -89,6 +89,8 @@ class Subscription { const std::map& getCredential() const { return credential; } + const std::string& getProxyUrl() const { return proxyUrl; } + const std::string& getServiceName() const { return serviceName; } const std::set& getInstrumentSet() const { return instrumentSet; } @@ -131,11 +133,14 @@ class Subscription { void setField(const std::string& field) { this->field = field; } + void setProxyUrl(const std::string& proxyUrl) { this->proxyUrl = proxyUrl; } + void setTimeSent(TimePoint timeSent) { this->timeSent = timeSent; } void setInstrumentType(const std::string& instrumentType) { this->instrumentType = instrumentType; } void setMarginType(const std::string& marginType) { this->marginType = marginType; } + enum class Status { UNKNOWN, SUBSCRIBING, @@ -180,6 +185,7 @@ class Subscription { std::map optionMap; std::string correlationId; std::map credential; + std::string proxyUrl; std::string serviceName; std::set instrumentSet; std::set fieldSet; diff --git a/include/ccapi_cpp/ccapi_ws_connection.h b/include/ccapi_cpp/ccapi_ws_connection.h index f4693e72..fdc28ca8 100644 --- a/include/ccapi_cpp/ccapi_ws_connection.h +++ b/include/ccapi_cpp/ccapi_ws_connection.h @@ -18,8 +18,8 @@ class WsConnection { WsConnection& operator=(const WsConnection&) = delete; WsConnection(const std::string& url, const std::string& group, const std::vector& subscriptionList, - const std::map& credential) - : url(url), group(group), subscriptionList(subscriptionList), credential(credential) { + const std::map& credential, const std::string& proxyUrl = "") + : url(url), group(group), subscriptionList(subscriptionList), credential(credential), proxyUrl(proxyUrl) { std::map shortCredential; for (const auto& x : credential) { shortCredential.insert(std::make_pair(x.first, UtilString::firstNCharacter(x.second, CCAPI_CREDENTIAL_DISPLAY_LENGTH))); @@ -51,8 +51,9 @@ class WsConnection { streamPtr); std::string output = "WsConnection [longId = " + longId + ", id = " + id + ", url = " + url + ", group = " + group + ", subscriptionList = " + ccapi::toString(subscriptionList) + ", credential = " + ccapi::toString(shortCredential) + - ", status = " + statusToString(status) + ", headers = " + ccapi::toString(headers) + ", streamPtr = " + oss.str() + - ", remoteCloseCode = " + std::to_string(remoteCloseCode) + ", remoteCloseReason = " + std::string(remoteCloseReason.reason.c_str()) + + ", proxyUrl = " + proxyUrl + ", status = " + statusToString(status) + ", headers = " + ccapi::toString(headers) + + ", streamPtr = " + oss.str() + ", remoteCloseCode = " + std::to_string(remoteCloseCode) + + ", remoteCloseReason = " + std::string(remoteCloseReason.reason.c_str()) + ", hostHttpHeaderValue = " + ccapi::toString(hostHttpHeaderValue) + ", path = " + ccapi::toString(path) + ", host = " + ccapi::toString(host) + ", port = " + ccapi::toString(port) + ", isSecure = " + ccapi::toString(isSecure) + "]"; return output; @@ -145,6 +146,7 @@ class WsConnection { Status status{Status::UNKNOWN}; std::map headers; std::map credential; + std::string proxyUrl; std::variant>>, std::shared_ptr>> streamPtr; beast::websocket::close_code remoteCloseCode{}; diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service.h b/include/ccapi_cpp/service/ccapi_execution_management_service.h index 9f1b77ea..96201296 100644 --- a/include/ccapi_cpp/service/ccapi_execution_management_service.h +++ b/include/ccapi_cpp/service/ccapi_execution_management_service.h @@ -61,13 +61,15 @@ class ExecutionManagementService : public Service { } const auto& fieldSet = subscription.getFieldSet(); + const auto& proxyUrl = subscription.getProxyUrl(); + if (fieldSet.find(CCAPI_EM_WEBSOCKET_ORDER_ENTRY) != fieldSet.end()) { - auto wsConnectionPtr = std::make_shared(that->baseUrlWsOrderEntry, "", std::vector{subscription}, credential); + auto wsConnectionPtr = std::make_shared(that->baseUrlWsOrderEntry, "", std::vector{subscription}, credential, proxyUrl); that->setWsConnectionStream(wsConnectionPtr); CCAPI_LOGGER_WARN("about to subscribe with new wsConnectionPtr " + toString(*wsConnectionPtr)); that->prepareConnect(wsConnectionPtr); } else { - auto wsConnectionPtr = std::make_shared(that->baseUrlWs, "", std::vector{subscription}, credential); + auto wsConnectionPtr = std::make_shared(that->baseUrlWs, "", std::vector{subscription}, credential, proxyUrl); that->setWsConnectionStream(wsConnectionPtr); CCAPI_LOGGER_WARN("about to subscribe with new wsConnectionPtr " + toString(*wsConnectionPtr)); that->prepareConnect(wsConnectionPtr); diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service_ascendex.h b/include/ccapi_cpp/service/ccapi_execution_management_service_ascendex.h index 14193e8a..5768cfe8 100644 --- a/include/ccapi_cpp/service/ccapi_execution_management_service_ascendex.h +++ b/include/ccapi_cpp/service/ccapi_execution_management_service_ascendex.h @@ -362,9 +362,10 @@ class ExecutionManagementServiceAscendex : public ExecutionManagementService { credential = that->credentialDefault; } const auto& accountGroup = mapGetWithDefault(credential, that->apiAccountGroupName); + const auto& proxyUrl = subscription.getProxyUrl(); auto wsConnectionPtr = std::make_shared(that->baseUrlWs + "/" + accountGroup + "/api/pro/v1/stream", "", - std::vector{subscription}, credential); + std::vector{subscription}, credential, proxyUrl); that->setWsConnectionStream(wsConnectionPtr); CCAPI_LOGGER_WARN("about to subscribe with new wsConnectionPtr " + toString(*wsConnectionPtr)); that->prepareConnect(wsConnectionPtr); diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service_gateio_perpetual_futures.h b/include/ccapi_cpp/service/ccapi_execution_management_service_gateio_perpetual_futures.h index 1c46c113..bb129152 100644 --- a/include/ccapi_cpp/service/ccapi_execution_management_service_gateio_perpetual_futures.h +++ b/include/ccapi_cpp/service/ccapi_execution_management_service_gateio_perpetual_futures.h @@ -82,31 +82,32 @@ class ExecutionManagementServiceGateioPerpetualFutures : public ExecutionManagem CCAPI_LOGGER_DEBUG("this->baseUrlWs = " + this->baseUrlWs); if (this->shouldContinue.load()) { for (auto& subscription : subscriptionList) { - boost::asio::post( - *this->serviceContextPtr->ioContextPtr, [that = shared_from_base(), subscription]() mutable { - auto now = UtilTime::now(); - subscription.setTimeSent(now); - const auto& instrumentSet = subscription.getInstrumentSet(); - auto it = instrumentSet.begin(); - if (it != instrumentSet.end()) { - std::string settle; - std::string symbolId = *it; - if (UtilString::endsWith(symbolId, "_USD")) { - settle = "btc"; - } else if (UtilString::endsWith(symbolId, "_USDT")) { - settle = "usdt"; - } - auto credential = subscription.getCredential(); - if (credential.empty()) { - credential = that->credentialDefault; - } + boost::asio::post(*this->serviceContextPtr->ioContextPtr, [that = shared_from_base(), + subscription]() mutable { + auto now = UtilTime::now(); + subscription.setTimeSent(now); + const auto& instrumentSet = subscription.getInstrumentSet(); + auto it = instrumentSet.begin(); + if (it != instrumentSet.end()) { + std::string settle; + std::string symbolId = *it; + if (UtilString::endsWith(symbolId, "_USD")) { + settle = "btc"; + } else if (UtilString::endsWith(symbolId, "_USDT")) { + settle = "usdt"; + } + auto credential = subscription.getCredential(); + if (credential.empty()) { + credential = that->credentialDefault; + } + const auto& proxyUrl = subscription.getProxyUrl(); - auto wsConnectionPtr = std::make_shared(that->baseUrlWs + settle, "", std::vector{subscription}, credential); - that->setWsConnectionStream(wsConnectionPtr); - CCAPI_LOGGER_WARN("about to subscribe with new wsConnectionPtr " + toString(*wsConnectionPtr)); - that->prepareConnect(wsConnectionPtr); - } - }); + auto wsConnectionPtr = std::make_shared(that->baseUrlWs + settle, "", std::vector{subscription}, credential, proxyUrl); + that->setWsConnectionStream(wsConnectionPtr); + CCAPI_LOGGER_WARN("about to subscribe with new wsConnectionPtr " + toString(*wsConnectionPtr)); + that->prepareConnect(wsConnectionPtr); + } + }); } } CCAPI_LOGGER_FUNCTION_EXIT; diff --git a/include/ccapi_cpp/service/ccapi_market_data_service.h b/include/ccapi_cpp/service/ccapi_market_data_service.h index 09ab9609..33a1afb1 100644 --- a/include/ccapi_cpp/service/ccapi_market_data_service.h +++ b/include/ccapi_cpp/service/ccapi_market_data_service.h @@ -59,21 +59,21 @@ class MarketDataService : public Service { CCAPI_LOGGER_FUNCTION_ENTER; if (this->shouldContinue.load()) { for (auto& x : this->groupSubscriptionListByInstrumentGroup(subscriptionList)) { - auto instrumentGroup = x.first; - auto subscriptionListGivenInstrumentGroup = x.second; + const auto& instrumentGroup = x.first; + auto& subscriptionListGivenInstrumentGroup = x.second; boost::asio::post(*this->serviceContextPtr->ioContextPtr, [that = shared_from_base(), instrumentGroup, subscriptionListGivenInstrumentGroup]() mutable { - auto now = UtilTime::now(); + const auto& now = UtilTime::now(); for (auto& subscription : subscriptionListGivenInstrumentGroup) { subscription.setTimeSent(now); } std::map> wsConnectionIdListByInstrumentGroupMap = invertMapMulti(that->instrumentGroupByWsConnectionIdMap); if (wsConnectionIdListByInstrumentGroupMap.find(instrumentGroup) != wsConnectionIdListByInstrumentGroupMap.end() && that->subscriptionStatusByInstrumentGroupInstrumentMap.find(instrumentGroup) != that->subscriptionStatusByInstrumentGroupInstrumentMap.end()) { - auto wsConnectionId = wsConnectionIdListByInstrumentGroupMap.at(instrumentGroup).at(0); - auto wsConnectionPtr = that->wsConnectionPtrByIdMap.at(wsConnectionId); + const auto& wsConnectionId = wsConnectionIdListByInstrumentGroupMap.at(instrumentGroup).at(0); + const auto& wsConnectionPtr = that->wsConnectionPtrByIdMap.at(wsConnectionId); for (const auto& subscription : subscriptionListGivenInstrumentGroup) { - auto instrument = subscription.getInstrument(); + const auto& instrument = subscription.getInstrument(); if (that->subscriptionStatusByInstrumentGroupInstrumentMap[instrumentGroup].find(instrument) != that->subscriptionStatusByInstrumentGroupInstrumentMap[instrumentGroup].end()) { that->onError(Event::Type::SUBSCRIPTION_STATUS, Message::Type::SUBSCRIPTION_FAILURE, "already subscribed: " + toString(subscription)); @@ -86,13 +86,15 @@ class MarketDataService : public Service { CCAPI_LOGGER_INFO("about to subscribe to exchange"); that->subscribeToExchange(wsConnectionPtr); } else { - auto url = UtilString::split(instrumentGroup, "|").at(0); + const auto& splittedInstrumentGroup = UtilString::split(instrumentGroup, "|"); + const auto& url = splittedInstrumentGroup.at(0); auto credential = subscriptionListGivenInstrumentGroup.at(0).getCredential(); if (credential.empty()) { credential = that->credentialDefault; } + const auto& proxyUrl = splittedInstrumentGroup.at(splittedInstrumentGroup.size() - 1); - auto wsConnectionPtr = std::make_shared(url, instrumentGroup, subscriptionListGivenInstrumentGroup, credential); + auto wsConnectionPtr = std::make_shared(url, instrumentGroup, subscriptionListGivenInstrumentGroup, credential, proxyUrl); that->setWsConnectionStream(wsConnectionPtr); CCAPI_LOGGER_WARN("about to subscribe with new wsConnectionPtr " + toString(*wsConnectionPtr)); that->prepareConnect(wsConnectionPtr); @@ -122,9 +124,11 @@ class MarketDataService : public Service { virtual std::string getInstrumentGroup(const Subscription& subscription) { const auto& field = subscription.getField(); if (field == CCAPI_GENERIC_PUBLIC_SUBSCRIPTION) { - return this->baseUrlWs + "|" + subscription.getField() + "|" + subscription.getCorrelationId() + "|" + subscription.getSerializedCredential(); + return this->baseUrlWs + "|" + subscription.getField() + "|" + subscription.getCorrelationId() + "|" + subscription.getSerializedCredential() + "|" + + subscription.getProxyUrl(); } else { - return this->baseUrlWs + "|" + subscription.getField() + "|" + subscription.getSerializedOptions() + "|" + subscription.getSerializedCredential(); + return this->baseUrlWs + "|" + subscription.getField() + "|" + subscription.getSerializedOptions() + "|" + subscription.getSerializedCredential() + "|" + + subscription.getProxyUrl(); } } diff --git a/include/ccapi_cpp/service/ccapi_service.h b/include/ccapi_cpp/service/ccapi_service.h index d860ce68..d44cf90b 100644 --- a/include/ccapi_cpp/service/ccapi_service.h +++ b/include/ccapi_cpp/service/ccapi_service.h @@ -976,8 +976,18 @@ class Service : public std::enable_shared_from_this { CCAPI_LOGGER_TRACE("wsConnectionPtr = " + wsConnectionPtr->toString()); CCAPI_LOGGER_TRACE("wsConnectionPtr->host = " + wsConnectionPtr->host); CCAPI_LOGGER_TRACE("wsConnectionPtr->port = " + wsConnectionPtr->port); - newResolverPtr->async_resolve(wsConnectionPtr->host, wsConnectionPtr->port, - beast::bind_front_handler(&Service::onResolveWs, shared_from_this(), wsConnectionPtr, newResolverPtr)); + CCAPI_LOGGER_TRACE("wsConnectionPtr->proxyUrl = " + wsConnectionPtr->proxyUrl); + std::string host; + std::string port; + if (wsConnectionPtr->proxyUrl.empty()) { + host = wsConnectionPtr->host; + port = wsConnectionPtr->port; + } else { + const auto& splitted = UtilString::split(wsConnectionPtr->proxyUrl, ':'); + host = splitted.at(0); + port = splitted.size() > 1 ? splitted.at(1) : CCAPI_HTTP_PORT_DEFAULT; + } + newResolverPtr->async_resolve(host, port, beast::bind_front_handler(&Service::onResolveWs, shared_from_this(), wsConnectionPtr, newResolverPtr)); } void onResolveWs(std::shared_ptr wsConnectionPtr, std::shared_ptr newResolverPtr, beast::error_code ec, From 9892199868828bb5d0894fbe593f683a4d5bbf07 Mon Sep 17 00:00:00 2001 From: hk Date: Mon, 22 Sep 2025 16:00:59 +0000 Subject: [PATCH 02/15] update README.md --- README.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index de800d94..be74d8d8 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,6 @@ - [Multiple subscription fields](#multiple-subscription-fields) - [Make Session::sendRequest blocking](#make-sessionsendrequest-blocking) - [Provide API credentials for an exchange](#provide-api-credentials-for-an-exchange) - - [Override exchange urls](#override-exchange-urls) - - [Connect to a proxy](#connect-to-a-proxy) - [Complex request parameters](#complex-request-parameters-1) - [Send request by Websocket API](#send-request-by-websocket-api) - [Specify instrument type](#specify-instrument-type) @@ -45,6 +43,8 @@ - [Set timer](#set-timer) - [Heartbeat](#heartbeat) - [Use multiple sessions](#use-multiple-sessions) + - [Override exchange urls](#override-exchange-urls) + - [Connect to a proxy](#connect-to-a-proxy) - [Performance Tuning](#performance-tuning) - [Known Issues and Workarounds](#known-issues-and-workarounds) @@ -768,15 +768,6 @@ Subscription subscription("okx", "BTC-USDT", "ORDER_UPDATE", "", "", { }); ``` -#### Override exchange urls -You can override exchange urls at compile time by using macros. See section "exchange REST urls", "exchange WS urls", and "exchange FIX urls" in [`include/ccapi_cpp/ccapi_macro.h`](include/ccapi_cpp/ccapi_macro.h). You can also override exchange urls at runtime. See [this example](example/src/override_exchange_url_at_runtime/main.cpp). These can be useful if you need to connect to test accounts (e.g. https://www.okx.com/docs-v5/en/#overview-demo-trading-services). - -#### Connect to a proxy -Instantiate `Subscription` with the desired `proxyUrl`. -``` -Subscription subscription("okx", "BTC-USDT", "MARKET_DEPTH", "", "", {}, "172.30.0.146:9000"); -``` - #### Complex request parameters Please follow the exchange's API documentations: e.g. https://www.okx.com/docs-v5/en/#order-book-trading-trade-post-place-order. ``` @@ -1080,6 +1071,14 @@ Subscription subscription("", "", "HEARTBEAT", "HEARTBEAT_INTERVAL_MILLISECONDS= session.subscribe(subscription); ``` +#### Override exchange urls +You can override exchange urls at compile time by using macros. See section "exchange REST urls", "exchange WS urls", and "exchange FIX urls" in [`include/ccapi_cpp/ccapi_macro.h`](include/ccapi_cpp/ccapi_macro.h). You can also override exchange urls at runtime. See [this example](example/src/override_exchange_url_at_runtime/main.cpp). These can be useful if you need to connect to test accounts (e.g. https://www.okx.com/docs-v5/en/#overview-demo-trading-services). + +#### Connect to a proxy +Instantiate `Subscription` with the desired `proxyUrl`. +``` +Subscription subscription("okx", "BTC-USDT", "MARKET_DEPTH", "", "", {}, "172.30.0.146:9000"); +``` ## Performance Tuning From 3b76d45782add58bb8f0c3f4b45b6964762dcac0 Mon Sep 17 00:00:00 2001 From: fedeitc <75090150+fedeitc@users.noreply.github.com> Date: Mon, 22 Sep 2025 16:17:45 +0200 Subject: [PATCH 03/15] fix reconnection in boost::asio code --- include/ccapi_cpp/service/ccapi_service.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/include/ccapi_cpp/service/ccapi_service.h b/include/ccapi_cpp/service/ccapi_service.h index d860ce68..1aae2076 100644 --- a/include/ccapi_cpp/service/ccapi_service.h +++ b/include/ccapi_cpp/service/ccapi_service.h @@ -1139,9 +1139,15 @@ class Service : public std::enable_shared_from_this { auto& connectionId = wsConnectionPtr->id; auto& readMessageBuffer = wsConnectionPtr->readMessageBuffer; if (ec) { - if (ec == beast::error::timeout) { + readMessageBuffer.consume(readMessageBuffer.size()); + if (ec == boost::asio::error::operation_aborted) { + return; + } else if (ec == beast::error::timeout) { CCAPI_LOGGER_TRACE("timeout, connection closed"); } + if (wsConnectionPtr->status == WsConnection::Status::CLOSING) { + return; + } CCAPI_LOGGER_TRACE("fail"); Event event; event.setType(Event::Type::SESSION_STATUS); @@ -1156,7 +1162,6 @@ class Service : public std::enable_shared_from_this { event.setMessageList({message}); this->eventHandler(event, nullptr); this->onFail(wsConnectionPtr); - readMessageBuffer.consume(readMessageBuffer.size()); return; } if (wsConnectionPtr->status != WsConnection::Status::OPEN) { @@ -1466,7 +1471,6 @@ class Service : public std::enable_shared_from_this { } else if (kind == boost::beast::websocket::frame_type::pong) { this->onPong(wsConnectionPtr, payload); } else if (kind == boost::beast::websocket::frame_type::close) { - this->onClose(wsConnectionPtr, {}); } } From 5ccc6749fe21b756bfe9d28f4b0f5fbbac15dc1d Mon Sep 17 00:00:00 2001 From: E Sequeira <5458743+geseq@users.noreply.github.com> Date: Mon, 29 Sep 2025 12:07:52 +0100 Subject: [PATCH 04/15] fix processing kraken MD subscriptions --- .../service/ccapi_market_data_service_kraken_futures.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/ccapi_cpp/service/ccapi_market_data_service_kraken_futures.h b/include/ccapi_cpp/service/ccapi_market_data_service_kraken_futures.h index e0b3120c..d9ebb2f5 100644 --- a/include/ccapi_cpp/service/ccapi_market_data_service_kraken_futures.h +++ b/include/ccapi_cpp/service/ccapi_market_data_service_kraken_futures.h @@ -73,7 +73,8 @@ class MarketDataServiceKrakenFutures : public MarketDataService { void processTextMessage(std::shared_ptr wsConnectionPtr, boost::beast::string_view textMessageView, const TimePoint& timeReceived, Event& event, std::vector& marketDataMessageList) override { - rj::Document document; + this->jsonDocumentAllocator.Clear(); + rj::Document document(&this->jsonDocumentAllocator); rj::Document::AllocatorType& allocator = document.GetAllocator(); document.Parse(textMessageView.data(), textMessageView.size()); if (document.HasMember("event")) { From 805236fe14c1b518d1abc44f44cc760ef1346cbc Mon Sep 17 00:00:00 2001 From: E Sequeira <5458743+geseq@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:25:29 +0100 Subject: [PATCH 05/15] fix processing kraken spot MD subscriptions --- include/ccapi_cpp/service/ccapi_market_data_service_kraken.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/ccapi_cpp/service/ccapi_market_data_service_kraken.h b/include/ccapi_cpp/service/ccapi_market_data_service_kraken.h index 2c5df2c9..3462d395 100644 --- a/include/ccapi_cpp/service/ccapi_market_data_service_kraken.h +++ b/include/ccapi_cpp/service/ccapi_market_data_service_kraken.h @@ -141,7 +141,8 @@ class MarketDataServiceKraken : public MarketDataService { void processTextMessage(std::shared_ptr wsConnectionPtr, boost::beast::string_view textMessageView, const TimePoint& timeReceived, Event& event, std::vector& marketDataMessageList) override { - rj::Document document; + this->jsonDocumentAllocator.Clear(); + rj::Document document(&this->jsonDocumentAllocator); rj::Document::AllocatorType& allocator = document.GetAllocator(); document.Parse(textMessageView.data(), textMessageView.size()); if (document.IsArray() && document.Size() >= 4 && document.Size() <= 5) { From dccd511c5c95bc81f7da276b081ce5605062d029 Mon Sep 17 00:00:00 2001 From: hk Date: Wed, 1 Oct 2025 22:48:11 +0000 Subject: [PATCH 06/15] fix bugs in proxy urls --- .../ccapi_market_data_service_gateio_perpetual_futures.h | 2 +- include/ccapi_cpp/service/ccapi_market_data_service_gemini.h | 2 +- .../ccapi_cpp/service/ccapi_market_data_service_huobi_base.h | 2 +- include/ccapi_cpp/service/ccapi_market_data_service_okx.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/ccapi_cpp/service/ccapi_market_data_service_gateio_perpetual_futures.h b/include/ccapi_cpp/service/ccapi_market_data_service_gateio_perpetual_futures.h index eeee64f5..d02d680b 100644 --- a/include/ccapi_cpp/service/ccapi_market_data_service_gateio_perpetual_futures.h +++ b/include/ccapi_cpp/service/ccapi_market_data_service_gateio_perpetual_futures.h @@ -41,7 +41,7 @@ class MarketDataServiceGateioPerpetualFutures : public MarketDataServiceGateioBa } else if (UtilString::endsWith(instrument, "_USDT")) { url += "usdt"; } - return url + "|" + subscription.getField() + "|" + subscription.getSerializedOptions(); + return url + "|" + subscription.getField() + "|" + subscription.getSerializedOptions() + "|" + subscription.getProxyUrl(); } void substituteParamSettle(std::string& target, const std::map& param, const std::string& symbolId) { diff --git a/include/ccapi_cpp/service/ccapi_market_data_service_gemini.h b/include/ccapi_cpp/service/ccapi_market_data_service_gemini.h index 016183c1..6c2b5422 100644 --- a/include/ccapi_cpp/service/ccapi_market_data_service_gemini.h +++ b/include/ccapi_cpp/service/ccapi_market_data_service_gemini.h @@ -216,7 +216,7 @@ class MarketDataServiceGemini : public MarketDataService { } url += parameter + "=true"; } - return url; + return url + "|" + subscription.getProxyUrl(); } void convertRequestForRest(http::request& req, const Request& request, const TimePoint& now, const std::string& symbolId, diff --git a/include/ccapi_cpp/service/ccapi_market_data_service_huobi_base.h b/include/ccapi_cpp/service/ccapi_market_data_service_huobi_base.h index 94dba75a..329ba201 100644 --- a/include/ccapi_cpp/service/ccapi_market_data_service_huobi_base.h +++ b/include/ccapi_cpp/service/ccapi_market_data_service_huobi_base.h @@ -102,7 +102,7 @@ class MarketDataServiceHuobiBase : public MarketDataService { url += "/ws"; } } - return url + "|" + field + "|" + subscription.getSerializedOptions(); + return url + "|" + field + "|" + subscription.getSerializedOptions() + "|" + subscription.getProxyUrl(); } void processTextMessage(std::shared_ptr wsConnectionPtr, boost::beast::string_view textMessageView, const TimePoint& timeReceived, Event& event, diff --git a/include/ccapi_cpp/service/ccapi_market_data_service_okx.h b/include/ccapi_cpp/service/ccapi_market_data_service_okx.h index 5c6f7cbd..828f9695 100644 --- a/include/ccapi_cpp/service/ccapi_market_data_service_okx.h +++ b/include/ccapi_cpp/service/ccapi_market_data_service_okx.h @@ -45,7 +45,7 @@ class MarketDataServiceOkx : public MarketDataService { baseUrlWsGivenSubscription = this->sessionConfigs.getUrlWebsocketBase().at(this->exchangeName) + CCAPI_OKX_BUSINESS_WS_PATH; } return baseUrlWsGivenSubscription + "|" + subscription.getField() + "|" + subscription.getSerializedOptions() + "|" + - subscription.getSerializedCredential(); + subscription.getSerializedCredential() + "|" + subscription.getProxyUrl(); } bool doesHttpBodyContainError(boost::beast::string_view bodyView) override { From d4fb51cd389dfef9ae2a5f098735fa9ebcbab030 Mon Sep 17 00:00:00 2001 From: hk Date: Wed, 1 Oct 2025 23:17:28 +0000 Subject: [PATCH 07/15] fix port for hostHttpHeaderValue when using proxy --- include/ccapi_cpp/service/ccapi_service.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/ccapi_cpp/service/ccapi_service.h b/include/ccapi_cpp/service/ccapi_service.h index d44cf90b..46444abe 100644 --- a/include/ccapi_cpp/service/ccapi_service.h +++ b/include/ccapi_cpp/service/ccapi_service.h @@ -1040,8 +1040,7 @@ class Service : public std::enable_shared_from_this { CCAPI_LOGGER_TRACE("connected"); CCAPI_LOGGER_TRACE("ep.port() = " + std::to_string(ep.port())); - wsConnectionPtr->hostHttpHeaderValue = - this->hostHttpHeaderValueIgnorePort ? wsConnectionPtr->host : wsConnectionPtr->host + ':' + std::to_string(ep.port()); + wsConnectionPtr->hostHttpHeaderValue = this->hostHttpHeaderValueIgnorePort ? wsConnectionPtr->host : wsConnectionPtr->host + ':' + wsConnectionPtr->port; CCAPI_LOGGER_TRACE("wsConnectionPtr->hostHttpHeaderValue = " + wsConnectionPtr->hostHttpHeaderValue); From f3dfb323201e9253c2498ea3dba475849a4e3d47 Mon Sep 17 00:00:00 2001 From: cryptochassis <57077778+cryptochassis@users.noreply.github.com> Date: Tue, 7 Oct 2025 20:22:14 -0700 Subject: [PATCH 08/15] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9b29797b..1056800d 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ * FIX: [binance](https://accounts.maxweb.academy/register?ref=1116718520), coinbase, gemini. * Join us on Discord https://discord.gg/b5EKcp9s8T and Medium https://cryptochassis.medium.com. * For any questions, email hello@cryptochassis.com. +* We’re experts in market data collection, high-speed trading system, infrastructure optimization, and proprietary market making. Hire us as engineers, liquidity providers, traders, or asset managers. ## Branches * The `develop` branch may contain experimental features. From 271dda52636c03529b7c8548765b105782b5f51b Mon Sep 17 00:00:00 2001 From: fedeitc <75090150+fedeitc@users.noreply.github.com> Date: Mon, 13 Oct 2025 16:11:13 +0200 Subject: [PATCH 09/15] fix normalizeDecimalString preserve trailing zeroes of exponent --- include/ccapi_cpp/ccapi_util_private.h | 4 ++-- test/test_unit/src/common/util/ccapi_util_test.cpp | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/ccapi_cpp/ccapi_util_private.h b/include/ccapi_cpp/ccapi_util_private.h index 6cd11efd..655a1d91 100644 --- a/include/ccapi_cpp/ccapi_util_private.h +++ b/include/ccapi_cpp/ccapi_util_private.h @@ -281,7 +281,7 @@ class UtilString { } static std::string normalizeDecimalString(const std::string& original) { - if (original.find('.') != std::string::npos) { + if (original.find('.') != std::string::npos && original.find_first_of("Ee") == std::string::npos) { std::string str(original); rtrimInPlace(str, "0"); rtrimInPlace(str, "."); @@ -303,7 +303,7 @@ class UtilString { static std::string_view normalizeDecimalStringView(std::string_view input) { // Quick check for dot size_t dotPos = input.find('.'); - if (dotPos == std::string_view::npos) return input; + if (dotPos == std::string_view::npos || input.find_first_of("Ee") != std::string::npos) return input; size_t end = input.size(); diff --git a/test/test_unit/src/common/util/ccapi_util_test.cpp b/test/test_unit/src/common/util/ccapi_util_test.cpp index ebf9d094..ffd7ad5d 100644 --- a/test/test_unit/src/common/util/ccapi_util_test.cpp +++ b/test/test_unit/src/common/util/ccapi_util_test.cpp @@ -129,6 +129,12 @@ TEST(UtilStringTest, normalizeDecimalString_5) { EXPECT_EQ(UtilString::normalizeDecimalString(original.c_str()), "1.1"); } +TEST(UtilStringTest, normalizeDecimalString_6) { + std::string original("1.1e10"); + EXPECT_EQ(UtilString::normalizeDecimalString(original), "1.1e10"); + EXPECT_EQ(UtilString::normalizeDecimalStringView(original), "1.1e10"); +} + TEST(UtilStringTest, toUpper) { std::string original("ab"); EXPECT_EQ(UtilString::toUpper(original), "AB"); From 7cf18e647ace1539c8e3691e62209f133c4c5d98 Mon Sep 17 00:00:00 2001 From: fedeitc <75090150+fedeitc@users.noreply.github.com> Date: Mon, 13 Oct 2025 16:12:21 +0200 Subject: [PATCH 10/15] huobi deriv de-dupe createEvent code already in extractOrderInfo --- ...execution_management_service_huobi_derivatives_base.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_derivatives_base.h b/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_derivatives_base.h index 12773cc2..1a9d3907 100644 --- a/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_derivatives_base.h +++ b/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_derivatives_base.h @@ -351,15 +351,6 @@ class ExecutionManagementServiceHuobiDerivativesBase : public ExecutionManagemen }; Element info; this->extractOrderInfo(info, document, extractionFieldNameMap); - { - auto it1 = document.FindMember("trade_volume"); - auto it2 = document.FindMember("trade_avg_price"); - if (it1 != document.MemberEnd() && it2 != document.MemberEnd()) { - info.insert( - CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUOTE_QUANTITY, - ConvertDecimalToString(Decimal(UtilString::printDoubleScientific(std::stod(it1->value.GetString()) * std::stod(it2->value.GetString()))))); - } - } for (const auto& x : document["trade"].GetArray()) { info.insert(CCAPI_TRADE_ID, std::string(x["trade_id"].GetString())); info.insert(CCAPI_EM_ORDER_LAST_EXECUTED_PRICE, x["trade_price"].GetString()); From f1aa9dc4a8134cb4ed3a18779ae050abf3c0828a Mon Sep 17 00:00:00 2001 From: fedeitc <75090150+fedeitc@users.noreply.github.com> Date: Mon, 13 Oct 2025 16:12:46 +0200 Subject: [PATCH 11/15] huobi deriv use orders topic for ORDER_UPDATE and PRIVATE_TRADE --- include/ccapi_cpp/ccapi_macro.h | 8 ------ ...ution_management_service_huobi_coin_swap.h | 1 - ...anagement_service_huobi_derivatives_base.h | 25 +++++++------------ ...ution_management_service_huobi_usdt_swap.h | 1 - 4 files changed, 9 insertions(+), 26 deletions(-) diff --git a/include/ccapi_cpp/ccapi_macro.h b/include/ccapi_cpp/ccapi_macro.h index 6fd9605a..610cb737 100644 --- a/include/ccapi_cpp/ccapi_macro.h +++ b/include/ccapi_cpp/ccapi_macro.h @@ -1160,10 +1160,6 @@ #define CCAPI_HUOBI_USDT_SWAP_SUBSCRIBE_ORDER_DATA_TOPIC "orders_cross" #endif -#ifndef CCAPI_HUOBI_USDT_SWAP_SUBSCRIBE_MATCH_ORDER_DATA_TOPIC -#define CCAPI_HUOBI_USDT_SWAP_SUBSCRIBE_MATCH_ORDER_DATA_TOPIC "matchOrders_cross" -#endif - #ifndef CCAPI_HUOBI_COIN_SWAP_URL_WS_BASE #define CCAPI_HUOBI_COIN_SWAP_URL_WS_BASE "wss://api.hbdm.com" #endif @@ -1172,10 +1168,6 @@ #define CCAPI_HUOBI_COIN_SWAP_SUBSCRIBE_ORDER_DATA_TOPIC "orders" #endif -#ifndef CCAPI_HUOBI_COIN_SWAP_SUBSCRIBE_MATCH_ORDER_DATA_TOPIC -#define CCAPI_HUOBI_COIN_SWAP_SUBSCRIBE_MATCH_ORDER_DATA_TOPIC "matchOrders" -#endif - #ifndef CCAPI_OKX_URL_WS_BASE #define CCAPI_OKX_URL_WS_BASE "wss://ws.okx.com:8443" #endif diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_coin_swap.h b/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_coin_swap.h index 3a37e186..7f8f1d96 100644 --- a/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_coin_swap.h +++ b/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_coin_swap.h @@ -27,7 +27,6 @@ class ExecutionManagementServiceHuobiCoinSwap : public ExecutionManagementServic this->getAccountPositionsTarget = CCAPI_HUOBI_COIN_SWAP_GET_ACCOUNT_POSITIONS_PATH; this->authenticationPath = "/swap-api/v1/swap_order"; this->orderDataTopic = CCAPI_HUOBI_COIN_SWAP_SUBSCRIBE_ORDER_DATA_TOPIC; - this->matchOrderDataTopic = CCAPI_HUOBI_COIN_SWAP_SUBSCRIBE_MATCH_ORDER_DATA_TOPIC; } virtual ~ExecutionManagementServiceHuobiCoinSwap() {} diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_derivatives_base.h b/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_derivatives_base.h index 1a9d3907..6400986c 100644 --- a/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_derivatives_base.h +++ b/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_derivatives_base.h @@ -269,17 +269,13 @@ class ExecutionManagementServiceHuobiDerivativesBase : public ExecutionManagemen std::string errCode = document["err-code"].GetString(); if (errCode == "0") { for (const auto& instrument : instrumentSet) { - for (const auto& field : fieldSet) { + if (fieldSet.find(CCAPI_EM_ORDER_UPDATE) != fieldSet.end() || fieldSet.find(CCAPI_EM_PRIVATE_TRADE) != fieldSet.end()) { rj::Document document; document.SetObject(); auto& allocator = document.GetAllocator(); document.AddMember("op", rj::Value("sub").Move(), allocator); std::string topic; - if (field == CCAPI_EM_ORDER_UPDATE) { - topic = this->orderDataTopic + "." + instrument; - } else if (field == CCAPI_EM_PRIVATE_TRADE) { - topic = this->matchOrderDataTopic + "." + instrument; - } + topic = this->orderDataTopic + "." + instrument; document.AddMember("topic", rj::Value(topic.c_str(), allocator).Move(), allocator); rj::StringBuffer stringBufferSubscribe; rj::Writer writerSubscribe(stringBufferSubscribe); @@ -334,14 +330,13 @@ class ExecutionManagementServiceHuobiDerivativesBase : public ExecutionManagemen message.setTimeReceived(timeReceived); message.setCorrelationIdList({subscription.getCorrelationId()}); std::string topic = document["topic"].GetString(); - if (topic.rfind(this->orderDataTopic + ".", 0) == 0 && fieldSet.find(CCAPI_EM_ORDER_UPDATE) != fieldSet.end()) { + if (fieldSet.find(CCAPI_EM_ORDER_UPDATE) != fieldSet.end()) { std::string instrument = document["contract_code"].GetString(); if (instrumentSet.empty() || instrumentSet.find(instrument) != instrumentSet.end()) { message.setTime(UtilTime::makeTimePointFromMilliseconds(std::stoll(document["ts"].GetString()))); message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_ORDER_UPDATE); const std::map>& extractionFieldNameMap = { {CCAPI_EM_ORDER_ID, std::make_pair("order_id", JsonDataType::STRING)}, - {CCAPI_EM_CLIENT_ORDER_ID, std::make_pair("client_order_id", JsonDataType::STRING)}, {CCAPI_EM_ORDER_SIDE, std::make_pair("direction", JsonDataType::STRING)}, {CCAPI_EM_ORDER_LIMIT_PRICE, std::make_pair("price", JsonDataType::STRING)}, {CCAPI_EM_ORDER_QUANTITY, std::make_pair("volume", JsonDataType::STRING)}, @@ -351,18 +346,17 @@ class ExecutionManagementServiceHuobiDerivativesBase : public ExecutionManagemen }; Element info; this->extractOrderInfo(info, document, extractionFieldNameMap); - for (const auto& x : document["trade"].GetArray()) { - info.insert(CCAPI_TRADE_ID, std::string(x["trade_id"].GetString())); - info.insert(CCAPI_EM_ORDER_LAST_EXECUTED_PRICE, x["trade_price"].GetString()); - info.insert(CCAPI_EM_ORDER_LAST_EXECUTED_SIZE, x["trade_volume"].GetString()); - info.insert(CCAPI_IS_MAKER, std::string_view(x["role"].GetString()) == "maker" ? "1" : "0"); + auto it = document.FindMember("client_order_id"); + if (it != document.MemberEnd() && it->value.IsString()) { + info.insert(CCAPI_EM_CLIENT_ORDER_ID, it->value.GetString()); } std::vector elementList; elementList.emplace_back(std::move(info)); message.setElementList(elementList); messageList.emplace_back(std::move(message)); } - } else if (topic.rfind(this->matchOrderDataTopic + ".", 0) == 0 && fieldSet.find(CCAPI_EM_PRIVATE_TRADE) != fieldSet.end()) { + } + if (fieldSet.find(CCAPI_EM_PRIVATE_TRADE) != fieldSet.end()) { std::string instrument = document["contract_code"].GetString(); if (instrumentSet.empty() || instrumentSet.find(instrument) != instrumentSet.end()) { std::string orderSide = std::string_view(document["direction"].GetString()) == "buy" ? CCAPI_EM_ORDER_SIDE_BUY : CCAPI_EM_ORDER_SIDE_SELL; @@ -370,7 +364,7 @@ class ExecutionManagementServiceHuobiDerivativesBase : public ExecutionManagemen std::string orderId = document["order_id"].GetString(); std::string clientOrderId; auto it = document.FindMember("client_order_id"); - if (!it->value.IsNull()) { + if (it != document.MemberEnd() && it->value.IsString()) { clientOrderId = it->value.GetString(); } for (const auto& x : document["trade"].GetArray()) { @@ -422,7 +416,6 @@ class ExecutionManagementServiceHuobiDerivativesBase : public ExecutionManagemen std::string authenticationPath; std::string orderDataTopic; - std::string matchOrderDataTopic; }; } /* namespace ccapi */ diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_usdt_swap.h b/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_usdt_swap.h index 7334ba55..3245911e 100644 --- a/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_usdt_swap.h +++ b/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_usdt_swap.h @@ -27,7 +27,6 @@ class ExecutionManagementServiceHuobiUsdtSwap : public ExecutionManagementServic this->getAccountPositionsTarget = CCAPI_HUOBI_USDT_SWAP_GET_ACCOUNT_POSITIONS_PATH; this->authenticationPath = "/linear-swap-notification"; this->orderDataTopic = CCAPI_HUOBI_USDT_SWAP_SUBSCRIBE_ORDER_DATA_TOPIC; - this->matchOrderDataTopic = CCAPI_HUOBI_USDT_SWAP_SUBSCRIBE_MATCH_ORDER_DATA_TOPIC; } virtual ~ExecutionManagementServiceHuobiUsdtSwap() {} From 75360bd3f32ff5c9cc0a0e4f8107faa3838a1b20 Mon Sep 17 00:00:00 2001 From: fedeitc <75090150+fedeitc@users.noreply.github.com> Date: Mon, 13 Oct 2025 16:12:52 +0200 Subject: [PATCH 12/15] fix huobi derivatives set lever_rate only when no ORDER_LEVERAGE --- .../ccapi_execution_management_service_huobi_derivatives_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_derivatives_base.h b/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_derivatives_base.h index 6400986c..60e39d6a 100644 --- a/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_derivatives_base.h +++ b/include/ccapi_cpp/service/ccapi_execution_management_service_huobi_derivatives_base.h @@ -62,7 +62,7 @@ class ExecutionManagementServiceHuobiDerivativesBase : public ExecutionManagemen if (param.find("offset") == param.end()) { document.AddMember("offset", rj::Value("open").Move(), allocator); } - if (param.find("lever_rate") == param.end()) { + if (param.find("lever_rate") == param.end() && param.find(CCAPI_EM_ORDER_LEVERAGE) == param.end()) { document.AddMember("lever_rate", rj::Value("1").Move(), allocator); } if (param.find("order_price_type") == param.end()) { From 2e87306dfe4456c803465a3289e1535da235d939 Mon Sep 17 00:00:00 2001 From: a <57077778+cryptochassis@users.noreply.github.com> Date: Fri, 31 Oct 2025 13:31:07 -0700 Subject: [PATCH 13/15] draft --- example/CMakeLists.txt | 1 + example/src/reduce_build_time/CMakeLists.txt | 6 ++++ example/src/reduce_build_time/main.cpp | 38 ++++++++++++++++++++ example/src/reduce_build_time/my_session.cpp | 14 ++++++++ example/src/reduce_build_time/my_session.h | 25 +++++++++++++ include/ccapi_cpp/ccapi_inflate_stream.h | 6 ++-- include/ccapi_cpp/ccapi_url.h | 20 +++++------ test/test_build/test.cpp | 2 +- 8 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 example/src/reduce_build_time/CMakeLists.txt create mode 100644 example/src/reduce_build_time/main.cpp create mode 100644 example/src/reduce_build_time/my_session.cpp create mode 100644 example/src/reduce_build_time/my_session.h diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 53f42f1f..c4649b5b 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -87,3 +87,4 @@ add_subdirectory(src/override_exchange_url_at_runtime) add_subdirectory(src/test_order_latency) add_subdirectory(src/test_cpu_usage) add_subdirectory(src/use_multiple_sessions) +add_subdirectory(src/reduce_build_time) diff --git a/example/src/reduce_build_time/CMakeLists.txt b/example/src/reduce_build_time/CMakeLists.txt new file mode 100644 index 00000000..0f6ad54b --- /dev/null +++ b/example/src/reduce_build_time/CMakeLists.txt @@ -0,0 +1,6 @@ +set(NAME reduce_build_time) +project(${NAME}) +add_compile_definitions(CCAPI_ENABLE_SERVICE_MARKET_DATA) +add_compile_definitions(CCAPI_ENABLE_EXCHANGE_OKX) +add_executable(${NAME} main.cpp my_session.cpp) +add_dependencies(${NAME} boost rapidjson) diff --git a/example/src/reduce_build_time/main.cpp b/example/src/reduce_build_time/main.cpp new file mode 100644 index 00000000..81a1537b --- /dev/null +++ b/example/src/reduce_build_time/main.cpp @@ -0,0 +1,38 @@ +#include "ccapi_cpp/ccapi_event_handler.h" +#include "my_session.h" + +namespace ccapi { + +Logger* Logger::logger = nullptr; // This line is needed. + +class MyEventHandler : public EventHandler { + public: + void processEvent(const Event& event, Session*) override { std::cout << "Received an event:\n" + event.toPrettyString(2, 2) << std::endl; } + + void setMySessionPtr(MySession* mySessionPtr) { this->mySessionPtr = mySessionPtr; } + + private: + MySession* mySessionPtr{nullptr}; +}; + +} /* namespace ccapi */ + +using ::ccapi::MyEventHandler; +using ::ccapi::MySession; +using ::ccapi::SessionConfigs; +using ::ccapi::SessionOptions; +using ::ccapi::Subscription; + +int main(int argc, char** argv) { + SessionOptions sessionOptions; + SessionConfigs sessionConfigs; + MyEventHandler eventHandler; + MySession mySession(sessionOptions, sessionConfigs, &eventHandler); + eventHandler.setMySessionPtr(&mySession); + Subscription subscription("okx", "BTC-USDT", "MARKET_DEPTH"); + mySession.subscribe(subscription); + std::this_thread::sleep_for(std::chrono::seconds(10)); + mySession.stop(); + std::cout << "Bye" << std::endl; + return EXIT_SUCCESS; +} diff --git a/example/src/reduce_build_time/my_session.cpp b/example/src/reduce_build_time/my_session.cpp new file mode 100644 index 00000000..33c8c526 --- /dev/null +++ b/example/src/reduce_build_time/my_session.cpp @@ -0,0 +1,14 @@ +#include "my_session.h" + +#include "ccapi_cpp/ccapi_session.h" + +namespace ccapi { + +MySession::MySession(const SessionOptions& sessionOptions, const SessionConfigs& sessionConfigs, EventHandler* eventHandler) + : ccapiSessionPtr(new Session(sessionOptions, sessionConfigs, eventHandler)) {} + +void MySession::subscribe(Subscription& subscription) { this->ccapiSessionPtr->subscribe(subscription); } + +void MySession::stop() { this->ccapiSessionPtr->stop(); } + +} // namespace ccapi diff --git a/example/src/reduce_build_time/my_session.h b/example/src/reduce_build_time/my_session.h new file mode 100644 index 00000000..35c78b46 --- /dev/null +++ b/example/src/reduce_build_time/my_session.h @@ -0,0 +1,25 @@ +#ifndef EXAMPLE_SRC_REDUCE_BUILD_TIME_MY_SESSION_H_ +#define EXAMPLE_SRC_REDUCE_BUILD_TIME_MY_SESSION_H_ + +#include "ccapi_cpp/ccapi_session_configs.h" +#include "ccapi_cpp/ccapi_session_options.h" +#include "ccapi_cpp/ccapi_subscription.h" + +namespace ccapi { + +class EventHandler; +class Session; + +class MySession { + public: + explicit MySession(const SessionOptions& sessionOptions, const SessionConfigs& sessionConfigs, EventHandler* eventHandler); + void subscribe(Subscription& subscription); + void stop(); + + private: + Session* ccapiSessionPtr{nullptr}; +}; + +} // namespace ccapi + +#endif // EXAMPLE_SRC_REDUCE_BUILD_TIME_MY_SESSION_H_ diff --git a/include/ccapi_cpp/ccapi_inflate_stream.h b/include/ccapi_cpp/ccapi_inflate_stream.h index c1e6bf55..80d646eb 100644 --- a/include/ccapi_cpp/ccapi_inflate_stream.h +++ b/include/ccapi_cpp/ccapi_inflate_stream.h @@ -58,14 +58,14 @@ class InflateStream { return boost::system::error_code(); } - boost::system::error_code decompress(uint8_t const *buf, size_t len, std::string &out) { + boost::system::error_code decompress(uint8_t const* buf, size_t len, std::string& out) { if (!this->initialized) { CCAPI_LOGGER_ERROR("decompress error"); return boost::system::error_code(); } this->istate.avail_in = len; - this->istate.next_in = const_cast(buf); + this->istate.next_in = const_cast(buf); do { this->istate.avail_out = this->decompressBufferSize; this->istate.next_out = this->buffer.get(); @@ -74,7 +74,7 @@ class InflateStream { CCAPI_LOGGER_ERROR("decompress error"); return boost::system::error_code(); } - out.append(reinterpret_cast(this->buffer.get()), this->decompressBufferSize - this->istate.avail_out); + out.append(reinterpret_cast(this->buffer.get()), this->decompressBufferSize - this->istate.avail_out); } while (this->istate.avail_out == 0); return boost::system::error_code(); } diff --git a/include/ccapi_cpp/ccapi_url.h b/include/ccapi_cpp/ccapi_url.h index 53f54121..a39fdadd 100644 --- a/include/ccapi_cpp/ccapi_url.h +++ b/include/ccapi_cpp/ccapi_url.h @@ -31,7 +31,7 @@ class Url { return output; } - static std::string urlEncode(const std::string &value) { + static std::string urlEncode(const std::string& value) { std::ostringstream escaped; escaped.fill('0'); escaped << std::hex; @@ -50,7 +50,7 @@ class Url { return escaped.str(); } - static std::string urlDecode(const std::string &value) { + static std::string urlDecode(const std::string& value) { std::string ret; char ch; int i, ii; @@ -67,18 +67,18 @@ class Url { return (ret); } - static std::map convertQueryStringToMap(const std::string &input) { + static std::map convertQueryStringToMap(const std::string& input) { std::map output; - for (const auto &x : UtilString::split(input, "&")) { + for (const auto& x : UtilString::split(input, "&")) { auto y = UtilString::split(x, "="); output.insert(std::make_pair(y.at(0), Url::urlDecode(y.at(1)))); } return output; } - static std::string convertMapToQueryString(const std::map &input) { + static std::string convertMapToQueryString(const std::map& input) { std::string output; - for (const auto &x : input) { + for (const auto& x : input) { output += x.first; output += "="; output += x.second; @@ -90,10 +90,10 @@ class Url { return output; } - static std::string convertMapToFormUrlEncoded(const std::map &input) { + static std::string convertMapToFormUrlEncoded(const std::map& input) { std::string output; int i = 0; - for (const auto &x : input) { + for (const auto& x : input) { output += Url::urlEncode(x.first); output += "="; output += Url::urlEncode(x.second); @@ -104,9 +104,9 @@ class Url { return output; } - static std::map convertFormUrlEncodedToMap(const std::string &input) { + static std::map convertFormUrlEncodedToMap(const std::string& input) { std::map output; - for (const auto &x : UtilString::split(input, "&")) { + for (const auto& x : UtilString::split(input, "&")) { auto y = UtilString::split(x, "="); output.insert(std::make_pair(Url::urlDecode(y.at(0)), Url::urlDecode(y.at(1)))); } diff --git a/test/test_build/test.cpp b/test/test_build/test.cpp index 02764e60..5e330e63 100644 --- a/test/test_build/test.cpp +++ b/test/test_build/test.cpp @@ -1,7 +1,7 @@ #include "ccapi_cpp/ccapi_session.h" using ::ccapi::Session; -int main(int argc, char **argv) { +int main(int argc, char** argv) { Session session; return EXIT_SUCCESS; } From cf68e23bfa856207698c1b0b860e03920f8ff1fc Mon Sep 17 00:00:00 2001 From: cc <57077778+cryptochassis@users.noreply.github.com> Date: Fri, 31 Oct 2025 13:43:29 -0700 Subject: [PATCH 14/15] update README.md --- README.md | 3 +++ example/src/reduce_build_time/my_session.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e58c5e74..69891b92 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ - [Use multiple sessions](#use-multiple-sessions) - [Override exchange urls](#override-exchange-urls) - [Connect to a proxy](#connect-to-a-proxy) + - [Reduce build time](#reduce-build-time) - [Performance Tuning](#performance-tuning) - [Known Issues and Workarounds](#known-issues-and-workarounds) @@ -1081,6 +1082,8 @@ Instantiate `Subscription` with the desired `proxyUrl`. Subscription subscription("okx", "BTC-USDT", "MARKET_DEPTH", "", "", {}, "172.30.0.146:9000"); ``` +#### Reduce build time +The Pimpl (Pointer to Implementation) idiom in C++ can significantly reduce build time. This reduction is achieved by minimizing compilation dependencies and isolating implementation details. See [this example](example/src/reduce_build_time). ## Performance Tuning * Turn on compiler optimization flags (e.g. `cmake -DCMAKE_BUILD_TYPE=Release ...`). diff --git a/example/src/reduce_build_time/my_session.h b/example/src/reduce_build_time/my_session.h index 35c78b46..dc206f23 100644 --- a/example/src/reduce_build_time/my_session.h +++ b/example/src/reduce_build_time/my_session.h @@ -8,7 +8,7 @@ namespace ccapi { class EventHandler; -class Session; +class Session; // forward declaration instead of including full header! class MySession { public: From b9e749b380f7764a94d62059a538030f7631f207 Mon Sep 17 00:00:00 2001 From: x-mass <36629999+x-mass@users.noreply.github.com> Date: Sun, 16 Nov 2025 14:07:11 +0000 Subject: [PATCH 15/15] refactor: include inflate stream only when needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `ccapi_inflate_stream.h` was included unconditionally even though it’s only used for specific exchanges, making zlib a hard dependency. Guard the include so zlib is required only when relevant; also replace the duplicated conditions with a shared macro for reuse. --- include/ccapi_cpp/service/ccapi_service.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/ccapi_cpp/service/ccapi_service.h b/include/ccapi_cpp/service/ccapi_service.h index 99d884f3..6edbdd76 100644 --- a/include/ccapi_cpp/service/ccapi_service.h +++ b/include/ccapi_cpp/service/ccapi_service.h @@ -27,6 +27,12 @@ #define CCAPI_WEBSOCKET_WRITE_BUFFER_SIZE (1 << 20) #endif +#define CCAPI_REQUIRES_INFLATE_STREAM \ + ((defined(CCAPI_ENABLE_SERVICE_MARKET_DATA) && \ + (defined(CCAPI_ENABLE_EXCHANGE_HUOBI) || defined(CCAPI_ENABLE_EXCHANGE_HUOBI_USDT_SWAP) || defined(CCAPI_ENABLE_EXCHANGE_HUOBI_COIN_SWAP))) || \ + (defined(CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT) && \ + (defined(CCAPI_ENABLE_EXCHANGE_HUOBI_USDT_SWAP) || defined(CCAPI_ENABLE_EXCHANGE_HUOBI_COIN_SWAP) || defined(CCAPI_ENABLE_EXCHANGE_BITMART)))) + #include #include "boost/asio/strand.hpp" @@ -50,7 +56,9 @@ #include "ccapi_cpp/ccapi_fix_connection.h" #include "ccapi_cpp/ccapi_http_connection.h" #include "ccapi_cpp/ccapi_http_retry.h" +#if CCAPI_REQUIRES_INFLATE_STREAM #include "ccapi_cpp/ccapi_inflate_stream.h" +#endif #include "ccapi_cpp/ccapi_queue.h" #include "ccapi_cpp/ccapi_request.h" #include "ccapi_cpp/ccapi_session_configs.h" @@ -1437,10 +1445,7 @@ class Service : public std::enable_shared_from_this { } else if (stream.got_binary()) { CCAPI_LOGGER_DEBUG("received a binary message: " + UtilAlgorithm::stringToHex(std::string(data, dataSize))); -#if defined(CCAPI_ENABLE_SERVICE_MARKET_DATA) && \ - (defined(CCAPI_ENABLE_EXCHANGE_HUOBI) || defined(CCAPI_ENABLE_EXCHANGE_HUOBI_USDT_SWAP) || defined(CCAPI_ENABLE_EXCHANGE_HUOBI_COIN_SWAP)) || \ - defined(CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT) && \ - (defined(CCAPI_ENABLE_EXCHANGE_HUOBI_USDT_SWAP) || defined(CCAPI_ENABLE_EXCHANGE_HUOBI_COIN_SWAP) || defined(CCAPI_ENABLE_EXCHANGE_BITMART)) +#if CCAPI_REQUIRES_INFLATE_STREAM if (this->needDecompressWebsocketMessage) { std::string decompressed; @@ -1660,13 +1665,8 @@ class Service : public std::enable_shared_from_this { // std::regex convertNumberToStringInJsonRegex{"(\\[|,|\":)\\s?(-?\\d+\\.?\\d*)"}; // std::string convertNumberToStringInJsonRewrite{"$1\"$2\""}; bool needDecompressWebsocketMessage{}; -#if defined(CCAPI_ENABLE_SERVICE_MARKET_DATA) && \ - (defined(CCAPI_ENABLE_EXCHANGE_HUOBI) || defined(CCAPI_ENABLE_EXCHANGE_HUOBI_USDT_SWAP) || defined(CCAPI_ENABLE_EXCHANGE_HUOBI_COIN_SWAP)) || \ - defined(CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT) && \ - (defined(CCAPI_ENABLE_EXCHANGE_HUOBI_USDT_SWAP) || defined(CCAPI_ENABLE_EXCHANGE_HUOBI_COIN_SWAP) || defined(CCAPI_ENABLE_EXCHANGE_BITMART)) - +#if CCAPI_REQUIRES_INFLATE_STREAM InflateStream inflater; - #endif std::array jsonParseBuffer;