11# Notice
2- * New release has about 20% reduction in cpu usage.
2+ * Small breaking change: renamed ` toStringPretty ` to ` toPrettyString ` .
3+ * Added FIX API support for binance.
34
45<!-- START doctoc generated TOC please keep comment here to allow auto update -->
56
4849 * [ Thread safety] ( #thread-safety )
4950 * [ Enable library logging] ( #enable-library-logging )
5051 * [ Set timer] ( #set-timer )
52+ * [ Heartbeat] ( #heartbeat )
5153 * [ Performance Tuning] ( #performance-tuning )
5254 * [ Known Issues and Workarounds] ( #known-issues-and-workarounds )
5355
5658<!-- END doctoc generated TOC please keep comment here to allow auto update -->
5759
5860
59-
60-
61-
62-
63-
6461# ccapi
6562* A header-only C++ library for streaming market data and executing trades directly from cryptocurrency exchanges (i.e. the connections are between your server and the exchange server without anything in-between).
6663* Bindings for other languages such as Python, Java, C#, Go, and Javascript are provided.
6966* Supported exchanges:
7067 * Market Data: ascendex, [ binance] ( https://www.marketwebb.net/activity/referral-entry/CPA?ref=CPA_00WFM0HU96 ) , [ binance-usds-futures] ( https://www.marketwebb.net/activity/referral-entry/CPA?ref=CPA_00WFM0HU96 ) , [ binance-coin-futures] ( https://www.marketwebb.net/activity/referral-entry/CPA?ref=CPA_00WFM0HU96 ) , bitfinex, bitget, bitget-futures, bitmart, bitmex, bitstamp, [ bybit] ( https://www.bybit.com/invite?ref=XNYP2K ) , coinbase, [ cryptocom] ( https://crypto.com/exch/tqj4b8x48w ) , deribit, erisx (Cboe Digital), [ gateio] ( https://www.gate.com/signup/VLUQXVFWAW?ref_type=103 ) , [ gateio-perpetual-futures] ( https://www.gate.com/signup/VLUQXVFWAW?ref_type=103 ) , gemini, huobi, huobi-usdt-swap, huobi-coin-swap, kraken, kraken-futures, kucoin, kucoin-futures, mexc, mexc-futures, [ okx] ( https://www.okx.com/join/47636709 ) , whitebit.
7168 * Execution Management: ascendex, [ binance] ( https://www.marketwebb.net/activity/referral-entry/CPA?ref=CPA_00WFM0HU96 ) , [ binance-usds-futures] ( https://www.marketwebb.net/activity/referral-entry/CPA?ref=CPA_00WFM0HU96 ) , [ binance-coin-futures] ( https://www.marketwebb.net/activity/referral-entry/CPA?ref=CPA_00WFM0HU96 ) , bitfinex, bitget, bitget-futures, bitmart, bitmex, bitstamp, [ bybit] ( https://www.bybit.com/invite?ref=XNYP2K ) , coinbase, [ cryptocom] ( https://crypto.com/exch/tqj4b8x48w ) , deribit, erisx (Cboe Digital), [ gateio] ( https://www.gate.com/signup/VLUQXVFWAW?ref_type=103 ) , [ gateio-perpetual-futures] ( https://www.gate.com/signup/VLUQXVFWAW?ref_type=103 ) , gemini, huobi, huobi-usdt-swap, huobi-coin-swap, kraken, kraken-futures, kucoin, kucoin-futures, mexc, [ okx] ( https://www.okx.com/join/47636709 ) .
72- * FIX: coinbase, gemini.
69+ * FIX: [ binance ] ( https://www.marketwebb.net/activity/referral-entry/CPA?ref=CPA_00WFM0HU96 ) , coinbase, gemini.
7370* Join us on Discord https://discord.gg/b5EKcp9s8T and Medium https://cryptochassis.medium.com .
7471
7572## Branches
@@ -232,7 +229,7 @@ Logger* Logger::logger = nullptr; // This line is needed.
232229class MyEventHandler : public EventHandler {
233230 public:
234231 void processEvent(const Event& event, Session* sessionPtr) override {
235- std::cout << "Received an event:\n" + event.toStringPretty (2, 2) << std::endl;
232+ std::cout << "Received an event:\n" + event.toPrettyString (2, 2) << std::endl;
236233 }
237234};
238235
@@ -311,7 +308,7 @@ class MyEventHandler : public EventHandler {
311308 public:
312309 void processEvent(const Event& event, Session* sessionPtr) override {
313310 if (event.getType() == Event::Type::SUBSCRIPTION_STATUS) {
314- std::cout << "Received an event of type SUBSCRIPTION_STATUS:\n" + event.toStringPretty (2, 2) << std::endl;
311+ std::cout << "Received an event of type SUBSCRIPTION_STATUS:\n" + event.toPrettyString (2, 2) << std::endl;
315312 } else if (event.getType() == Event::Type::SUBSCRIPTION_DATA) {
316313 for (const auto& message : event.getMessageList()) {
317314 std::cout << std::string("Best bid and ask at ") + UtilTime::getISOTimestamp(message.getTime()) + " are:" << std::endl;
@@ -505,7 +502,7 @@ Logger* Logger::logger = nullptr; // This line is needed.
505502class MyEventHandler : public EventHandler {
506503 public:
507504 void processEvent(const Event& event, Session* sessionPtr) override {
508- std::cout << "Received an event:\n" + event.toStringPretty (2, 2) << std::endl;
505+ std::cout << "Received an event:\n" + event.toPrettyString (2, 2) << std::endl;
509506 }
510507};
511508
@@ -605,7 +602,7 @@ class MyEventHandler : public EventHandler {
605602 public:
606603 void processEvent(const Event& event, Session* sessionPtr) override {
607604 if (event.getType() == Event::Type::SUBSCRIPTION_STATUS) {
608- std::cout << "Received an event of type SUBSCRIPTION_STATUS:\n" + event.toStringPretty (2, 2) << std::endl;
605+ std::cout << "Received an event of type SUBSCRIPTION_STATUS:\n" + event.toPrettyString (2, 2) << std::endl;
609606 auto message = event.getMessageList().at(0);
610607 if (message.getType() == Message::Type::SUBSCRIPTION_STARTED) {
611608 Request request(Request::Operation::CREATE_ORDER, "okx", "BTC-USDT");
@@ -618,7 +615,7 @@ class MyEventHandler : public EventHandler {
618615 sessionPtr->sendRequest(request);
619616 }
620617 } else if (event.getType() == Event::Type::SUBSCRIPTION_DATA) {
621- std::cout << "Received an event of type SUBSCRIPTION_DATA:\n" + event.toStringPretty (2, 2) << std::endl;
618+ std::cout << "Received an event of type SUBSCRIPTION_DATA:\n" + event.toPrettyString (2, 2) << std::endl;
622619 }
623620 }
624621};
@@ -843,61 +840,66 @@ For a specific exchange and instrument, submit a simple limit order.
843840[ C++] ( example/src/fix_simple/main.cpp ) / [ Python] ( binding/python/example/fix_simple/main.py ) / [ Java] ( binding/java/example/fix_simple/Main.java ) / [ C#] ( binding/csharp/example/fix_simple/MainProgram.cs ) / [ Go] ( binding/go/example/fix_simple/main.go ) / [ Javascript] ( binding/javascript/example/fix_simple/index.js )
844841```
845842#include "ccapi_cpp/ccapi_session.h"
843+
846844namespace ccapi {
847845
848846Logger* Logger::logger = nullptr; // This line is needed.
847+
849848class MyEventHandler : public EventHandler {
850849 public:
850+ MyEventHandler(const std::string& fixSubscriptionCorrelationId) : fixSubscriptionCorrelationId(fixSubscriptionCorrelationId) {}
851+
851852 void processEvent(const Event& event, Session* sessionPtr) override {
852- if (event.getType() == Event::Type::AUTHORIZATION_STATUS) {
853- std::cout << "Received an event of type AUTHORIZATION_STATUS:\n" + event.toStringPretty(2, 2) << std::endl;
854- auto message = event.getMessageList().at(0);
855- if (message.getType() == Message::Type::AUTHORIZATION_SUCCESS) {
856- Request request(Request::Operation::FIX, "okx", "", "same correlation id for subscription and request");
857- request.appendParamFix({
853+ std::cout << "Received an event:\n" + event.toPrettyString(2, 2) << std::endl;
854+ if (!willSendRequest) {
855+ sessionPtr->setTimer("id", 1000, nullptr, [this, sessionPtr]() {
856+ Request request(Request::Operation::FIX, "binance");
857+ request.appendFixParam({
858858 {35, "D"},
859- {11, "6d4eb0fb-2229-469f-873e-557dd78ac11e" },
860- {55, "BTC-USDT "},
859+ {11, request.generateNextClientOrderId() },
860+ {55, "BTCUSDT "},
861861 {54, "1"},
862- {44, "20000 "},
863- {38, "0.001 "},
862+ {44, "100000 "},
863+ {38, "0.0001 "},
864864 {40, "2"},
865865 {59, "1"},
866866 });
867- sessionPtr->sendRequestByFix(request);
868- }
869- } else if (event.getType() == Event::Type::FIX) {
870- std::cout << "Received an event of type FIX:\n" + event.toStringPretty(2, 2) << std::endl;
867+ sessionPtr->sendRequestByFix(this->fixSubscriptionCorrelationId, request);
868+ });
869+ willSendRequest = true;
871870 }
872871 }
872+
873+ private:
874+ std::string fixSubscriptionCorrelationId;
875+ bool willSendRequest{};
873876};
874877
875878} /* namespace ccapi */
879+
876880using ::ccapi::MyEventHandler;
877881using ::ccapi::Session;
878882using ::ccapi::SessionConfigs;
879883using ::ccapi::SessionOptions;
880884using ::ccapi::Subscription;
881885using ::ccapi::UtilSystem;
886+
882887int main(int argc, char** argv) {
883- if (UtilSystem::getEnvAsString("OKX_API_KEY ").empty()) {
884- std::cerr << "Please set environment variable OKX_API_KEY " << std::endl;
888+ if (UtilSystem::getEnvAsString("BINANCE_FIX_API_KEY ").empty()) {
889+ std::cerr << "Please set environment variable BINANCE_FIX_API_KEY " << std::endl;
885890 return EXIT_FAILURE;
886891 }
887- if (UtilSystem::getEnvAsString("OKX_API_SECRET").empty()) {
888- std::cerr << "Please set environment variable OKX_API_SECRET" << std::endl;
889- return EXIT_FAILURE;
890- }
891- if (UtilSystem::getEnvAsString("OKX_API_PASSPHRASE").empty()) {
892- std::cerr << "Please set environment variable OKX_API_PASSPHRASE" << std::endl;
892+ if (UtilSystem::getEnvAsString("BINANCE_FIX_API_PRIVATE_KEY_PATH").empty()) {
893+ std::cerr << "Please set environment variable BINANCE_FIX_API_PRIVATE_KEY_PATH" << std::endl;
893894 return EXIT_FAILURE;
894895 }
895896 SessionOptions sessionOptions;
896897 SessionConfigs sessionConfigs;
897- MyEventHandler eventHandler;
898+ std::string fixSubscriptionCorrelationId("any");
899+ MyEventHandler eventHandler(fixSubscriptionCorrelationId);
898900 Session session(sessionOptions, sessionConfigs, &eventHandler);
899- Subscription subscription("okx ", "", "FIX", "", "same correlation id for subscription and request" );
900- session.subscribeByFix (subscription);
901+ Subscription subscription("binance ", "", "FIX", "", fixSubscriptionCorrelationId );
902+ session.subscribe (subscription);
901903 std::this_thread::sleep_for(std::chrono::seconds(10));
902904 session.stop();
903905 std::cout << "Bye" << std::endl;
@@ -906,56 +908,97 @@ int main(int argc, char** argv) {
906908```
907909** Output:**
908910``` console
909- Received an event of type AUTHORIZATION_STATUS:
911+ Received an event:
912+ Event [
913+ type = SESSION_STATUS,
914+ messageList = [
915+ Message [
916+ type = SESSION_CONNECTION_UP,
917+ recapType = UNKNOWN,
918+ time = 1970-01-01T00:00:00.000000000Z,
919+ timeReceived = 2025-08-08T18:50:06.816550779Z,
920+ elementList = [
921+ Element [
922+ tagValueList = [
923+
924+ ],
925+ nameValueMap = {
926+ CONNECTION_ID = IF8j4HbdLP0,
927+ CONNECTION_URL = tcp+tls://fix-oe.binance.com:9000
928+ }
929+ ]
930+ ],
931+ correlationIdList = [ any ],
932+ ]
933+ ]
934+ ]
935+ Received an event:
910936 Event [
911937 type = AUTHORIZATION_STATUS,
912938 messageList = [
913939 Message [
914940 type = AUTHORIZATION_SUCCESS,
915941 recapType = UNKNOWN,
916942 time = 1970-01-01T00:00:00.000000000Z,
917- timeReceived = 2021-05-25T05:05:15.892366000Z ,
943+ timeReceived = 2025-08-08T18:50:06.819417404Z ,
918944 elementList = [
919945 Element [
920- tagValueMap = {
921- 96 = 0srtt0WetUTYHiTpvyWnC+XKKHCzQQIJ/8G9lE4KVxM=,
922- 98 = 0,
923- 108 = 15,
924- 554 = 26abh7of52i
946+ tagValueList = [
947+ (35, "A"),
948+ (98, "0"),
949+ (108, "60"),
950+ (25037, "e9ed8253-8f49-4a1b-bba9-718ff73991e5")
951+ ],
952+ nameValueMap = {
953+
925954 }
926955 ]
927956 ],
928- correlationIdList = [ same correlation id for subscription and request ]
957+ correlationIdList = [ any ],
929958 ]
930959 ]
931960 ]
932- Received an event of type FIX :
961+ Received an event:
933962 Event [
934963 type = FIX,
935964 messageList = [
936965 Message [
937966 type = FIX,
938967 recapType = UNKNOWN,
939968 time = 1970-01-01T00:00:00.000000000Z,
940- timeReceived = 2021-05-25T05:05:15.984090000Z ,
969+ timeReceived = 2025-08-08T18:50:07.820112736Z ,
941970 elementList = [
942971 Element [
943- tagValueMap = {
944- 11 = 6d4eb0fb-2229-469f-873e-557dd78ac11e,
945- 17 = b7caec79-1bc8-460e-af28-6489cf12f45e,
946- 20 = 0,
947- 37 = 458acfe5-bdea-46d2-aa87-933cda84163f,
948- 38 = 0.001,
949- 39 = 0,
950- 44 = 20000,
951- 54 = 1,
952- 55 = BTC-USDT,
953- 60 = 20210525-05:05:16.008,
954- 150 = 0
972+ tagValueList = [
973+ (35, "8"),
974+ (17, "100146443390"),
975+ (11, "x-XHKUG2CH-1754679007000"),
976+ (37, "47156106695"),
977+ (38, "0.00010000"),
978+ (40, "2"),
979+ (54, "1"),
980+ (55, "BTCUSDT"),
981+ (44, "100000.00000000"),
982+ (59, "1"),
983+ (60, "20250808-18:50:07.819027"),
984+ (25018, "20250808-18:50:07.819027"),
985+ (25001, "3"),
986+ (150, "0"),
987+ (14, "0.00000000"),
988+ (151, "0.00010000"),
989+ (25017, "0.00000000"),
990+ (1057, "Y"),
991+ (32, "0.00000000"),
992+ (39, "0"),
993+ (636, "Y"),
994+ (25023, "20250808-18:50:07.819027")
995+ ],
996+ nameValueMap = {
997+
955998 }
956999 ]
9571000 ],
958- correlationIdList = [ same correlation id for subscription and request ]
1001+ correlationIdList = [ any ],
9591002 ]
9601003 ]
9611004 ]
@@ -976,7 +1019,7 @@ std::vector<Event> eventList = session.getEventQueue().purge();
9761019An example can be found [ here] ( example/src/market_data_advanced_subscription/main.cpp ) .
9771020
9781021#### Thread safety
979- * The following methods are implemented to be thread-safe: ` Session::sendRequest ` , ` Session::subscribe ` , ` Session::sendRequestByFix ` , ` Session::subscribeByFix ` , ` Session:: setTimer` , all public methods in ` Queue ` .
1022+ * The following methods are implemented to be thread-safe: ` Session::sendRequest ` , ` Session::subscribe ` , ` Session::sendRequestByFix ` , ` Session::setTimer ` , all public methods in ` Queue ` .
9801023* If you choose to inject an external ` boost::asio::io_context ` to ` ServiceContext ` , the ` boost::asio::io_context ` has to run on a single thread to ensure thread safety.
9811024
9821025#### Enable library logging
@@ -1007,7 +1050,7 @@ Logger* Logger::logger = &myLogger;
10071050
10081051#### Set timer
10091052
1010- [ C++] ( example/src/utility_set_timer /main.cpp )
1053+ [ C++] ( example/src/set_timer /main.cpp )
10111054
10121055To perform an asynchronous wait, use the utility method ` setTimer ` in class ` Session ` . The handlers are invoked in the same threads as the ` processEvent ` method in the ` EventHandler ` class. The ` id ` of the timer should be unique. ` delayMilliseconds ` can be 0.
10131056```
@@ -1019,6 +1062,16 @@ sessionPtr->setTimer(
10191062 []() { std::cout << std::string("Timer success handler is triggered at ") + UtilTime::getISOTimestamp(UtilTime::now()) << std::endl; });
10201063```
10211064
1065+ #### Heartbeat
1066+
1067+ [ C++] ( example/src/heartbeat/main.cpp )
1068+
1069+ To receive heartbeat events, instantiate a ` Subscription ` object with field ` HEARTBEAT ` and subscribe it.
1070+ ```
1071+ Subscription subscription("", "", "HEARTBEAT", "HEARTBEAT_INTERVAL_MILLISECONDS=1000");
1072+ session.subscribe(subscription);
1073+ ```
1074+
10221075## Performance Tuning
10231076* Turn on compiler optimization flags (e.g. ` cmake -DCMAKE_BUILD_TYPE=Release ... ` ).
10241077* Enable link time optimization (e.g. in CMakeLists.txt ` set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) ` before a target is created). Note that link time optimization is only applicable to static linking.
0 commit comments