[!IMPORTANT] ⚠️ Action Required for OKX Users — Update Before October 31, 2026 If you use OKX, please update to our latest stable release before October 31, 2026 to ensure compatibility with the upcoming discontinuation of OKX WebSocket Port 8443. For more details, see the [official OKX announcement](https://www.okx.com/help/okx-websocket-port-8443-discontinuation-announcement). **Table of Contents** *generated with [DocToc](https://github.com/ktechhub/doctoc)* - [ccapi](#ccapi) - [Branches](#branches) - [Build](#build) - [C++](#c) - [non-C++](#non-c) - [Constants](#constants) - [Examples](#examples) - [Documentations](#documentations) - [Simple Market Data](#simple-market-data) - [Advanced Market Data](#advanced-market-data) - [Complex request parameters](#complex-request-parameters) - [Specify subscription market depth](#specify-subscription-market-depth) - [Specify correlation id](#specify-correlation-id) - [Multiple exchanges and/or instruments](#multiple-exchanges-andor-instruments) - [Receive subscription events at periodic intervals](#receive-subscription-events-at-periodic-intervals) - [Receive subscription events at periodic intervals including when the market depth snapshot hasn't changed](#receive-subscription-events-at-periodic-intervals-including-when-the-market-depth-snapshot-hasnt-changed) - [Receive subscription market depth updates](#receive-subscription-market-depth-updates) - [Receive subscription trade events](#receive-subscription-trade-events) - [Receive subscription calculated-candlestick events at periodic intervals](#receive-subscription-calculated-candlestick-events-at-periodic-intervals) - [Receive subscription exchange-provided-candlestick events at periodic intervals](#receive-subscription-exchange-provided-candlestick-events-at-periodic-intervals) - [Send generic public requests](#send-generic-public-requests) - [Make generic public subscriptions](#make-generic-public-subscriptions) - [Send generic private requests](#send-generic-private-requests) - [Simple Execution Management](#simple-execution-management) - [Advanced Execution Management](#advanced-execution-management) - [Specify correlation id](#specify-correlation-id-1) - [Multiple exchanges and/or instruments](#multiple-exchanges-andor-instruments-1) - [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) - [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) - [Thread safety](#thread-safety) - [Enable library logging](#enable-library-logging) - [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) - [Reduce build time](#reduce-build-time) - [Exchange Specific Notes](#exchange-specific-notes) - [Hyperliquid](#hyperliquid) - [Performance Tuning](#performance-tuning) - [Known Issues and Workarounds](#known-issues-and-workarounds) # ccapi * 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). * Bindings for other languages such as Python, Java, C#, Go, and Javascript are provided. * Code closely follows Bloomberg's API: https://www.bloomberg.com/professional/support/api-library/. * It is ultra fast thanks to very careful optimizations. * Supported exchanges: * Market Data: ascendex, [binance](https://accounts.maxweb.academy/register?ref=1116718520), [binance-usds-futures](https://accounts.maxweb.academy/register?ref=1116718520), [binance-coin-futures](https://accounts.maxweb.academy/register?ref=1116718520), bitfinex, [bitget](https://bonus.bitget.com/0V0WA1), [bitget-futures](https://bonus.bitget.com/0V0WA1), [bitget-uta](https://bonus.bitget.com/0V0WA1), 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](https://www.htx.com/invite/en-us/1f?invite_code=rmw7d223), [huobi-usdt-swap](https://www.htx.com/invite/en-us/1f?invite_code=rmw7d223), [huobi-coin-swap](https://www.htx.com/invite/en-us/1f?invite_code=rmw7d223), [hyperliquid](https://app.hyperliquid.xyz/), kraken, kraken-futures, kucoin, kucoin-futures, mexc, mexc-futures, [okx](https://www.okx.com/join/47636709), whitebit. * Execution Management: ascendex, [binance](https://accounts.maxweb.academy/register?ref=1116718520), [binance-usds-futures](https://accounts.maxweb.academy/register?ref=1116718520), [binance-coin-futures](https://accounts.maxweb.academy/register?ref=1116718520), bitfinex, [bitget](https://bonus.bitget.com/0V0WA1), [bitget-futures](https://bonus.bitget.com/0V0WA1), [bitget-uta](https://bonus.bitget.com/0V0WA1), 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](https://www.htx.com/invite/en-us/1f?invite_code=rmw7d223), [huobi-usdt-swap](https://www.htx.com/invite/en-us/1f?invite_code=rmw7d223), [huobi-coin-swap](https://www.htx.com/invite/en-us/1f?invite_code=rmw7d223), [hyperliquid](https://app.hyperliquid.xyz/), kraken, kraken-futures, kucoin, kucoin-futures, mexc, [okx](https://www.okx.com/join/47636709). * 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. * The `master` branch represents the most recent stable release. ## Build ### C++ * This library is header-only. * Example CMake: example/CMakeLists.txt. * Require C++17 and OpenSSL. * Macros in the compiler command line: * Define service enablement macro such as `CCAPI_ENABLE_SERVICE_MARKET_DATA`, `CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT`, `CCAPI_ENABLE_SERVICE_FIX`, etc. and exchange enablement macros such as `CCAPI_ENABLE_EXCHANGE_BYBIT`, etc. These macros can be found at the top of [`include/ccapi_cpp/ccapi_session.h`](include/ccapi_cpp/ccapi_session.h). * Dependencies: * boost https://archives.boost.io/release/1.87.0/source/boost_1_87_0.tar.gz (notice that its include directory is boost). * rapidjson https://github.com/Tencent/rapidjson/archive/refs/tags/v1.1.0.tar.gz (notice that its include directory is rapidjson/include). * If you use FIX API, also need hffix https://github.com/jamesdbrock/hffix/archive/refs/tags/v1.4.1.tar.gz (notice that its include directory is hffix/include). * Include directory for this library: * include. * Link libraries: * OpenSSL: libssl. * OpenSSL: libcrypto. * If you need market data for huobi/huobi-usdt-swap/huobi-coin-swap or execution management for huobi-usdt-swap/huobi-coin-swap/bitmart, also link ZLIB. * On Windows, also link ws2_32. * Compiler flags: * `-pthread` for GCC and MinGW. * Tested platforms: * macOS: Clang. * Linux: GCC. * Windows: MinGW. * Troubleshoot: * Try to remove all build artifacts and start from scratch (e.g. for cmake remove all the contents inside your build directory). * "Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR (missing: OPENSSL_INCLUDE_DIR)". Try `cmake -DOPENSSL_ROOT_DIR=...`. On macOS, you might be missing headers for OpenSSL, `brew install openssl` and `cmake -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl`. On Ubuntu, `sudo apt-get install libssl-dev`. On Windows, `vcpkg install openssl:x64-windows` and `cmake -DOPENSSL_ROOT_DIR=C:/vcpkg/installed/x64-windows-static`. * "Fatal error: can't write \ bytes to section .text of \: 'File too big'". Try to add compiler flag `-Wa,-mbig-obj`. See https://github.com/assimp/assimp/issues/2067. * "string table overflow at offset \". Try to add optimization flag `-O1` or `-O2`. See https://stackoverflow.com/questions/14125007/gcc-string-table-overflow-error-during-compilation. * On Windows, if you still encounter resource related issues, try to add optimization flag `-O3 -DNDEBUG`. ### non-C++ * Require SWIG and CMake. * SWIG: On macOS, `brew install SWIG`. On Linux, `sudo apt-get install -y swig`. * CMake: https://cmake.org/download/. * Run the following commands. ``` mkdir binding/build cd binding/build rm -rf * (if rebuild from scratch) cmake -DBUILD_PYTHON=ON -DBUILD_VERSION=1.0.0 .. (Use -DBUILD_JAVA=ON if the target language is Java, -DBUILD_CSHARP=ON if the target language is C#, -DBUILD_GO=ON if the target language is Go, -DBUILD_JAVASCRIPT=ON if the target language is Javascript) cmake --build . ``` * The packaged build artifacts are located in the `binding/build//packaging/` directory. SWIG generated raw files and build artifacts are located in the `binding/build//ccapi_binding_` directory. * Python: If a virtual environment (managed by `venv` or `conda`) is active (i.e. the `activate` script has been evaluated), the package will be installed into the virtual environment rather than globally. * C#: The shared library is built using the .NET framework. * Troubleshoot: * "Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR (missing: OPENSSL_INCLUDE_DIR)". Try `cmake -DOPENSSL_ROOT_DIR=...`. On macOS, you might be missing headers for OpenSSL, `brew install openssl` and `cmake -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl`. On Ubuntu, `sudo apt-get install libssl-dev`. On Windows, `vcpkg install openssl:x64-windows` and `cmake -DOPENSSL_ROOT_DIR=C:/vcpkg/installed/x64-windows-static`. * Python: * "CMake Error at python/CMakeLists.txt:... (message): Require Python 3". Try to create and activate a virtual environment (managed by `venv` or `conda`) with Python 3. * "‘_PyObject_GC_UNTRACK’ was not declared in this scope". If you use Python >= 3.8, please use SWIG >= 4.0. * Java: * "Could NOT find JNI (missing: JAVA_INCLUDE_PATH JAVA_INCLUDE_PATH2 JAVA_AWT_INCLUDE_PATH)". Check that the environment variable `JAVA_HOME` is correct. * Javascript: * "Check for node-gyp Program: not found". You can install node-gyp using npm: `npm install -g node-gyp` ## Constants [`include/ccapi_cpp/ccapi_macro.h`](include/ccapi_cpp/ccapi_macro.h) ## Examples [C++](example) * Require CMake. * CMake: https://cmake.org/download/. * Run the following commands. ``` mkdir example/build cd example/build rm -rf * (if rebuild from scratch) cmake .. cmake --build . --target ``` * The executable is `example/build/src//`. Run it. [Python](binding/python/example) * Python API is nearly identical to C++ API and covers nearly all the functionalities from C++ API. * Build and install the Python binding as shown [above](#non-c). * Inside a concrete example directory (e.g. binding/python/example/market_data_simple_subscription), run ``` python3 main.py ``` * Troubleshoot: * "Fatal Python error: Segmentation fault". If the macOS version is relatively new and the Python version is relatively old, please upgrade Python to a relatively new version. [Java](binding/java/example) * Java API is nearly identical to C++ API and covers nearly all the functionalities from C++ API. * Build and install the Java binding as shown [above](#non-c). * Inside a concrete example directory (e.g. binding/python/example/market_data_simple_subscription), run ``` mkdir build cd build rm -rf * (if rebuild from scratch) javac -cp ../../../../build/java/packaging/1.0.0/ccapi-1.0.0.jar -d . ../Main.java java -cp .:../../../../build/java/packaging/1.0.0/ccapi-1.0.0.jar -Djava.library.path=../../../../build/java/packaging/1.0.0 Main ``` * Troubleshoot: * "../Main.java:1: error: package com.cryptochassis.ccapi does not exist". Check that `javac`'s classpath includes `binding/build/java/packaging/1.0.0/ccapi-1.0.0.jar`. * "Exception in thread "main" java.lang.UnsatisfiedLinkError: no ccapi_binding_java in java.library.path: ...". Check that `java`'s `java.library.path` property includes `binding/build/java/packaging/1.0.0`. See https://stackoverflow.com/questions/1403788/java-lang-unsatisfiedlinkerror-no-dll-in-java-library-path. [C#](binding/csharp/example) * C# API is nearly identical to C++ API and covers nearly all the functionalities from C++ API. * Build and install the C# binding as shown [above](#non-c). * Inside a concrete example directory (e.g. binding/csharp/example/market_data_simple_subscription), run ``` dotnet clean (if rebuild from scratch) env LD_LIBRARY_PATH="$LD_LIBRARY_PATH:../../../build/csharp/packaging/1.0.0" dotnet run --property:CcapiLibraryPath=../../../build/csharp/packaging/1.0.0/ccapi.dll -c Release ``` * Troubleshoot: * "error CS0246: The type or namespace name 'ccapi' could not be found". Check that you aren't missing the ccapi assembly reference. * "System.DllNotFoundException: Unable to load shared library 'ccapi_binding_csharp.so' or one of its dependencies.". Check that environment variable `LD_LIBRARY_PATH` includes `binding/build/csharp/packaging/1.0.0`. [Go](binding/go/example) * Go API is nearly identical to C++ API and covers nearly all the functionalities from C++ API. * Build and install the Go binding as shown [above](#non-c). * Inside a concrete example directory (e.g. binding/go/example/market_data_simple_subscription), run ``` go clean (if rebuild from scratch) source ../../../build/go/packaging/1.0.0/export_compiler_options.sh (this step is important) go build . ./main ``` * Troubleshoot: * Some C/C++ header files not found. Check that you sourced the export_compiler_options.sh file which provides important environment variables needed by the cgo tool. [Javascript](binding/javascript/example) * Javascript API is nearly identical to C++ API and covers nearly all the functionalities from C++ API. * Build and install the Javascript binding as shown [above](#non-c). * Inside a concrete example directory (e.g. binding/javascript/example/market_data_simple_subscription), run ``` rm -rf node_modules (if rebuild from scratch) npm install node index.js ``` ## Documentations ### Simple Market Data **Objective 1:** For a specific exchange and instrument, get recents trades. **Code 1:** [C++](example/market_data_simple_request/main.cpp) / [Python](binding/python/example/market_data_simple_request/main.py) / [Java](binding/java/example/market_data_simple_request/Main.java) / [C#](binding/csharp/example/market_data_simple_request/MainProgram.cs) / [Go](binding/go/example/market_data_simple_request/main.go) / [Javascript](binding/javascript/example/market_data_simple_request/index.js) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { Logger* Logger::logger = nullptr; // This line is needed. class MyEventHandler : public EventHandler { public: void processEvent(const Event& event, Session* sessionPtr) override { std::cout << "Received an event:\n" + event.toPrettyString(2, 2) << std::endl; } }; } /* namespace ccapi */ using ::ccapi::MyEventHandler; using ::ccapi::Request; using ::ccapi::Session; using ::ccapi::SessionConfigs; using ::ccapi::SessionOptions; int main(int argc, char** argv) { SessionOptions sessionOptions; SessionConfigs sessionConfigs; MyEventHandler eventHandler; Session session(sessionOptions, sessionConfigs, &eventHandler); Request request(Request::Operation::GET_RECENT_TRADES, "bybit", "BTCUSDT"); request.appendParam({ {"LIMIT", "1"}, }); session.sendRequest(request); std::this_thread::sleep_for(std::chrono::seconds(10)); session.stop(); std::cout << "Bye" << std::endl; return EXIT_SUCCESS; } ``` **Output 1:** ```console Received an event: Event [ type = RESPONSE, messageList = [ Message [ type = GET_RECENT_TRADES, recapType = UNKNOWN, time = 2021-05-25T03:23:31.124000000Z, timeReceived = 2021-05-25T03:23:31.239734000Z, elementList = [ Element [ nameValueMap = { IS_BUYER_MAKER = 1, LAST_PRICE = 38270.71, LAST_SIZE = 0.001, TRADE_ID = 178766798 } ] ], correlationIdList = [ 5PN2qmWqBlQ9wQj99nsQzldVI5ZuGXbE ] ] ] ] Bye ``` * Request operation types: `GET_SERVER_TIME`, `GET_INSTRUMENT`, `GET_INSTRUMENTS`, `GET_BBOS`, `GET_RECENT_TRADES`, `GET_HISTORICAL_TRADES`, `GET_RECENT_CANDLESTICKS`, `GET_HISTORICAL_CANDLESTICKS`, `GET_RECENT_AGG_TRADES`, `GET_HISTORICAL_AGG_TRADES`(only applicable to binance family: https://binance-docs.github.io/apidocs/spot/en/#compressed-aggregate-trades-list), ``. * Request parameter names: `LIMIT`, `INSTRUMENT_TYPE`, `CANDLESTICK_INTERVAL_SECONDS`, `START_TIME_SECONDS`, `END_TIME_SECONDS`, `START_TRADE_ID`, `END_TRADE_ID`, `START_AGG_TRADE_ID`, `END_AGG_TRADE_ID`. Instead of these convenient names you can also choose to use arbitrary parameter names and they will be passed to the exchange's native API. See [this example](example/market_data_advanced_request/main.cpp). * Message's `time` represents the exchange's reported timestamp. Its `timeReceived` represents the library's receiving timestamp. `time` can be retrieved by `getTime` method and `timeReceived` can be retrieved by `getTimeReceived` method. (For non-C++, please use `getTimeUnix` and `getTimeReceivedUnix` methods or `getTimeISO` and `getTimeReceivedISO` methods). **Objective 2:** For a specific exchange and instrument, whenever the best bid's or ask's price or size changes, print the market depth snapshot at that moment. **Code 2:** [C++](example/market_data_simple_subscription/main.cpp) / [Python](binding/python/example/market_data_simple_subscription/main.py) / [Java](binding/java/example/market_data_simple_subscription/Main.java) / [C#](binding/csharp/example/market_data_simple_subscription/MainProgram.cs) / [Go](binding/go/example/market_data_simple_subscription/main.go) / [Javascript](binding/javascript/example/market_data_simple_subscription/index.js) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { Logger* Logger::logger = nullptr; // This line is needed. class MyEventHandler : public EventHandler { public: void processEvent(const Event& event, Session* sessionPtr) override { if (event.getType() == Event::Type::SUBSCRIPTION_STATUS) { std::cout << "Received an event of type SUBSCRIPTION_STATUS:\n" + event.toPrettyString(2, 2) << std::endl; } else if (event.getType() == Event::Type::SUBSCRIPTION_DATA) { for (const auto& message : event.getMessageList()) { std::cout << std::string("Best bid and ask at ") + UtilTime::getISOTimestamp(message.getTime()) + " are:" << std::endl; for (const auto& element : message.getElementList()) { // They key std::string_view is created from a string literal and therefore is safe, because string // literals have static storage duration, meaning they live for the entire duration of the program. const std::map& elementNameValueMap = element.getNameValueMap(); std::cout << " " + toString(elementNameValueMap) << std::endl; } } } } }; } /* namespace ccapi */ using ::ccapi::MyEventHandler; using ::ccapi::Session; using ::ccapi::SessionConfigs; using ::ccapi::SessionOptions; using ::ccapi::Subscription; using ::ccapi::toString; int main(int argc, char** argv) { SessionOptions sessionOptions; SessionConfigs sessionConfigs; MyEventHandler eventHandler; Session session(sessionOptions, sessionConfigs, &eventHandler); Subscription subscription("bybit", "BTCUSDT", "MARKET_DEPTH"); session.subscribe(subscription); std::this_thread::sleep_for(std::chrono::seconds(10)); session.stop(); std::cout << "Bye" << std::endl; return EXIT_SUCCESS; } ``` **Output 2:** ```console Best bid and ask at 2020-07-27T23:56:51.884855000Z are: {BID_PRICE=10995, BID_SIZE=0.22187803} {ASK_PRICE=10995.44, ASK_SIZE=2} Best bid and ask at 2020-07-27T23:56:51.935993000Z are: ... ``` * Subscription fields: `MARKET_DEPTH`, `TRADE`, `CANDLESTICK`, `AGG_TRADE`(only applicable to binance family: https://binance-docs.github.io/apidocs/spot/en/#aggregate-trade-streams). ### Advanced Market Data #### Complex request parameters Please follow the exchange's API documentations: e.g. https://bybit-exchange.github.io/docs/v5/market/instrument. ``` Request request(Request::Operation::GET_INSTRUMENTS, "bybit"); request.appendParam({ {"category", "linear"}, {"limit", "1000"}, }); ``` #### Specify subscription market depth Instantiate `Subscription` with option `MARKET_DEPTH_MAX` set to be the desired market depth (e.g. you want to receive market depth snapshot whenever the top 10 bid's or ask's price or size changes). ``` Subscription subscription("bybit", "BTCUSDT", "MARKET_DEPTH", "MARKET_DEPTH_MAX=10"); ``` #### Specify correlation id Instantiate `Request` with the desired correlationId. The `correlationId` should be unique. ``` Request request(Request::Operation::GET_RECENT_TRADES, "bybit", "BTCUSDT", "cool correlation id"); ``` Instantiate `Subscription` with the desired correlationId. ``` Subscription subscription("bybit", "BTCUSDT", "MARKET_DEPTH", "", "cool correlation id"); ``` This is used to match a particular request or subscription with its returned data. Within each `Message` there is a `correlationIdList` to identify the request or subscription that requested the data. #### Multiple exchanges and/or instruments Send a `std::vector`. ``` Request request_1(Request::Operation::GET_RECENT_TRADES, "bybit", "BTCUSDT", "cool correlation id for BTC"); request_1.appendParam(...); Request request_2(Request::Operation::GET_RECENT_TRADES, "binance", "ETH-USDT", "cool correlation id for ETH"); request_2.appendParam(...); std::vector requests = {request_1, request_2}; session.sendRequest(requests); ``` Subscribe a `std::vector`. ``` Subscription subscription_1("bybit", "BTCUSDT", "MARKET_DEPTH", "", "cool correlation id for bybit BTCUSDT"); Subscription subscription_2("binance", "ETHUSDT", "MARKET_DEPTH", "", "cool correlation id for binance ETHUSDT"); std::vector subscriptions = {subscription_1, subscription_2}; session.subscribe(subscriptions); ``` #### Receive subscription events at periodic intervals Instantiate `Subscription` with option `CONFLATE_INTERVAL_MILLISECONDS` set to be the desired interval. ``` Subscription subscription("bybit", "BTCUSDT", "MARKET_DEPTH", "CONFLATE_INTERVAL_MILLISECONDS=1000"); ``` #### Receive subscription events at periodic intervals including when the market depth snapshot hasn't changed Instantiate `Subscription` with option `CONFLATE_INTERVAL_MILLISECONDS` set to be the desired interval and `CONFLATE_GRACE_PERIOD_MILLISECONDS` to be the grace period for late events. ``` Subscription subscription("bybit", "BTCUSDT", "MARKET_DEPTH", "CONFLATE_INTERVAL_MILLISECONDS=1000&CONFLATE_GRACE_PERIOD_MILLISECONDS=0"); ``` #### Receive subscription market depth updates Instantiate `Subscription` with option `MARKET_DEPTH_RETURN_UPDATE` set to 1. This will return the order book updates instead of snapshots. ``` Subscription subscription("bybit", "BTCUSDT", "MARKET_DEPTH", "MARKET_DEPTH_RETURN_UPDATE=1&MARKET_DEPTH_MAX=2"); ``` #### Receive subscription trade events Instantiate `Subscription` with field `TRADE`. ``` Subscription subscription("bybit", "BTCUSDT", "TRADE"); ``` #### Receive subscription calculated-candlestick events at periodic intervals Instantiate `Subscription` with field `TRADE` and option `CONFLATE_INTERVAL_MILLISECONDS` set to be the desired interval and `CONFLATE_GRACE_PERIOD_MILLISECONDS` to be your network latency. ``` Subscription subscription("bybit", "BTCUSDT", "TRADE", "CONFLATE_INTERVAL_MILLISECONDS=5000&CONFLATE_GRACE_PERIOD_MILLISECONDS=0"); ``` #### Receive subscription exchange-provided-candlestick events at periodic intervals Instantiate `Subscription` with field `CANDLESTICK` and option `CANDLESTICK_INTERVAL_SECONDS` set to be the desired interval. ``` Subscription subscription("bybit", "BTCUSDT", "CANDLESTICK", "CANDLESTICK_INTERVAL_SECONDS=60"); ``` #### Send generic public requests Instantiate `Request` with operation `GENERIC_PUBLIC_REQUEST`. Provide request parameters `HTTP_METHOD`, `HTTP_PATH`, and optionally `HTTP_QUERY_STRING` (query string parameter values should be url-encoded), `HTTP_BODY`. ``` Request request(Request::Operation::GENERIC_PUBLIC_REQUEST, "bybit", "", "Check Server Time"); request.appendParam({ {"HTTP_METHOD", "GET"}, {"HTTP_PATH", "/api/v5/public/time"}, }); ``` #### Make generic public subscriptions Instantiate `Subscription` with empty instrument, field `GENERIC_PUBLIC_SUBSCRIPTION` and options set to be the desired websocket payload. ``` Subscription subscription("bybit", "", "GENERIC_PUBLIC_SUBSCRIPTION", R"({"type":"subscribe","channels":[{"name":"status"}]})"); ``` #### Send generic private requests Instantiate `Request` with operation `GENERIC_PRIVATE_REQUEST`. Provide request parameters `HTTP_METHOD`, `HTTP_PATH`, and optionally `HTTP_QUERY_STRING` (query string parameter values should be url-encoded), `HTTP_BODY`. ``` Request request(Request::Operation::GENERIC_PRIVATE_REQUEST, "bybit", "", "close all positions"); request.appendParam({ {"HTTP_METHOD", "POST"}, {"HTTP_PATH", "/api/v5/trade/close-position"}, {"HTTP_BODY", R"({ "instId": "BTC-USDT-SWAP", "mgnMode": "cross" })"}, }); ``` ### Simple Execution Management **Objective 1:** For a specific exchange and instrument, submit a simple limit order. **Code 1:** [C++](example/execution_management_simple_request/main.cpp) / [Python](binding/python/example/execution_management_simple_request/main.py) / [Java](binding/java/example/execution_management_simple_request/Main.java) / [C#](binding/csharp/example/execution_management_simple_request/MainProgram.cs) / [Go](binding/go/example/execution_management_simple_request/main.go) / [Javascript](binding/javascript/example/execution_management_simple_request/index.js) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { Logger* Logger::logger = nullptr; // This line is needed. class MyEventHandler : public EventHandler { public: void processEvent(const Event& event, Session* sessionPtr) override { std::cout << "Received an event:\n" + event.toPrettyString(2, 2) << std::endl; } }; } /* namespace ccapi */ using ::ccapi::MyEventHandler; using ::ccapi::Request; using ::ccapi::Session; using ::ccapi::SessionConfigs; using ::ccapi::SessionOptions; using ::ccapi::toString; using ::ccapi::UtilSystem; int main(int argc, char** argv) { if (UtilSystem::getEnvAsString("BYBIT_API_KEY").empty()) { std::cerr << "Please set environment variable BYBIT_API_KEY" << std::endl; return EXIT_FAILURE; } if (UtilSystem::getEnvAsString("BYBIT_API_SECRET").empty()) { std::cerr << "Please set environment variable BYBIT_API_SECRET" << std::endl; return EXIT_FAILURE; } SessionOptions sessionOptions; SessionConfigs sessionConfigs; MyEventHandler eventHandler; Session session(sessionOptions, sessionConfigs, &eventHandler); Request request(Request::Operation::CREATE_ORDER, "bybit", "BTCUSDT"); request.appendParam({ {"SIDE", "BUY"}, {"QUANTITY", "0.0005"}, {"LIMIT_PRICE", "100000"}, }); session.sendRequest(request); std::this_thread::sleep_for(std::chrono::seconds(10)); session.stop(); std::cout << "Bye" << std::endl; return EXIT_SUCCESS; } ``` **Output 1:** ```console Received an event: Event [ type = RESPONSE, messageList = [ Message [ type = CREATE_ORDER, recapType = UNKNOWN, time = 1970-01-01T00:00:00.000000000Z, timeReceived = 2021-05-25T03:47:15.599562000Z, elementList = [ Element [ nameValueMap = { CLIENT_ORDER_ID = wBgmzOJbbMTCLJlwTrIeiH, CUMULATIVE_FILLED_PRICE_TIMES_QUANTITY = 0, CUMULATIVE_FILLED_QUANTITY = 0, INSTRUMENT = BTC-USDT, LIMIT_PRICE = 100000, ORDER_ID = 383781246, QUANTITY = 0.0005, SIDE = BUY, STATUS = live } ] ], correlationIdList = [ 5PN2qmWqBlQ9wQj99nsQzldVI5ZuGXbE ] ] ] ] Bye ``` * Request operation types: `CREATE_ORDER`, `CANCEL_ORDER`, `GET_ORDER`, `GET_OPEN_ORDERS`, `CANCEL_OPEN_ORDERS`, `GET_ACCOUNTS`, `GET_ACCOUNT_BALANCES`, `GET_ACCOUNT_POSITIONS`. * Request parameter names: `SIDE`, `QUANTITY`, `LIMIT_PRICE`, `ACCOUNT_ID`, `ACCOUNT_TYPE`, `ORDER_ID`, `CLIENT_ORDER_ID`, `PARTY_ID`, `ORDER_TYPE`, `LEVERAGE`. Instead of these convenient names you can also choose to use arbitrary parameter names and they will be passed to the exchange's native API. See [this example](example/execution_management_advanced_request/main.cpp). **Objective 2:** For a specific exchange and instrument, receive order updates. **Code 2:** [C++](example/execution_management_simple_subscription/main.cpp) / [Python](binding/python/example/execution_management_simple_subscription/main.py) / [Java](binding/java/example/execution_management_simple_subscription/Main.java) / [C#](binding/csharp/example/execution_management_simple_subscription/MainProgram.cs) / [Go](binding/go/example/execution_management_simple_subscription/main.go) / [Javascript](binding/javascript/example/execution_management_simple_subscription/index.js) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { Logger* Logger::logger = nullptr; // This line is needed. class MyEventHandler : public EventHandler { public: void processEvent(const Event& event, Session* sessionPtr) override { if (event.getType() == Event::Type::SUBSCRIPTION_STATUS) { std::cout << "Received an event of type SUBSCRIPTION_STATUS:\n" + event.toPrettyString(2, 2) << std::endl; auto message = event.getMessageList().at(0); if (message.getType() == Message::Type::SUBSCRIPTION_STARTED) { Request request(Request::Operation::CREATE_ORDER, "bybit", "BTCUSDT"); request.appendParam({ {"SIDE", "BUY"}, {"LIMIT_PRICE", "20000"}, {"QUANTITY", "0.001"}, {"CLIENT_ORDER_ID", request.generateNextClientOrderId()}, }); sessionPtr->sendRequest(request); } } else if (event.getType() == Event::Type::SUBSCRIPTION_DATA) { std::cout << "Received an event of type SUBSCRIPTION_DATA:\n" + event.toPrettyString(2, 2) << std::endl; } } }; } /* namespace ccapi */ using ::ccapi::MyEventHandler; using ::ccapi::Request; using ::ccapi::Session; using ::ccapi::SessionConfigs; using ::ccapi::SessionOptions; using ::ccapi::Subscription; using ::ccapi::UtilSystem; int main(int argc, char** argv) { if (UtilSystem::getEnvAsString("BYBIT_API_KEY").empty()) { std::cerr << "Please set environment variable BYBIT_API_KEY" << std::endl; return EXIT_FAILURE; } if (UtilSystem::getEnvAsString("BYBIT_API_SECRET").empty()) { std::cerr << "Please set environment variable BYBIT_API_SECRET" << std::endl; return EXIT_FAILURE; } SessionOptions sessionOptions; SessionConfigs sessionConfigs; MyEventHandler eventHandler; Session session(sessionOptions, sessionConfigs, &eventHandler); Subscription subscription("bybit", "BTCUSDT", "ORDER_UPDATE"); session.subscribe(subscription); std::this_thread::sleep_for(std::chrono::seconds(10)); session.stop(); std::cout << "Bye" << std::endl; return EXIT_SUCCESS; } ``` **Output 2:** ```console Received an event of type SUBSCRIPTION_STATUS: Event [ type = SUBSCRIPTION_STATUS, messageList = [ Message [ type = SUBSCRIPTION_STARTED, recapType = UNKNOWN, time = 1970-01-01T00:00:00.000000000Z, timeReceived = 2021-05-25T04:22:25.906197000Z, elementList = [ ], correlationIdList = [ 5PN2qmWqBlQ9wQj99nsQzldVI5ZuGXbE ] ] ] ] Received an event of type SUBSCRIPTION_DATA: Event [ type = SUBSCRIPTION_DATA, messageList = [ Message [ type = EXECUTION_MANAGEMENT_EVENTS_ORDER_UPDATE, recapType = UNKNOWN, time = 2021-05-25T04:22:26.653785000Z, timeReceived = 2021-05-25T04:22:26.407419000Z, elementList = [ Element [ nameValueMap = { CLIENT_ORDER_ID = , INSTRUMENT = BTC-USDT, LIMIT_PRICE = 20000, ORDER_ID = 6ca39186-be79-4777-97ab-1695fccd0ce4, QUANTITY = 0.001, SIDE = BUY, STATUS = live } ] ], correlationIdList = [ 5PN2qmWqBlQ9wQj99nsQzldVI5ZuGXbE ] ] ] ] Bye ``` * Subscription fields: `ORDER_UPDATE`, `PRIVATE_TRADE`, `PRIVATE_TRADE_LITE`, `BALANCE_UPDATE`, `POSITION_UPDATE`. ### Advanced Execution Management #### Specify correlation id Instantiate `Request` with the desired `correlationId`. The `correlationId` should be unique. ``` Request request(Request::Operation::CREATE_ORDER, "bybit", "BTCUSDT", "cool correlation id"); ``` Instantiate `Subscription` with the desired `correlationId`. ``` Subscription subscription("bybit", "BTCUSDT", "ORDER_UPDATE", "", "cool correlation id"); ``` This is used to match a particular request or subscription with its returned data. Within each `Message` there is a `correlationIdList` to identify the request or subscription that requested the data. #### Multiple exchanges and/or instruments Send a `std::vector`. ``` Request request_1(Request::Operation::CREATE_ORDER, "bybit", "BTCUSDT", "cool correlation id for BTC"); request_1.appendParam(...); Request request_2(Request::Operation::CREATE_ORDER, "bybit", "ETH-USDT", "cool correlation id for ETH"); request_2.appendParam(...); std::vector requests = {request_1, request_2}; session.sendRequest(requests); ``` Subscribe one `Subscription` per exchange with a comma separated string of instruments. ``` Subscription subscription("bybit", "BTC-USDT,ETH-USDT", "ORDER_UPDATE"); ``` #### Multiple subscription fields Subscribe one `Subscription` with a comma separated string of fields. ``` Subscription subscription("bybit", "BTCUSDT", "ORDER_UPDATE,PRIVATE_TRADE"); ``` #### Make Session::sendRequest blocking Instantiate `Session` without `EventHandler` argument, and pass a pointer to `Queue` as an additional argument. ``` Session session(sessionOptions, sessionConfigs); ... Queue eventQueue; session.sendRequest(request, &eventQueue); // block until a response is received std::vector eventList = eventQueue.purge(); ``` #### Provide API credentials for an exchange There are 3 ways to provide API credentials (listed with increasing priority). * Set the relevent environment variables. Some exchanges might need additional credentials other than API keys and secrets: e.g. `OKX_API_PASSPHRASE`, `KUCOIN_API_PASSPHRASE`, `BINANCE_USDS_FUTURES_WEBSOCKET_ORDER_ENTRY_API_KEY`, `BINANCE_USDS_FUTURES_WEBSOCKET_ORDER_ENTRY_API_PRIVATE_KEY_PATH`, `BINANCE_COIN_FUTURES_WEBSOCKET_ORDER_ENTRY_API_KEY`, `BINANCE_COIN_FUTURES_WEBSOCKET_ORDER_ENTRY_API_PRIVATE_KEY_PATH`, `BINANCE_WEBSOCKET_ORDER_ENTRY_API_KEY`, `BINANCE_WEBSOCKET_ORDER_ENTRY_API_PRIVATE_KEY_PATH`. See section "exchange API credentials" in [`include/ccapi_cpp/ccapi_macro.h`](include/ccapi_cpp/ccapi_macro.h). * Provide credentials to `SessionConfigs`. ``` sessionConfigs.setCredential({ {"BYBIT_API_KEY", ...}, {"BYBIT_API_SECRET", ...} }); ``` * Provide credentials to `Request` or `Subscription`. ``` Request request(Request::Operation::CREATE_ORDER, "bybit", "BTCUSDT", "", { {"BYBIT_API_KEY", ...}, {"BYBIT_API_SECRET", ...} }); ``` ``` Subscription subscription("bybit", "BTCUSDT", "ORDER_UPDATE", "", "", { {"BYBIT_API_KEY", ...}, {"BYBIT_API_SECRET", ...} }); ``` #### Complex request parameters Please follow the exchange's API documentations: e.g. https://bybit-exchange.github.io/docs/v5/order/create-order. ``` Request request(Request::Operation::CREATE_ORDER, "bybit", "BTCUSDT"); request.appendParam({ {"isLeverage", "1"}, }); ``` #### Send request by Websocket API For okx, cryptocom: ``` std::string websocketOrderEntrySubscriptionCorrelationId("any"); Subscription subscription("bybit", "", "ORDER_UPDATE", "", websocketOrderEntrySubscriptionCorrelationId); session.subscribe(subscription); ... Request request(Request::Operation::CREATE_ORDER, "bybit", "BTCUSDT"); request.appendParam({ {"SIDE", "BUY"}, {"LIMIT_PRICE", "20000"}, {"QUANTITY", "0.001"}, }); session.sendRequestByWebsocket(websocketOrderEntrySubscriptionCorrelationId, request); ``` For bybit, binance, binance-usds-futures, binance-coin-futures: ``` std::string websocketOrderEntrySubscriptionCorrelationId("any"); Subscription subscription_1("bybit", "", "ORDER_UPDATE"); Subscription subscription_2("bybit", "", "WEBSOCKET_ORDER_ENTRY", "", websocketOrderEntrySubscriptionCorrelationId); std::vector subscriptions = {subscription_1, subscription_2}; session.subscribe(subscriptions); ... Request request(Request::Operation::CREATE_ORDER, "bybit", "BTCUSDT"); request.appendParam({ {"SIDE", "BUY"}, {"LIMIT_PRICE", "20000"}, {"QUANTITY", "0.001"}, }); session.sendRequestByWebsocket(websocketOrderEntrySubscriptionCorrelationId, request); ``` #### Specify instrument type Some exchanges (i.e. bybit, bitget-uta) 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:** For a specific exchange and instrument, submit a simple limit order. **Code:** [C++](example/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) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { Logger* Logger::logger = nullptr; // This line is needed. class MyEventHandler : public EventHandler { public: MyEventHandler(const std::string& fixSubscriptionCorrelationId) : fixSubscriptionCorrelationId(fixSubscriptionCorrelationId) {} void processEvent(const Event& event, Session* sessionPtr) override { std::cout << "Received an event:\n" + event.toPrettyString(2, 2) << std::endl; if (!willSendRequest) { sessionPtr->setTimer("id", 1000, nullptr, [this, sessionPtr]() { Request request(Request::Operation::FIX, "binance"); request.appendFixParam({ {35, "D"}, {11, request.generateNextClientOrderId()}, {55, "BTCUSDT"}, {54, "1"}, {44, "100000"}, {38, "0.0001"}, {40, "2"}, {59, "1"}, }); sessionPtr->sendRequestByFix(this->fixSubscriptionCorrelationId, request); }); willSendRequest = true; } } private: std::string fixSubscriptionCorrelationId; bool willSendRequest{}; }; } /* namespace ccapi */ using ::ccapi::MyEventHandler; using ::ccapi::Session; using ::ccapi::SessionConfigs; using ::ccapi::SessionOptions; using ::ccapi::Subscription; using ::ccapi::UtilSystem; int main(int argc, char** argv) { if (UtilSystem::getEnvAsString("BINANCE_FIX_API_KEY").empty()) { std::cerr << "Please set environment variable BINANCE_FIX_API_KEY" << std::endl; return EXIT_FAILURE; } if (UtilSystem::getEnvAsString("BINANCE_FIX_API_PRIVATE_KEY_PATH").empty()) { std::cerr << "Please set environment variable BINANCE_FIX_API_PRIVATE_KEY_PATH" << std::endl; return EXIT_FAILURE; } SessionOptions sessionOptions; SessionConfigs sessionConfigs; std::string fixSubscriptionCorrelationId("any"); MyEventHandler eventHandler(fixSubscriptionCorrelationId); Session session(sessionOptions, sessionConfigs, &eventHandler); Subscription subscription("binance", "", "FIX", "", fixSubscriptionCorrelationId); session.subscribe(subscription); std::this_thread::sleep_for(std::chrono::seconds(10)); session.stop(); std::cout << "Bye" << std::endl; return EXIT_SUCCESS; } ``` **Output:** ```console Received an event: Event [ type = SESSION_STATUS, messageList = [ Message [ type = SESSION_CONNECTION_UP, recapType = UNKNOWN, time = 1970-01-01T00:00:00.000000000Z, timeReceived = 2025-08-08T18:50:06.816550779Z, elementList = [ Element [ tagValueList = [ ], nameValueMap = { CONNECTION_ID = IF8j4HbdLP0, CONNECTION_URL = tcp+tls://fix-oe.binance.com:9000 } ] ], correlationIdList = [ any ], ] ] ] Received an event: Event [ type = AUTHORIZATION_STATUS, messageList = [ Message [ type = AUTHORIZATION_SUCCESS, recapType = UNKNOWN, time = 1970-01-01T00:00:00.000000000Z, timeReceived = 2025-08-08T18:50:06.819417404Z, elementList = [ Element [ tagValueList = [ (35, "A"), (98, "0"), (108, "60"), (25037, "e9ed8253-8f49-4a1b-bba9-718ff73991e5") ], nameValueMap = { } ] ], correlationIdList = [ any ], ] ] ] Received an event: Event [ type = FIX, messageList = [ Message [ type = FIX, recapType = UNKNOWN, time = 1970-01-01T00:00:00.000000000Z, timeReceived = 2025-08-08T18:50:07.820112736Z, elementList = [ Element [ tagValueList = [ (35, "8"), (17, "100146443390"), (11, "x-XHKUG2CH-1754679007000"), (37, "47156106695"), (38, "0.00010000"), (40, "2"), (54, "1"), (55, "BTCUSDT"), (44, "100000.00000000"), (59, "1"), (60, "20250808-18:50:07.819027"), (25018, "20250808-18:50:07.819027"), (25001, "3"), (150, "0"), (14, "0.00000000"), (151, "0.00010000"), (25017, "0.00000000"), (1057, "Y"), (32, "0.00000000"), (39, "0"), (636, "Y"), (25023, "20250808-18:50:07.819027") ], nameValueMap = { } ] ], correlationIdList = [ any ], ] ] ] Bye ``` ### More Advanced Topics #### Handle events in "immediate" vs. "batching" mode In general there are 2 ways to handle events. * When a `Session` is instantiated with an `eventHandler` argument, it will handle events in immediate mode. The `processEvent` method in the `eventHandler` will be invoked immediately when an `Event` is available, and the invocation will run on the thread where `boost::asio::io_context` runs. When a `Session` is instantiated with an `eventHandler` and an `eventDispatcher` argument, it will also handle events in immediate mode. The `processEvent` method in the `eventHandler` will also be invoked immediately when an `Event` is available, but the invocation will run in the thread(s) provided by the `eventDispatcher` therefore not blocking the thread where `boost::asio::io_context` runs. `EventHandler`s and/or `EventDispatcher`s can be shared among different sessions. Otherwise, different sessions are independent from each other. An example can be found [here](example/market_data_advanced_subscription/main.cpp). * When a `Session` is instantiated without an `eventHandler` argument, it will handle events in batching mode. The events will be batched into an internal `Queue` and can be retrieved by ``` std::vector eventList = session.getEventQueue().purge(); ``` An example can be found [here](example/market_data_advanced_subscription/main.cpp). #### Thread safety * The following methods are implemented to be thread-safe: `Session::sendRequest`, `Session::subscribe`, `Session::sendRequestByFix`, `Session::setTimer`, all public methods in `Queue`. * 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. #### Enable library logging [C++](example/enable_library_logging/main.cpp) / [Python](binding/python/example/enable_library_logging/main.py) / [Java](binding/java/example/enable_library_logging/Main.java) / [C#](binding/csharp/example/enable_library_logging/MainProgram.cs) / [Go](binding/go/example/enable_library_logging/main.go) Extend a subclass, e.g. `MyLogger`, from class `Logger` and override method `logMessage`. Assign a `MyLogger` pointer to `Logger::logger`. Add one of the following macros in the compiler command line: `CCAPI_ENABLE_LOG_TRACE`, `CCAPI_ENABLE_LOG_DEBUG`, `CCAPI_ENABLE_LOG_INFO`, `CCAPI_ENABLE_LOG_WARN`, `CCAPI_ENABLE_LOG_ERROR`, `CCAPI_ENABLE_LOG_FATAL`. Enable logging if you'd like to inspect raw responses/messages from the exchange for troubleshooting purposes. ``` namespace ccapi { class MyLogger final : public Logger { public: void logMessage(const std::string& severity, const std::string& threadId, const std::string& timeISO, const std::string& fileName, const std::string& lineNumber, const std::string& message) override { std::lock_guard lock(m); std::cout << threadId << ": [" << timeISO << "] {" << fileName << ":" << lineNumber << "} " << severity << std::string(8, ' ') << message << std::endl; } private: std::mutex m; }; MyLogger myLogger; Logger* Logger::logger = &myLogger; } /* namespace ccapi */ ``` #### Set timer [C++](example/set_timer/main.cpp) To 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. ``` sessionPtr->setTimer( "id", 1000, [](const boost::system::error_code&) { std::cout << std::string("Timer error handler is triggered at ") + UtilTime::getISOTimestamp(UtilTime::now()) << std::endl; }, []() { std::cout << std::string("Timer success handler is triggered at ") + UtilTime::getISOTimestamp(UtilTime::now()) << std::endl; }); ``` #### Heartbeat [C++](example/heartbeat/main.cpp) To receive heartbeat events, instantiate a `Subscription` object with field `HEARTBEAT` and subscribe it. ``` Subscription subscription("", "", "HEARTBEAT", "HEARTBEAT_INTERVAL_MILLISECONDS=1000"); session.subscribe(subscription); ``` #### Use multiple sessions [C++](example/use_multiple_sessions/main.cpp) Multiple `session` instances, each with their own `SessionOptions` and `SessionConfigs`, can share a common `EventHandler`. If no `EventDispatcher` is provided, thread safety must be maintained by using a shared `ServiceContext`. ``` Subscription subscription("", "", "HEARTBEAT", "HEARTBEAT_INTERVAL_MILLISECONDS=1000"); 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/override_exchange_url_at_runtime/main.cpp). These can be useful if you need to connect to test accounts (e.g. https://testnet.bybit.com/). #### Connect to a proxy Instantiate `Subscription` with the desired `proxyUrl`. ``` Subscription subscription("bybit", "BTCUSDT", "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/reduce_build_time). ## Exchange Specific Notes ### Hyperliquid * If you need execution management, secp256k1 and msgpack are needed. For example, a CMakeLists.txt for macOS will look like ``` if(APPLE) find_path(SECP256K1_INCLUDE_DIR secp256k1.h PATHS /opt/homebrew/include) find_path( MSGPACK_INCLUDE_DIR NAMES msgpack.hpp PATHS /opt/homebrew/include) find_library(SECP256K1_LIBRARY secp256k1 PATHS /opt/homebrew/lib) find_library(MSGPACKC_LIBRARY msgpackc PATHS /opt/homebrew/lib) endif() add_compile_definitions(CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT) add_compile_definitions(CCAPI_ENABLE_EXCHANGE_HYPERLIQUID) add_executable(${NAME} main.cpp) add_dependencies(${NAME} boost rapidjson) target_include_directories(${NAME} PRIVATE ${SECP256K1_INCLUDE_DIR} ${MSGPACK_INCLUDE_DIR}) target_link_libraries(${NAME} PRIVATE ${SECP256K1_LIBRARY} ${MSGPACKC_LIBRARY}) ``` * For create order and cancel order, please provide hyperliquid's asset id as instrument (e.g. BTC = 0 on mainnet. See https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/asset-ids). For example, ``` Request request(Request::Operation::CREATE_ORDER, "hyperliquid", "0"); // Corresponds to https://app.hyperliquid.xyz/trade/BTC ``` * For the client order ID, please use either request.generateNextClientOrderId() or your own implementation, as long as it complies with the Hyperliquid requirements: https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/api/exchange-endpoint#place-an-order. Orders submitted with an invalid client order ID will be rejected. ## Performance Tuning * Turn on compiler optimization flags (e.g. `cmake -DCMAKE_BUILD_TYPE=Release ...`). * 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. * Only enable the services and exchanges that you need. * Handle events in ["batching" mode](#handle-events-in-immediate-vs-batching-mode) if your application (e.g. market data archiver) isn't latency sensitive. ## Known Issues and Workarounds * Kraken invalid nonce errors. Give the API key a nonce window (https://support.kraken.com/hc/en-us/articles/360001148023-What-is-a-nonce-window-). We use unix timestamp with microsecond resolution as nonce and therefore a nonce window of 500000 translates to a tolerance of 0.5 second.