Skip to content

Commit

Permalink
Merge pull request #362 from crypto-chassis/develop
Browse files Browse the repository at this point in the history
Release
cryptochassis authored Jan 13, 2023

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
2 parents 0423817 + 1f8c543 commit dd59edf
Showing 16 changed files with 498 additions and 83 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@
* Code closely follows Bloomberg's API: https://www.bloomberg.com/professional/support/api-library/.
* It is ultra fast thanks to very careful optimizations: move semantics, regex optimization, locality of reference, lock contention minimization, etc.
* Supported exchanges:
* Market data: coinbase, gemini, kraken, kraken-futures, bitstamp, bitfinex, bitmex, binance-us, binance, binance-usds-futures, binance-coin-futures, huobi, huobi-usdt-swap, huobi-coin-swap, okx, erisx, kucoin, kucoin-futures, deribit, gateio, gateio-perpetual-futures, cryptocom, bybit, bybit-derivatives, ascendex, bitget, bitget-futures, bitmart, mexc, mexc-futures.
* Market data: coinbase, gemini, kraken, kraken-futures, bitstamp, bitfinex, bitmex, binance-us, binance, binance-usds-futures, binance-coin-futures, huobi, huobi-usdt-swap, huobi-coin-swap, okx, erisx, kucoin, kucoin-futures, deribit, gateio, gateio-perpetual-futures, cryptocom, bybit, bybit-derivatives, ascendex, bitget, bitget-futures, bitmart, mexc, mexc-futures, whitebit.
* Execution Management: coinbase, gemini, kraken, kraken-futures, bitstamp, bitfinex, bitmex, binance-us, binance, binance-usds-futures, binance-coin-futures, huobi, huobi-usdt-swap, huobi-coin-swap, okx, erisx, kucoin, kucoin-futures, deribit, gateio, gateio-perpetual-futures, cryptocom, bybit, bybit-derivatives, ascendex, bitget, bitget-futures, bitmart, mexc.
* FIX: coinbase, gemini.
* A spot market making application is provided as an end-to-end solution for liquidity providers.
2 changes: 2 additions & 0 deletions binding/user_specified_cmake_include.cmake.example
Original file line number Diff line number Diff line change
@@ -61,6 +61,8 @@ include_guard(DIRECTORY)
#
# add_compile_definitions(CCAPI_ENABLE_EXCHANGE_MEXC_FUTURES)
#
# add_compile_definitions(CCAPI_ENABLE_EXCHANGE_WHITEBIT)
#
# add_compile_definitions(CCAPI_ENABLE_LOG_TRACE)
#
# add_compile_definitions(CCAPI_ENABLE_LOG_DEBUG)
14 changes: 14 additions & 0 deletions include/ccapi_cpp/ccapi_macro.h
Original file line number Diff line number Diff line change
@@ -148,6 +148,9 @@
#ifndef CCAPI_EXCHANGE_NAME_MEXC_FUTURES
#define CCAPI_EXCHANGE_NAME_MEXC_FUTURES "mexc-futures"
#endif
#ifndef CCAPI_EXCHANGE_NAME_WHITEBIT
#define CCAPI_EXCHANGE_NAME_WHITEBIT "whitebit"
#endif
#ifndef CCAPI_LAST_PRICE
#define CCAPI_LAST_PRICE "LAST_PRICE"
#endif
@@ -325,6 +328,8 @@
#define CCAPI_WEBSOCKET_MEXC_CHANNEL_DIFF_DEPTH "[email protected]"
#define CCAPI_WEBSOCKET_MEXC_FUTURES_CHANNEL_TRANSACTION "deal"
#define CCAPI_WEBSOCKET_MEXC_FUTURES_CHANNEL_DEPTH "depth"
#define CCAPI_WEBSOCKET_WHITEBIT_CHANNEL_MARKET_TRADES "trades"
#define CCAPI_WEBSOCKET_WHITEBIT_CHANNEL_MARKET_DEPTH "depth"
#ifndef CCAPI_CHANNEL_ID
#define CCAPI_CHANNEL_ID "channelId"
#endif
@@ -465,6 +470,9 @@
#ifndef CCAPI_EM_ORDER_CUMULATIVE_FILLED_PRICE_TIMES_QUANTITY
#define CCAPI_EM_ORDER_CUMULATIVE_FILLED_PRICE_TIMES_QUANTITY "CUMULATIVE_FILLED_PRICE_TIMES_QUANTITY"
#endif
#ifndef CCAPI_EM_ORDER_AVERAGE_FILLED_PRICE
#define CCAPI_EM_ORDER_AVERAGE_FILLED_PRICE "AVERAGE_FILLED_PRICE"
#endif
#ifndef CCAPI_EM_ORDER_INSTRUMENT
#define CCAPI_EM_ORDER_INSTRUMENT "INSTRUMENT"
#endif
@@ -714,6 +722,9 @@
#ifndef CCAPI_MEXC_FUTURES_URL_REST_BASE
#define CCAPI_MEXC_FUTURES_URL_REST_BASE "https://contract.mexc.com"
#endif
#ifndef CCAPI_WHITEBIT_URL_REST_BASE
#define CCAPI_WHITEBIT_URL_REST_BASE "https://whitebit.com"
#endif
// end: exchange REST urls

// start: exchange WS urls
@@ -831,6 +842,9 @@
#ifndef CCAPI_MEXC_FUTURES_URL_WS_BASE
#define CCAPI_MEXC_FUTURES_URL_WS_BASE "wss://contract.mexc.com"
#endif
#ifndef CCAPI_WHITEBIT_URL_WS_BASE
#define CCAPI_WHITEBIT_URL_WS_BASE "wss://api.whitebit.com"
#endif
// end: exchange WS urls

// start: exchange FIX urls
4 changes: 2 additions & 2 deletions include/ccapi_cpp/ccapi_message.h
Original file line number Diff line number Diff line change
@@ -16,8 +16,8 @@ class Message CCAPI_FINAL {
public:
enum class RecapType {
UNKNOWN,
NONE, // normal data tick; not a recap
SOLICITED, // generated on request by subscriber
NONE, // Normal data tick, not a recap. For market depth, it represents the new snapshot. For public trade, it represents the new trades.
SOLICITED, // A recap. For market depth, it represents the initial snapshot. For public trade, it represents the recent trades.
};
static std::string recapTypeToString(RecapType recapType) {
std::string output;
15 changes: 15 additions & 0 deletions include/ccapi_cpp/ccapi_session.h
Original file line number Diff line number Diff line change
@@ -100,6 +100,9 @@
#ifdef CCAPI_ENABLE_EXCHANGE_MEXC_FUTURES
#include "ccapi_cpp/service/ccapi_market_data_service_mexc_futures.h"
#endif
#ifdef CCAPI_ENABLE_EXCHANGE_WHITEBIT
#include "ccapi_cpp/service/ccapi_market_data_service_whitebit.h"
#endif
#endif
// end: enable exchanges for market data

@@ -204,6 +207,9 @@
// #ifdef CCAPI_ENABLE_EXCHANGE_MEXC_FUTURES
// #include "ccapi_cpp/service/ccapi_execution_management_service_mexc_futures.h"
// #endif
#ifdef CCAPI_ENABLE_EXCHANGE_WHITEBIT
#include "ccapi_cpp/service/ccapi_execution_management_service_whitebit.h"
#endif
#endif
// end: enable exchanges for execution management

@@ -421,6 +427,10 @@ class Session {
this->serviceByServiceNameExchangeMap[CCAPI_MARKET_DATA][CCAPI_EXCHANGE_NAME_MEXC_FUTURES] =
std::make_shared<MarketDataServiceMexcFutures>(this->internalEventHandler, sessionOptions, sessionConfigs, this->serviceContextPtr);
#endif
#ifdef CCAPI_ENABLE_EXCHANGE_WHITEBIT
this->serviceByServiceNameExchangeMap[CCAPI_MARKET_DATA][CCAPI_EXCHANGE_NAME_WHITEBIT] =
std::make_shared<MarketDataServiceWhitebit>(this->internalEventHandler, sessionOptions, sessionConfigs, this->serviceContextPtr);
#endif
#endif
#ifdef CCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT
#ifdef CCAPI_ENABLE_EXCHANGE_COINBASE
@@ -555,7 +565,12 @@ class Session {
// this->serviceByServiceNameExchangeMap[CCAPI_EXECUTION_MANAGEMENT][CCAPI_EXCHANGE_NAME_MEXC_FUTURES] =
// std::make_shared<ExecutionManagementServiceMexcFutures>(this->internalEventHandler, sessionOptions, sessionConfigs, this->serviceContextPtr);
// #endif
// #ifdef CCAPI_ENABLE_EXCHANGE_WHITEBIT
// this->serviceByServiceNameExchangeMap[CCAPI_EXECUTION_MANAGEMENT][CCAPI_EXCHANGE_NAME_WHITEBIT] =
// std::make_shared<ExecutionManagementServiceWhitebit>(this->internalEventHandler, sessionOptions, sessionConfigs, this->serviceContextPtr);
// #endif
#endif

#ifdef CCAPI_ENABLE_SERVICE_FIX
#ifdef CCAPI_ENABLE_EXCHANGE_COINBASE
this->serviceByServiceNameExchangeMap[CCAPI_FIX][CCAPI_EXCHANGE_NAME_COINBASE] =
12 changes: 11 additions & 1 deletion include/ccapi_cpp/ccapi_session_configs.h
Original file line number Diff line number Diff line change
@@ -163,6 +163,10 @@ class SessionConfigs CCAPI_FINAL {
{CCAPI_TRADE, CCAPI_WEBSOCKET_MEXC_FUTURES_CHANNEL_TRANSACTION},
{CCAPI_MARKET_DEPTH, CCAPI_WEBSOCKET_MEXC_FUTURES_CHANNEL_DEPTH},
};
std::map<std::string, std::string> fieldWebsocketChannelMapWhitebit = {
{CCAPI_TRADE, CCAPI_WEBSOCKET_WHITEBIT_CHANNEL_MARKET_TRADES},
{CCAPI_MARKET_DEPTH, CCAPI_WEBSOCKET_WHITEBIT_CHANNEL_MARKET_DEPTH},
};
for (auto const& fieldWebsocketChannel : fieldWebsocketChannelMapCoinbase) {
this->exchangeFieldMap[CCAPI_EXCHANGE_NAME_COINBASE].push_back(fieldWebsocketChannel.first);
}
@@ -259,6 +263,9 @@ class SessionConfigs CCAPI_FINAL {
for (auto const& fieldWebsocketChannel : fieldWebsocketChannelMapMexcFutures) {
this->exchangeFieldMap[CCAPI_EXCHANGE_NAME_MEXC_FUTURES].push_back(fieldWebsocketChannel.first);
}
for (auto const& fieldWebsocketChannel : fieldWebsocketChannelMapWhitebit) {
this->exchangeFieldMap[CCAPI_EXCHANGE_NAME_WHITEBIT].push_back(fieldWebsocketChannel.first);
}
for (auto& x : this->exchangeFieldMap) {
x.second.push_back(CCAPI_GENERIC_PUBLIC_SUBSCRIPTION);
}
@@ -296,6 +303,7 @@ class SessionConfigs CCAPI_FINAL {
{CCAPI_EXCHANGE_NAME_BITMART, fieldWebsocketChannelMapBitmart},
{CCAPI_EXCHANGE_NAME_MEXC, fieldWebsocketChannelMapMexc},
{CCAPI_EXCHANGE_NAME_MEXC_FUTURES, fieldWebsocketChannelMapMexcFutures},
{CCAPI_EXCHANGE_NAME_WHITEBIT, fieldWebsocketChannelMapWhitebit},
};
this->urlWebsocketBase = {
{CCAPI_EXCHANGE_NAME_COINBASE, CCAPI_COINBASE_URL_WS_BASE},
@@ -334,6 +342,7 @@ class SessionConfigs CCAPI_FINAL {
{CCAPI_EXCHANGE_NAME_BITMART, CCAPI_BITMART_URL_WS_BASE},
{CCAPI_EXCHANGE_NAME_MEXC, CCAPI_MEXC_URL_WS_BASE},
{CCAPI_EXCHANGE_NAME_MEXC_FUTURES, CCAPI_MEXC_FUTURES_URL_WS_BASE},
{CCAPI_EXCHANGE_NAME_WHITEBIT, CCAPI_WHITEBIT_URL_WS_BASE},
};
this->initialSequenceByExchangeMap = {{CCAPI_EXCHANGE_NAME_GEMINI, 0}, {CCAPI_EXCHANGE_NAME_BITFINEX, 1}};
}
@@ -370,9 +379,10 @@ class SessionConfigs CCAPI_FINAL {
{CCAPI_EXCHANGE_NAME_ASCENDEX, CCAPI_ASCENDEX_URL_REST_BASE},
{CCAPI_EXCHANGE_NAME_BITGET, CCAPI_BITGET_URL_REST_BASE},
{CCAPI_EXCHANGE_NAME_BITGET_FUTURES, CCAPI_BITGET_FUTURES_URL_REST_BASE},
{CCAPI_EXCHANGE_NAME_BITMART, CCAPI_BITMART_URL_REST_BASE},
{CCAPI_EXCHANGE_NAME_MEXC, CCAPI_MEXC_URL_REST_BASE},
{CCAPI_EXCHANGE_NAME_MEXC_FUTURES, CCAPI_MEXC_FUTURES_URL_REST_BASE},
{CCAPI_EXCHANGE_NAME_BITMART, CCAPI_BITMART_URL_REST_BASE},
{CCAPI_EXCHANGE_NAME_WHITEBIT, CCAPI_WHITEBIT_URL_REST_BASE},
};
}
void initializUrlFixBase() {
Original file line number Diff line number Diff line change
@@ -280,62 +280,64 @@ class ExecutionManagementServiceKrakenFutures : public ExecutionManagementServic
if (document.FindMember("event") == document.MemberEnd()) {
if (document.FindMember("feed") != document.MemberEnd()) {
std::string feed = document["feed"].GetString();
std::string field;
if (feed == "fills") {
field = CCAPI_EM_PRIVATE_TRADE;
} else if (feed == "open_orders") {
field = CCAPI_EM_ORDER_UPDATE;
}
const auto& fieldSet = subscription.getFieldSet();
if (fieldSet.find(field) != fieldSet.end()) {
const auto& instrumentSet = subscription.getInstrumentSet();
if (field == CCAPI_EM_PRIVATE_TRADE) {
for (const auto& x : document["fills"].GetArray()) {
if (feed == "fills" || feed == "open_orders") {
std::string field;
if (feed == "fills") {
field = CCAPI_EM_PRIVATE_TRADE;
} else if (feed == "open_orders") {
field = CCAPI_EM_ORDER_UPDATE;
}
const auto& fieldSet = subscription.getFieldSet();
if (fieldSet.find(field) != fieldSet.end()) {
const auto& instrumentSet = subscription.getInstrumentSet();
if (field == CCAPI_EM_PRIVATE_TRADE) {
for (const auto& x : document["fills"].GetArray()) {
std::string instrument = x["instrument"].GetString();
if (instrumentSet.empty() || instrumentSet.find(instrument) != instrumentSet.end()) {
Message message;
message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_PRIVATE_TRADE);
message.setTime(UtilTime::makeTimePointFromMilliseconds(std::stoll(x["time"].GetString())));
message.setTimeReceived(timeReceived);
message.setCorrelationIdList({subscription.getCorrelationId()});
std::vector<Element> elementList;
Element element;
element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_PRICE, x["price"].GetString());
element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_SIZE, x["qty"].GetString());
element.insert(CCAPI_EM_ORDER_SIDE, x["buy"].GetBool() ? CCAPI_EM_ORDER_SIDE_BUY : CCAPI_EM_ORDER_SIDE_SELL);
element.insert(CCAPI_EM_ORDER_ID, std::string(x["order_id"].GetString()));
element.insert(CCAPI_EM_CLIENT_ORDER_ID, std::string(x["cli_ord_id"].GetString()));
element.insert(CCAPI_IS_MAKER, std::string(x["fill_type"].GetString()) == "maker" ? "1" : "0");
element.insert(CCAPI_EM_ORDER_INSTRUMENT, instrument);
element.insert(CCAPI_EM_ORDER_FEE_QUANTITY, std::string(x["fee_paid"].GetString()));
element.insert(CCAPI_EM_ORDER_FEE_ASSET, std::string(x["fee_currency"].GetString()));
elementList.emplace_back(std::move(element));
message.setElementList(elementList);
messageList.emplace_back(std::move(message));
}
}
} else if (field == CCAPI_EM_ORDER_UPDATE) {
const rj::Value& x = document["order"];
std::string instrument = x["instrument"].GetString();
if (instrumentSet.empty() || instrumentSet.find(instrument) != instrumentSet.end()) {
Message message;
message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_PRIVATE_TRADE);
message.setTime(UtilTime::makeTimePointFromMilliseconds(std::stoll(x["time"].GetString())));
message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_ORDER_UPDATE);
message.setTime(UtilTime::makeTimePointFromMilliseconds(std::stoll(x["last_update_time"].GetString())));
message.setTimeReceived(timeReceived);
message.setCorrelationIdList({subscription.getCorrelationId()});
std::vector<Element> elementList;
Element element;
element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_PRICE, x["price"].GetString());
element.insert(CCAPI_EM_ORDER_LAST_EXECUTED_SIZE, x["qty"].GetString());
element.insert(CCAPI_EM_ORDER_SIDE, x["buy"].GetBool() ? CCAPI_EM_ORDER_SIDE_BUY : CCAPI_EM_ORDER_SIDE_SELL);
element.insert(CCAPI_EM_ORDER_ID, std::string(x["order_id"].GetString()));
element.insert(CCAPI_EM_CLIENT_ORDER_ID, std::string(x["cli_ord_id"].GetString()));
element.insert(CCAPI_IS_MAKER, std::string(x["fill_type"].GetString()) == "maker" ? "1" : "0");
element.insert(CCAPI_EM_ORDER_INSTRUMENT, instrument);
element.insert(CCAPI_EM_ORDER_FEE_QUANTITY, std::string(x["fee_paid"].GetString()));
element.insert(CCAPI_EM_ORDER_FEE_ASSET, std::string(x["fee_currency"].GetString()));
element.insert(CCAPI_EM_ORDER_QUANTITY, x["qty"].GetString());
element.insert(CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, x["filled"].GetString());
element.insert(CCAPI_EM_ORDER_LIMIT_PRICE, x["limit_price"].GetString());
element.insert(CCAPI_EM_ORDER_ID, x["order_id"].GetString());
element.insert(CCAPI_EM_ORDER_SIDE, std::string(x["direction"].GetString()) == "0" ? CCAPI_EM_ORDER_SIDE_BUY : CCAPI_EM_ORDER_SIDE_SELL);
element.insert("is_cancel", document["is_cancel"].GetBool() ? "1" : "0");
elementList.emplace_back(std::move(element));
message.setElementList(elementList);
messageList.emplace_back(std::move(message));
}
}
} else if (field == CCAPI_EM_ORDER_UPDATE) {
const rj::Value& x = document["order"];
std::string instrument = x["instrument"].GetString();
if (instrumentSet.empty() || instrumentSet.find(instrument) != instrumentSet.end()) {
Message message;
message.setType(Message::Type::EXECUTION_MANAGEMENT_EVENTS_ORDER_UPDATE);
message.setTime(UtilTime::makeTimePointFromMilliseconds(std::stoll(x["last_update_time"].GetString())));
message.setTimeReceived(timeReceived);
message.setCorrelationIdList({subscription.getCorrelationId()});
std::vector<Element> elementList;
Element element;
element.insert(CCAPI_EM_ORDER_INSTRUMENT, instrument);
element.insert(CCAPI_EM_ORDER_QUANTITY, x["qty"].GetString());
element.insert(CCAPI_EM_ORDER_CUMULATIVE_FILLED_QUANTITY, x["filled"].GetString());
element.insert(CCAPI_EM_ORDER_LIMIT_PRICE, x["limit_price"].GetString());
element.insert(CCAPI_EM_ORDER_ID, x["order_id"].GetString());
element.insert(CCAPI_EM_ORDER_SIDE, std::string(x["direction"].GetString()) == "0" ? CCAPI_EM_ORDER_SIDE_BUY : CCAPI_EM_ORDER_SIDE_SELL);
element.insert("is_cancel", document["is_cancel"].GetBool() ? "1" : "0");
elementList.emplace_back(std::move(element));
message.setElementList(elementList);
messageList.emplace_back(std::move(message));
}
}
}
}
@@ -353,25 +355,39 @@ class ExecutionManagementServiceKrakenFutures : public ExecutionManagementServic
auto signature = UtilAlgorithm::base64Encode(Hmac::hmac(Hmac::ShaVersion::SHA512, UtilAlgorithm::base64Decode(apiSecret), challengeToSignSha256));
std::vector<std::string> sendStringList;
for (const auto& field : subscription.getFieldSet()) {
std::string feed;
if (field == CCAPI_EM_PRIVATE_TRADE) {
feed = "fills";
} else if (field == CCAPI_EM_ORDER_UPDATE) {
feed = "open_orders";
{
std::string feed;
if (field == CCAPI_EM_PRIVATE_TRADE) {
feed = "fills";
} else if (field == CCAPI_EM_ORDER_UPDATE) {
feed = "open_orders";
}
rj::Document document;
document.SetObject();
auto& allocator = document.GetAllocator();
document.AddMember("event", rj::Value("subscribe").Move(), allocator);
document.AddMember("feed", rj::Value(feed.c_str(), allocator).Move(), allocator);
document.AddMember("api_key", rj::Value(apiKey.c_str(), allocator).Move(), allocator);
document.AddMember("original_challenge", rj::Value(challengeToSign.c_str(), allocator).Move(), allocator);
document.AddMember("signed_challenge", rj::Value(signature.c_str(), allocator).Move(), allocator);
rj::StringBuffer stringBuffer;
rj::Writer<rj::StringBuffer> writer(stringBuffer);
document.Accept(writer);
std::string sendString = stringBuffer.GetString();
sendStringList.push_back(sendString);
}
{
rj::Document document;
document.SetObject();
auto& allocator = document.GetAllocator();
document.AddMember("event", rj::Value("subscribe").Move(), allocator);
document.AddMember("feed", rj::Value("heartbeat").Move(), allocator);
rj::StringBuffer stringBuffer;
rj::Writer<rj::StringBuffer> writer(stringBuffer);
document.Accept(writer);
std::string sendString = stringBuffer.GetString();
sendStringList.push_back(sendString);
}
rj::Document document;
document.SetObject();
auto& allocator = document.GetAllocator();
document.AddMember("event", rj::Value("subscribe").Move(), allocator);
document.AddMember("feed", rj::Value(feed.c_str(), allocator).Move(), allocator);
document.AddMember("api_key", rj::Value(apiKey.c_str(), allocator).Move(), allocator);
document.AddMember("original_challenge", rj::Value(challengeToSign.c_str(), allocator).Move(), allocator);
document.AddMember("signed_challenge", rj::Value(signature.c_str(), allocator).Move(), allocator);
rj::StringBuffer stringBuffer;
rj::Writer<rj::StringBuffer> writer(stringBuffer);
document.Accept(writer);
std::string sendString = stringBuffer.GetString();
sendStringList.push_back(sendString);
}
for (const auto& sendString : sendStringList) {
ErrorCode ec;
Loading

0 comments on commit dd59edf

Please sign in to comment.