Table of Contents generated with DocToc
develop
branch may contain experimental features.master
branch represents the most recent stable release.CCAPI_ENABLE_SERVICE_MARKET_DATA
, CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT
, CCAPI_ENABLE_SERVICE_FIX
, etc. and exchange enablement macros such as CCAPI_ENABLE_EXCHANGE_COINBASE
, etc. These macros can be found at the top of include/ccapi_cpp/ccapi_session.h
.-pthread
for GCC and MinGW.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
.-Wa,-mbig-obj
. See https://github.com/assimp/assimp/issues/2067.-O1
or -O2
. See https://stackoverflow.com/questions/14125007/gcc-string-table-overflow-error-during-compilation.-O3 -DNDEBUG
.brew install SWIG
. On Linux, sudo apt-get install -y swig
.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 .
binding/build/<language>/packaging/<BUILD_VERSION>
directory. SWIG generated raw files and build artifacts are located in the binding/build/<language>/ccapi_binding_<language>
directory.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.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
.venv
or conda
) with Python 3.JAVA_HOME
is correct.npm install -g node-gyp
include/ccapi_cpp/ccapi_macro.h
mkdir example/build
cd example/build
rm -rf * (if rebuild from scratch)
cmake ..
cmake --build . --target <example-name>
example/build/src/<example-name>/<example-name>
. Run it.python3 main.py
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
javac
's classpath includes binding/build/java/packaging/1.0.0/ccapi-1.0.0.jar
.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.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
LD_LIBRARY_PATH
includes binding/build/csharp/packaging/1.0.0
.go clean (if rebuild from scratch)
source ../../../build/go/packaging/1.0.0/export_compiler_options.sh (this step is important)
go build .
./main
rm -rf node_modules (if rebuild from scratch)
npm install
node index.js
Objective 1:
For a specific exchange and instrument, get recents trades.
Code 1:
C++ / Python / Java / C# / Go / Javascript
#include "ccapi_cpp/ccapi_session.h"
namespace ccapi {
Logger* Logger::logger = nullptr; // This line is needed.
class MyEventHandler : public EventHandler {
public:
bool processEvent(const Event& event, Session* session) override {
std::cout << "Received an event:\n" + event.toStringPretty(2, 2) << std::endl;
return true;
}
};
} /* namespace ccapi */
using ::ccapi::MyEventHandler;
using ::ccapi::Request;
using ::ccapi::Session;
using ::ccapi::SessionConfigs;
using ::ccapi::SessionOptions;
using ::ccapi::toString;
int main(int argc, char** argv) {
SessionOptions sessionOptions;
SessionConfigs sessionConfigs;
MyEventHandler eventHandler;
Session session(sessionOptions, sessionConfigs, &eventHandler);
Request request(Request::Operation::GET_RECENT_TRADES, "coinbase", "BTC-USD");
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:
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
GET_INSTRUMENT
, GET_INSTRUMENTS
, 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), ``.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.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++ / Python / Java / C# / Go / Javascript
#include "ccapi_cpp/ccapi_session.h"
namespace ccapi {
Logger* Logger::logger = nullptr; // This line is needed.
class MyEventHandler : public EventHandler {
public:
bool processEvent(const Event& event, Session *session) override {
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()) {
const std::map<std::string, std::string>& elementNameValueMap = element.getNameValueMap();
std::cout << " " + toString(elementNameValueMap) << std::endl;
}
}
}
return true;
}
};
} /* namespace ccapi */
using ::ccapi::MyEventHandler;
using ::ccapi::Session;
using ::ccapi::SessionConfigs;
using ::ccapi::SessionOptions;
using ::ccapi::Subscription;
int main(int argc, char **argv) {
SessionOptions sessionOptions;
SessionConfigs sessionConfigs;
MyEventHandler eventHandler;
Session session(sessionOptions, sessionConfigs, &eventHandler);
Subscription subscription("coinbase", "BTC-USD", "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:
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:
...
MARKET_DEPTH
, TRADE
, CANDLESTICK
, AGG_TRADE
(only applicable to binance family: https://binance-docs.github.io/apidocs/spot/en/#aggregate-trade-streams).Please follow the exchange's API documentations: e.g. https://docs.pro.coinbase.com/#pagination.
Request request(Request::Operation::GET_RECENT_TRADES, "coinbase", "BTC-USD");
request.appendParam({
{"before", "1"},
{"after", "3"},
{"limit", "1"},
});
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("coinbase", "BTC-USD", "MARKET_DEPTH", "MARKET_DEPTH_MAX=10");
Instantiate Request
with the desired correlationId. The correlationId
should be unique.
Request request(Request::Operation::GET_RECENT_TRADES, "coinbase", "BTC-USD", "cool correlation id");
Instantiate Subscription
with the desired correlationId.
Subscription subscription("coinbase", "BTC-USD", "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.
Send a std::vector<Request>
.
Request request_1(Request::Operation::GET_RECENT_TRADES, "coinbase", "BTC-USD", "cool correlation id for BTC");
request_1.appendParam(...);
Request request_2(Request::Operation::GET_RECENT_TRADES, "coinbase", "ETH-USD", "cool correlation id for ETH");
request_2.appendParam(...);
session.sendRequest({request_1, request_2});
Subscribe a std::vector<Subscription>
.
Subscription subscription_1("coinbase", "BTC-USD", "MARKET_DEPTH", "", "cool correlation id for coinbase BTC-USD");
Subscription subscription_2("binance-us", "ethusd", "MARKET_DEPTH", "", "cool correlation id for binance-us ethusd");
session.subscribe({subscription_1, subscription_2});
Instantiate Subscription
with option CONFLATE_INTERVAL_MILLISECONDS
set to be the desired interval.
Subscription subscription("coinbase", "BTC-USD", "MARKET_DEPTH", "CONFLATE_INTERVAL_MILLISECONDS=1000");
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("coinbase", "BTC-USD", "MARKET_DEPTH", "CONFLATE_INTERVAL_MILLISECONDS=1000&CONFLATE_GRACE_PERIOD_MILLISECONDS=0");
Instantiate Subscription
with option MARKET_DEPTH_RETURN_UPDATE
set to 1. This will return the order book updates instead of snapshots.
Subscription subscription("coinbase", "BTC-USD", "MARKET_DEPTH", "MARKET_DEPTH_RETURN_UPDATE=1&MARKET_DEPTH_MAX=2");
Instantiate Subscription
with field TRADE
.
Subscription subscription("coinbase", "BTC-USD", "TRADE");
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("coinbase", "BTC-USD", "TRADE", "CONFLATE_INTERVAL_MILLISECONDS=5000&CONFLATE_GRACE_PERIOD_MILLISECONDS=0");
Instantiate Subscription
with field CANDLESTICK
and option CANDLESTICK_INTERVAL_SECONDS
set to be the desired interval.
Subscription subscription("okx", "BTC-USDT", "CANDLESTICK", "CANDLESTICK_INTERVAL_SECONDS=60");
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, "binance");
request.appendParam({
{"HTTP_METHOD", "GET"},
{"HTTP_PATH", "/api/v3/historicalTrades"},
{"HTTP_QUERY_STRING", "symbol=BTCUSDT"},
});
Instantiate Subscription
with empty instrument, field GENERIC_PUBLIC_SUBSCRIPTION
and options set to be the desired websocket payload.
Subscription subscription("coinbase", "", "GENERIC_PUBLIC_SUBSCRIPTION", R"({"type":"subscribe","channels":[{"name":"status"}]})");
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, "coinbase");
request.appendParam({
{"HTTP_METHOD", "GET"},
{"HTTP_PATH", "/fills"},
{"HTTP_QUERY_STRING", "product_id=BTC-USD"},
});
Objective 1:
For a specific exchange and instrument, submit a simple limit order.
Code 1:
C++ / Python / Java / C# / Go / Javascript
#include "ccapi_cpp/ccapi_session.h"
namespace ccapi {
Logger* Logger::logger = nullptr; // This line is needed.
class MyEventHandler : public EventHandler {
public:
bool processEvent(const Event& event, Session *session) override {
std::cout << "Received an event: " + event.toStringPretty(2, 2) << std::endl;
return true;
}
};
} /* 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) {
std::string key = UtilSystem::getEnvAsString("BINANCE_US_API_KEY");
if (key.empty()) {
std::cerr << "Please set environment variable BINANCE_US_API_KEY" << std::endl;
return EXIT_FAILURE;
}
std::string secret = UtilSystem::getEnvAsString("BINANCE_US_API_SECRET");
if (secret.empty()) {
std::cerr << "Please set environment variable BINANCE_US_API_SECRET" << std::endl;
return EXIT_FAILURE;
}
SessionOptions sessionOptions;
SessionConfigs sessionConfigs;
MyEventHandler eventHandler;
Session session(sessionOptions, sessionConfigs, &eventHandler);
Request request(Request::Operation::CREATE_ORDER, "binance-us", "BTCUSD");
request.appendParam({
{"SIDE", "BUY"},
{"QUANTITY", "0.0005"},
{"LIMIT_PRICE", "20000"}
});
session.sendRequest(request);
std::this_thread::sleep_for(std::chrono::seconds(10));
session.stop();
std::cout << "Bye" << std::endl;
return EXIT_SUCCESS;
}
Output 1:
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.0000,
CUMULATIVE_FILLED_QUANTITY = 0.00000000,
INSTRUMENT = BTCUSD,
LIMIT_PRICE = 20000.0000,
ORDER_ID = 383781246,
QUANTITY = 0.00100000,
SIDE = BUY,
STATUS = NEW
}
]
],
correlationIdList = [ 5PN2qmWqBlQ9wQj99nsQzldVI5ZuGXbE ]
]
]
]
Bye
CREATE_ORDER
, CANCEL_ORDER
, GET_ORDER
, GET_OPEN_ORDERS
, CANCEL_OPEN_ORDERS
, GET_ACCOUNTS
, GET_ACCOUNT_BALANCES
, GET_ACCOUNT_POSITIONS
.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.Objective 2:
For a specific exchange and instrument, receive order updates.
Code 2:
C++ / Python / Java / C# / Go / Javascript
#include "ccapi_cpp/ccapi_session.h"
namespace ccapi {
Logger* Logger::logger = nullptr; // This line is needed.
class MyEventHandler : public EventHandler {
public:
bool processEvent(const Event& event, Session* session) 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);
if (message.getType() == Message::Type::SUBSCRIPTION_STARTED) {
Request request(Request::Operation::CREATE_ORDER, "coinbase", "BTC-USD");
request.appendParam({
{"SIDE", "BUY"},
{"LIMIT_PRICE", "20000"},
{"QUANTITY", "0.001"},
});
session->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;
}
return true;
}
};
} /* namespace ccapi */
using ::ccapi::MyEventHandler;
using ::ccapi::Request;
using ::ccapi::Session;
using ::ccapi::SessionConfigs;
using ::ccapi::SessionOptions;
using ::ccapi::Subscription;
using ::ccapi::toString;
using ::ccapi::UtilSystem;
int main(int argc, char** argv) {
if (UtilSystem::getEnvAsString("COINBASE_API_KEY").empty()) {
std::cerr << "Please set environment variable COINBASE_API_KEY" << std::endl;
return EXIT_FAILURE;
}
if (UtilSystem::getEnvAsString("COINBASE_API_SECRET").empty()) {
std::cerr << "Please set environment variable COINBASE_API_SECRET" << std::endl;
return EXIT_FAILURE;
}
if (UtilSystem::getEnvAsString("COINBASE_API_PASSPHRASE").empty()) {
std::cerr << "Please set environment variable COINBASE_API_PASSPHRASE" << std::endl;
return EXIT_FAILURE;
}
SessionOptions sessionOptions;
SessionConfigs sessionConfigs;
MyEventHandler eventHandler;
Session session(sessionOptions, sessionConfigs, &eventHandler);
Subscription subscription("coinbase", "BTC-USD", "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:
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-USD,
LIMIT_PRICE = 20000,
ORDER_ID = 6ca39186-be79-4777-97ab-1695fccd0ce4,
QUANTITY = 0.001,
SIDE = BUY,
STATUS = received
}
]
],
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.407704000Z,
elementList = [
Element [
nameValueMap = {
INSTRUMENT = BTC-USD,
LIMIT_PRICE = 20000,
ORDER_ID = 6ca39186-be79-4777-97ab-1695fccd0ce4,
REMAINING_QUANTITY = 0.001,
SIDE = BUY,
STATUS = open
}
]
],
correlationIdList = [ 5PN2qmWqBlQ9wQj99nsQzldVI5ZuGXbE ]
]
]
]
Bye
ORDER_UPDATE
, PRIVATE_TRADE
, BALANCE_UPDATE
, POSITION_UPDATE
.Instantiate Request
with the desired correlationId. The correlationId
should be unique.
Request request(Request::Operation::CREATE_ORDER, "binance-us", "BTCUSD", "cool correlation id");
Instantiate Subscription
with the desired correlationId.
Subscription subscription("coinbase", "BTC-USD", "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.
Send a std::vector<Request>
.
Request request_1(Request::Operation::CREATE_ORDER, "binance-us", "BTCUSD", "cool correlation id for BTC");
request_1.appendParam(...);
Request request_2(Request::Operation::CREATE_ORDER, "binance-us", "ETHUSD", "cool correlation id for ETH");
request_2.appendParam(...);
session.sendRequest({request_1, request_2});
Subscribe one Subscription
per exchange with a comma separated string of instruments.
Subscription subscription("coinbase", "BTC-USD,ETH-USD", "ORDER_UPDATE");
Subscribe one Subscription
with a comma separated string of fields.
Subscription subscription("coinbase", "BTC-USD", "ORDER_UPDATE,PRIVATE_TRADE");
Instantiate Session
without EventHandler
argument, and pass a pointer to Queue<Event>
as an additional argument.
Session session(sessionOptions, sessionConfigs);
...
Queue<Event> eventQueue;
session.sendRequest(request, &eventQueue); // block until a response is received
std::vector<Event> eventList = eventQueue.purge();
There are 3 ways to provide API credentials (listed with increasing priority).
COINBASE_API_PASSPHRASE
, KUCOIN_API_PASSPHRASE
. See section "exchange API credentials" in include/ccapi_cpp/ccapi_macro.h
.SessionConfigs
.
sessionConfigs.setCredential({
{"BINANCE_US_API_KEY", ...},
{"BINANCE_US_API_SECRET", ...}
});
Request
or Subscription
.
Request request(Request::Operation::CREATE_ORDER, "binance-us", "BTCUSD", "", {
{"BINANCE_US_API_KEY", ...},
{"BINANCE_US_API_SECRET", ...}
});
Subscription subscription("coinbase", "BTC-USD", "ORDER_UPDATE", "", "", {
{"COINBASE_API_KEY", ...},
{"COINBASE_API_SECRET", ...}
});
See section "exchange REST urls", "exchange WS urls", and "exchange FIX urls" in include/ccapi_cpp/ccapi_macro.h
. This can be useful if you need to connect to test accounts (e.g. https://docs.pro.coinbase.com/#sandbox).
Please follow the exchange's API documentations: e.g. https://github.com/binance-us/binance-official-api-docs/blob/master/rest-api.md#new-order--trade.
Request request(Request::Operation::CREATE_ORDER, "binance-us", "BTCUSD");
request.appendParam({
{"side", "SELL"},
{"type", "STOP_LOSS_LIMIT"},
{"quantity", "0.0005"},
{"stopPrice", "20001"},
{"price", "20000"},
{"timeInForce", "GTC"}
});
Subscription subscription("okx", "BTC-USDT", "ORDER_UPDATE", "", "same correlation id for subscription and request");
session.subscribe(subscription);
...
Request request(Request::Operation::CREATE_ORDER, "okx", "BTC-USDT", "same correlation id for subscription and request");
request.appendParam({
{"SIDE", "BUY"},
{"LIMIT_PRICE", "20000"},
{"QUANTITY", "0.001"},
});
session.sendRequestByWebsocket(request);
Some exchanges (i.e. bybit) might need instrument type for Subscription
. Use Subscription
's setInstrumentType
method.
Subscription subscription("bybit", "BTCUSDT", "MARKET_DEPTH");
subscription.setInstrumentType("spot");
session.subscribe(subscription);
Objective:
For a specific exchange and instrument, submit a simple limit order.
Code:
C++ / Python / Java / C# / Go / Javascript
#include "ccapi_cpp/ccapi_session.h"
namespace ccapi {
Logger* Logger::logger = nullptr; // This line is needed.
class MyEventHandler : public EventHandler {
public:
bool processEvent(const Event& event, Session* session) 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);
if (message.getType() == Message::Type::AUTHORIZATION_SUCCESS) {
Request request(Request::Operation::FIX, "coinbase", "", "same correlation id for subscription and request");
request.appendParamFix({
{35, "D"},
{11, "6d4eb0fb-2229-469f-873e-557dd78ac11e"},
{55, "BTC-USD"},
{54, "1"},
{44, "20000"},
{38, "0.001"},
{40, "2"},
{59, "1"},
});
session->sendRequestByFix(request);
}
} else if (event.getType() == Event::Type::FIX) {
std::cout << "Received an event of type FIX:\n" + event.toStringPretty(2, 2) << std::endl;
}
return true;
}
};
} /* 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("COINBASE_API_KEY").empty()) {
std::cerr << "Please set environment variable COINBASE_API_KEY" << std::endl;
return EXIT_FAILURE;
}
if (UtilSystem::getEnvAsString("COINBASE_API_SECRET").empty()) {
std::cerr << "Please set environment variable COINBASE_API_SECRET" << std::endl;
return EXIT_FAILURE;
}
if (UtilSystem::getEnvAsString("COINBASE_API_PASSPHRASE").empty()) {
std::cerr << "Please set environment variable COINBASE_API_PASSPHRASE" << std::endl;
return EXIT_FAILURE;
}
SessionOptions sessionOptions;
SessionConfigs sessionConfigs;
MyEventHandler eventHandler;
Session session(sessionOptions, sessionConfigs, &eventHandler);
Subscription subscription("coinbase", "", "FIX", "", "same correlation id for subscription and request");
session.subscribeByFix(subscription);
std::this_thread::sleep_for(std::chrono::seconds(10));
session.stop();
std::cout << "Bye" << std::endl;
return EXIT_SUCCESS;
}
Output:
Received an event of type AUTHORIZATION_STATUS:
Event [
type = AUTHORIZATION_STATUS,
messageList = [
Message [
type = AUTHORIZATION_SUCCESS,
recapType = UNKNOWN,
time = 1970-01-01T00:00:00.000000000Z,
timeReceived = 2021-05-25T05:05:15.892366000Z,
elementList = [
Element [
tagValueMap = {
96 = 0srtt0WetUTYHiTpvyWnC+XKKHCzQQIJ/8G9lE4KVxM=,
98 = 0,
108 = 15,
554 = 26abh7of52i
}
]
],
correlationIdList = [ same correlation id for subscription and request ]
]
]
]
Received an event of type FIX:
Event [
type = FIX,
messageList = [
Message [
type = FIX,
recapType = UNKNOWN,
time = 1970-01-01T00:00:00.000000000Z,
timeReceived = 2021-05-25T05:05:15.984090000Z,
elementList = [
Element [
tagValueMap = {
11 = 6d4eb0fb-2229-469f-873e-557dd78ac11e,
17 = b7caec79-1bc8-460e-af28-6489cf12f45e,
20 = 0,
37 = 458acfe5-bdea-46d2-aa87-933cda84163f,
38 = 0.001,
39 = 0,
44 = 20000,
54 = 1,
55 = BTC-USD,
60 = 20210525-05:05:16.008,
150 = 0
}
]
],
correlationIdList = [ same correlation id for subscription and request ]
]
]
]
Bye
In general there are 2 ways to handle events.
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.Session
is instantiated without an eventHandler
argument, it will handle events in batching mode. The evetns will be batched into an internal Queue<Event>
and can be retrieved by
std::vector<Event> eventList = session.getEventQueue().purge();
An example can be found here.
Session::sendRequest
, Session::subscribe
, Session::sendRequestByFix
, Session::subscribeByFix
, Session::setTimer
, all public methods in Queue
.processEvent
method in the eventHandler
is invoked on one of the internal threads in the eventDispatcher
. A default EventDispatcher
with 1 internal thread will be created if no eventDispatcher
argument is provided in Session
instantiation. To dispatch events to multiple threads, instantiate EventDispatcher
with numDispatcherThreads
set to be the desired number. EventHandler
s and/or EventDispatcher
s can be shared among different sessions. Otherwise, different sessions are independent from each other.
EventDispatcher eventDispatcher(2);
Session session(sessionOptions, sessionConfigs, &eventHandler, &eventDispatcher);
An example can be found here.
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 {
...
}
};
MyLogger myLogger;
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(
"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; });
Define macro CCAPI_EXPOSE_INTERNAL
. Extend a subclass e.g. MyService
from class Service
. Inject a MyService
pointer into Session
. E.g.
session.serviceByServiceNameExchangeMap[CCAPI_EXECUTION_MANAGEMENT][CCAPI_EXCHANGE_NAME_COINBASE] =
std::make_shared<ExecutionManagementServiceCoinbaseCustom>(session.internalEventHandler, session.sessionOptions, session.sessionConfigs,
session.serviceContextPtr);
cmake -DCMAKE_BUILD_TYPE=Release ...
).set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
before a target is created). Note that link time optimization is only applicable to static linking.Element
(e.g. in CmakeLists.txt add_compile_definitions(CCAPI_BEST_BID_N_PRICE="b")
).CCAPI_USE_SINGLE_THREAD
. It reduces locking overhead for single threaded applications.app/src/spot_market_making/config.env.example
for more details. And read more at https://medium.com/open-crypto-market-data-initiative/simplified-avellaneda-stoikov-market-making-608b9d437403.mkdir app/build
cd app/build
rm -rf * (if rebuild from scratch)
cmake ..
cmake --build . --target spot_market_making
app/build/src/spot_market_making/spot_market_making
. Run it after setting relevant environment variables shown in app/src/spot_market_making/config.env.example
. For example, we can copy file config.env.example
to config.env
, edit it, and export $(grep -v '^#' config.env | xargs)
. To enable and configure advanced parameters, set additional environment variables shown in app/src/spot_market_making/config_advanced.env.example
.app/src/spot_market_making/config.env.example
for more details.app/src/single_order_execution/config.env.example
.mkdir app/build
cd app/build
rm -rf * (if rebuild from scratch)
cmake ..
cmake --build . --target single_order_execution
app/build/src/single_order_execution/single_order_execution
. Run it after setting relevant environment variables shown in app/src/single_order_execution/config.env.example
. For example, we can copy file config.env.example
to config.env
, edit it, and export $(grep -v '^#' config.env | xargs)
. To enable and configure advanced parameters, set additional environment variables shown in app/src/single_order_execution/config_advanced.env.example
.app/src/single_order_execution/config.env.example
for more details.develop
branch and submit a pull request to the develop
branch.