Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8ee825e
fix: fix ServiceContext
May 27, 2025
f77ac95
fix: remove spurious errors associated with intended timer cancel
May 27, 2025
fc32ba5
feat: change how to do order entry via websocket
May 27, 2025
02bdc14
fix compile issues
May 28, 2025
13cc800
fix compile issues
May 28, 2025
8fba034
fix compile issue on mac
May 28, 2025
836dc74
update test.yml
May 28, 2025
29616de
Create dependabot.yml
cryptochassis May 28, 2025
375799b
Merge branch 'develop' of https://github.com/crypto-chassis/ccapi int…
May 28, 2025
7cf2119
build(deps): bump actions/setup-node from 1 to 4
dependabot[bot] May 28, 2025
aa93255
build(deps): bump actions/checkout from 1 to 4
dependabot[bot] May 28, 2025
f9d1bdd
update test.yml
May 28, 2025
81f68ca
update test.yml
May 28, 2025
b36f9a3
arm64
May 28, 2025
d7fe380
CMAKE_VERSION: 4.0.2
May 28, 2025
da0028d
Merge pull request #512 from crypto-chassis/dependabot/github_actions…
cryptochassis May 29, 2025
42643e7
build(deps): bump actions/setup-node from 1 to 4
dependabot[bot] May 29, 2025
2f3278b
resolve merge conflict
May 29, 2025
5d95888
Merge branch 'dependabot/github_actions/actions/setup-node-4' of http…
May 29, 2025
04c0f9d
Merge pull request #511 from crypto-chassis/dependabot/github_actions…
cryptochassis May 29, 2025
1d3d22c
Merge branch 'develop' into feat-rewrite-websocket-order-entry
May 29, 2025
d0019ed
fix ci
May 29, 2025
fe565c2
add -DCMAKE_OSX_ARCHITECTURES=arm64
May 29, 2025
a5f5968
Merge pull request #510 from crypto-chassis/feat-rewrite-websocket-or…
cryptochassis May 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ jobs:
name: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: 'echo ''{
"name": "ccapi_cpp",
"version": "1.0.0",
Expand Down
24 changes: 14 additions & 10 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ jobs:
name: ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }}
strategy:
max-parallel: 1
fail-fast: false
matrix:
config:
Expand All @@ -34,11 +33,12 @@ jobs:
os: ubuntu-latest,
cc: "gcc", cxx: "g++"
}
# - {
# name: "macOS Latest Clang",
# os: macos-latest,
# cc: "clang", cxx: "clang++"
# }
- {
name: "macOS Latest Clang",
os: macos-latest,
cc: "clang", cxx: "clang++",
arch: "arm64"
}
# - {
# name: "Windows Latest MinGW",
# os: windows-latest,
Expand All @@ -53,7 +53,7 @@ jobs:
# }

steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4

- name: Download Ninja and CMake
id: cmake_and_ninja
Expand Down Expand Up @@ -126,7 +126,7 @@ jobs:
message(STATUS "Using host CMake version: ${cmake_version}")
if ("${{ runner.os }}" STREQUAL "macOS")
execute_process(
COMMAND brew reinstall openssl@1.1
COMMAND brew reinstall openssl@3
)
elseif ("${{ runner.os }}" STREQUAL "Windows")
execute_process(
Expand Down Expand Up @@ -206,6 +206,7 @@ jobs:

execute_process(
COMMAND ${{ steps.cmake_and_ninja.outputs.cmake_dir }}/cmake
-DCMAKE_OSX_ARCHITECTURES=arm64
-DBUILD_TEST_BUILD=OFF
-DBUILD_TEST_UNIT=OFF
-S test
Expand All @@ -223,6 +224,7 @@ jobs:

execute_process(
COMMAND ${{ steps.cmake_and_ninja.outputs.cmake_dir }}/cmake
-DCMAKE_OSX_ARCHITECTURES=arm64
-S example
-B example/build
-D CMAKE_BUILD_TYPE=$ENV{CMAKE_BUILD_TYPE}
Expand All @@ -239,8 +241,9 @@ jobs:
if ("${{ runner.os }}" STREQUAL "macOS" OR "${{ runner.os }}" STREQUAL "Linux")
execute_process(
COMMAND ${{ steps.cmake_and_ninja.outputs.cmake_dir }}/cmake
-DCMAKE_OSX_ARCHITECTURES=arm64
-DBUILD_PYTHON=ON
-DBUILD_JAVA=ON
# -DBUILD_JAVA=ON
# -DBUILD_CSHARP=ON
# -DBUILD_GO=ON
# -DBUILD_JAVASCRIPT=ON
Expand Down Expand Up @@ -370,7 +373,8 @@ jobs:

execute_process(
COMMAND ${{ steps.cmake_and_ninja.outputs.cmake_dir }}/cmake
-DBUILD_TEST_BUILD=OFF
-DCMAKE_OSX_ARCHITECTURES=arm64
-DBUILD_TEST_BUILD=ON
-DBUILD_TEST_UNIT=ON
-S test
-B test/build
Expand Down
156 changes: 91 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,62 @@
# Some breaking changes introduced
* Please update boost version to at least 1.87.0.
* When a subscription fails due to the underlying websocket connection fails to open, the emitted message type is SUBSCRIPTION_FAILURE_DUE_TO_CONNECTION_FAILURE instead of SUBSCRIPTION_FAILURE.
* Removed the spot market making application and the single order execution application.
* We made a change on how to "Send request by Websocket API".

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/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)
- [Override exchange urls](#override-exchange-urls)
- [Complex request parameters](#complex-request-parameters-1)
- [Send request by Websocket API](#send-request-by-websocket-api)
- [Specify instrument type](#specify-instrument-type)
- [FIX API](#fix-api)
- [More Advanced Topics](#more-advanced-topics)
- [Handle events in "immediate" vs. "batching" mode](#handle-events-in-immediate-vs-batching-mode)
- [Thread safety](#thread-safety)
- [Enable library logging](#enable-library-logging)
- [Set timer](#set-timer)
- [Performance Tuning](#performance-tuning)
- [Known Issues and Workarounds](#known-issues-and-workarounds)
- [Contributing](#contributing)

**Table of Contents** *generated with [DocToc](https://github.com/ktechhub/doctoc)*

<!---toc start-->

* [Some breaking changes introduced](#some-breaking-changes-introduced)
* [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)
* [Override exchange urls](#override-exchange-urls)
* [Complex request parameters](#complex-request-parameters-1)
* [Send request by Websocket API](#send-request-by-websocket-api)
* [Specify instrument type](#specify-instrument-type)
* [FIX API](#fix-api)
* [More Advanced Topics](#more-advanced-topics)
* [Handle events in "immediate" vs. "batching" mode](#handle-events-in-immediate-vs-batching-mode)
* [Thread safety](#thread-safety)
* [Enable library logging](#enable-library-logging)
* [Set timer](#set-timer)
* [Performance Tuning](#performance-tuning)
* [Known Issues and Workarounds](#known-issues-and-workarounds)
* [Contributing](#contributing)

<!---toc end-->

<!-- END doctoc generated TOC please keep comment here to allow auto update -->


# 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.
Expand Down Expand Up @@ -220,7 +225,7 @@ Logger* Logger::logger = nullptr; // This line is needed.

class MyEventHandler : public EventHandler {
public:
bool processEvent(const Event& event, Session* session) override {
bool processEvent(const Event& event, Session* sessionPtr) override {
std::cout << "Received an event:\n" + event.toStringPretty(2, 2) << std::endl;
return true;
}
Expand Down Expand Up @@ -297,7 +302,7 @@ Logger* Logger::logger = nullptr; // This line is needed.

class MyEventHandler : public EventHandler {
public:
bool processEvent(const Event& event, Session* session) override {
bool 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.toStringPretty(2, 2) << std::endl;
} else if (event.getType() == Event::Type::SUBSCRIPTION_DATA) {
Expand Down Expand Up @@ -386,13 +391,15 @@ Request request_1(Request::Operation::GET_RECENT_TRADES, "okx", "BTC-USDT", "coo
request_1.appendParam(...);
Request request_2(Request::Operation::GET_RECENT_TRADES, "binance", "ETH-USDT", "cool correlation id for ETH");
request_2.appendParam(...);
session.sendRequest({request_1, request_2});
std::vector<ccapi::Request> requests = {request_1, request_2};
session.sendRequest(requests);
```
Subscribe a `std::vector<Subscription>`.
```
Subscription subscription_1("okx", "BTC-USDT", "MARKET_DEPTH", "", "cool correlation id for okx BTC-USDT");
Subscription subscription_2("binance", "ETH-USDT", "MARKET_DEPTH", "", "cool correlation id for binance ETH-USDT");
session.subscribe({subscription_1, subscription_2});
std::vector<ccapi::Subscription> subscriptions = {subscription_1, subscription_2};
session.subscribe(subscriptions);
```

#### Receive subscription events at periodic intervals
Expand Down Expand Up @@ -434,7 +441,7 @@ Subscription subscription("okx", "BTC-USDT", "TRADE", "CONFLATE_INTERVAL_MILLISE

Instantiate `Subscription` with field `CANDLESTICK` and option `CANDLESTICK_INTERVAL_SECONDS` set to be the desired interval.
```
Subscription subscription("okx", "BTC-USDTT", "CANDLESTICK", "CANDLESTICK_INTERVAL_SECONDS=60");
Subscription subscription("okx", "BTC-USDT", "CANDLESTICK", "CANDLESTICK_INTERVAL_SECONDS=60");
```

#### Send generic public requests
Expand Down Expand Up @@ -487,7 +494,7 @@ Logger* Logger::logger = nullptr; // This line is needed.

class MyEventHandler : public EventHandler {
public:
bool processEvent(const Event& event, Session* session) override {
bool processEvent(const Event& event, Session* sessionPtr) override {
std::cout << "Received an event:\n" + event.toStringPretty(2, 2) << std::endl;
return true;
}
Expand Down Expand Up @@ -585,7 +592,7 @@ Logger* Logger::logger = nullptr; // This line is needed.

class MyEventHandler : public EventHandler {
public:
bool processEvent(const Event& event, Session* session) override {
bool 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.toStringPretty(2, 2) << std::endl;
auto message = event.getMessageList().at(0);
Expand All @@ -597,7 +604,7 @@ class MyEventHandler : public EventHandler {
{"QUANTITY", "0.001"},
{"CLIENT_ORDER_ID", "6d4eb0fb"},
});
session->sendRequest(request);
sessionPtr->sendRequest(request);
}
} else if (event.getType() == Event::Type::SUBSCRIPTION_DATA) {
std::cout << "Received an event of type SUBSCRIPTION_DATA:\n" + event.toStringPretty(2, 2) << std::endl;
Expand Down Expand Up @@ -712,7 +719,8 @@ Request request_1(Request::Operation::CREATE_ORDER, "okx", "BTC-USDT", "cool cor
request_1.appendParam(...);
Request request_2(Request::Operation::CREATE_ORDER, "okx", "ETH-USDT", "cool correlation id for ETH");
request_2.appendParam(...);
session.sendRequest({request_1, request_2});
std::vector<ccapi::Request> requests = {request_1, request_2};
session.sendRequest(requests);
```
Subscribe one `Subscription` per exchange with a comma separated string of instruments.
```
Expand Down Expand Up @@ -774,23 +782,41 @@ request.appendParam({
```

#### Send request by Websocket API
For okx:
```
Subscription subscription("okx", "BTC-USDTT", "ORDER_UPDATE", "", "same correlation id for subscription and request");
std::string websocketOrderEntrySubscriptionCorrelationId("any");
Subscription subscription("okx", "", "ORDER_UPDATE", "", websocketOrderEntrySubscriptionCorrelationId);
session.subscribe(subscription);
...
Request request(Request::Operation::CREATE_ORDER, "okx", "BTC-USDTT", "same correlation id for subscription and request");
Request request(Request::Operation::CREATE_ORDER, "okx", "BTC-USDT");
request.appendParam({
{"SIDE", "BUY"},
{"LIMIT_PRICE", "20000"},
{"QUANTITY", "0.001"},
});
session.sendRequestByWebsocket(websocketOrderEntrySubscriptionCorrelationId, request);
```
For bybit:
```
std::string websocketOrderEntrySubscriptionCorrelationId("any");
Subscription subscription_1("bybit", "", "ORDER_UPDATE");
Subscription subscription_2("bybit", "", "WEBSOCKET_ORDER_ENTRY", "", websocketOrderEntrySubscriptionCorrelationId);
std::vector<ccapi::Subscription> 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(request);
session.sendRequestByWebsocket(websocketOrderEntrySubscriptionCorrelationId, request);
```

#### Specify instrument type
Some exchanges (i.e. bybit) might need instrument type for `Subscription`. Use `Subscription`'s `setInstrumentType` method.
```
Subscription subscription("bybit", "BTCUSDTT", "MARKET_DEPTH");
Subscription subscription("bybit", "BTCUSDT", "MARKET_DEPTH");
subscription.setInstrumentType("spot");
session.subscribe(subscription);
```
Expand All @@ -810,7 +836,7 @@ namespace ccapi {
Logger* Logger::logger = nullptr; // This line is needed.
class MyEventHandler : public EventHandler {
public:
bool processEvent(const Event& event, Session* session) override {
bool processEvent(const Event& event, Session* sessionPtr) override {
if (event.getType() == Event::Type::AUTHORIZATION_STATUS) {
std::cout << "Received an event of type AUTHORIZATION_STATUS:\n" + event.toStringPretty(2, 2) << std::endl;
auto message = event.getMessageList().at(0);
Expand All @@ -826,7 +852,7 @@ class MyEventHandler : public EventHandler {
{40, "2"},
{59, "1"},
});
session->sendRequestByFix(request);
sessionPtr->sendRequestByFix(request);
}
} else if (event.getType() == Event::Type::FIX) {
std::cout << "Received an event of type FIX:\n" + event.toStringPretty(2, 2) << std::endl;
Expand Down Expand Up @@ -975,7 +1001,7 @@ Logger* Logger::logger = &myLogger;

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.
```
session->setTimer(
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;
Expand Down
2 changes: 1 addition & 1 deletion example/src/execution_management_advanced_request/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Logger* Logger::logger = nullptr; // This line is needed.

class MyEventHandler : public EventHandler {
public:
bool processEvent(const Event& event, Session* session) override {
bool processEvent(const Event& event, Session* sessionPtr) override {
std::cout << "Received an event:\n" + event.toStringPretty(2, 2) << std::endl;
return true;
}
Expand Down
Loading
Loading