Skip to content

Commit

Permalink
feat: Update WebSocket to v5 for Bybit spot market service
Browse files Browse the repository at this point in the history
  • Loading branch information
obergaba committed May 20, 2024
1 parent 491f0cb commit 7fda872
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 59 deletions.
7 changes: 3 additions & 4 deletions include/ccapi_cpp/ccapi_macro.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@
#define CCAPI_OPEN_PRICE "OPEN_PRICE"
#endif
#ifndef CCAPI_HIGH_PRICE
#define CCAPI_HIGH_PRICE "HIG_PRICEH"
#define CCAPI_HIGH_PRICE "HIGH_PRICE"
#endif
#ifndef CCAPI_LOW_PRICE
#define CCAPI_LOW_PRICE "LOW_PRICE"
Expand Down Expand Up @@ -354,9 +354,8 @@
#define CCAPI_WEBSOCKET_GATEIO_PERPETUAL_FUTURES_CHANNEL_CANDLESTICKS "futures.candlesticks"
#define CCAPI_WEBSOCKET_CRYPTOCOM_CHANNEL_TRADE "trade.{instrument_name}"
#define CCAPI_WEBSOCKET_CRYPTOCOM_CHANNEL_BOOK "book.{instrument_name}.{depth}"
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_TRADE "trade.{symbol}"
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_BOOK_TICKER "bookticker.{symbol}"
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH "orderbook.40.{symbol}"
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_TRADE "publicTrade.{symbol}"
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH "orderbook.{depth}.{symbol}"
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_KLINE "kline.{interval}.{symbol}"
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_KLINE_2 "kline"
#define CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_TRADE "publicTrade.{symbol}"
Expand Down
89 changes: 34 additions & 55 deletions include/ccapi_cpp/service/ccapi_market_data_service_bybit.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase {
ServiceContext* serviceContextPtr)
: MarketDataServiceBybitBase(eventHandler, sessionOptions, sessionConfigs, serviceContextPtr) {
this->exchangeName = CCAPI_EXCHANGE_NAME_BYBIT;
this->baseUrlWs = sessionConfigs.getUrlWebsocketBase().at(this->exchangeName) + "/spot/public/v3";
this->baseUrlWs = sessionConfigs.getUrlWebsocketBase().at(this->exchangeName) + "/v5/public/spot";
this->baseUrlRest = sessionConfigs.getUrlRestBase().at(this->exchangeName);
this->setHostRestFromUrlRest(this->baseUrlRest);
this->setHostWsFromUrlWs(this->baseUrlWs);
Expand Down Expand Up @@ -54,11 +54,13 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase {
const Subscription& subscription, const std::map<std::string, std::string> optionMap) override {
auto marketDepthRequested = std::stoi(optionMap.at(CCAPI_MARKET_DEPTH_MAX));
if (field == CCAPI_MARKET_DEPTH) {
if (marketDepthRequested == 1) {
channelId = CCAPI_WEBSOCKET_BYBIT_CHANNEL_BOOK_TICKER;
} else {
channelId = CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH;
}
std::vector<int> depths = {1, 50, 200};
int marketDepthSubscribedToExchange = 1;
marketDepthSubscribedToExchange = this->calculateMarketDepthAllowedByExchange(marketDepthRequested, depths);
channelId += std::string("?") + CCAPI_MARKET_DEPTH_SUBSCRIBED_TO_EXCHANGE + "=" + std::to_string(marketDepthSubscribedToExchange);
this->marketDepthSubscribedToExchangeByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId] = marketDepthSubscribedToExchange;

} else if (field == CCAPI_CANDLESTICK) {
int intervalSeconds = std::stoi(optionMap.at(CCAPI_CANDLESTICK_INTERVAL_SECONDS));
std::string interval = this->convertCandlestickIntervalSecondsToInterval(intervalSeconds);
Expand All @@ -79,8 +81,15 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase {
for (const auto& subscriptionListByInstrument : subscriptionListByChannelIdSymbolId.second) {
auto symbolId = subscriptionListByInstrument.first;
auto exchangeSubscriptionId = channelId;
if (channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_BOOK_TICKER || channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH) {
if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH, 0) == 0) {
int marketDepthSubscribedToExchange =
this->marketDepthSubscribedToExchangeByConnectionIdChannelIdSymbolIdMap.at(wsConnection.id).at(channelId).at(symbolId);
this->l2UpdateIsReplaceByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId] = true;
exchangeSubscriptionId = CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH;
std::string toReplace = "{depth}";
exchangeSubscriptionId.replace(exchangeSubscriptionId.find(toReplace), toReplace.length(), std::to_string(marketDepthSubscribedToExchange));
}
if (channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH) {
}
std::string toReplace = "{symbol}";
exchangeSubscriptionId.replace(exchangeSubscriptionId.find(toReplace), toReplace.length(), symbolId);
Expand Down Expand Up @@ -162,80 +171,50 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase {
std::string channelId = this->channelIdSymbolIdByConnectionIdExchangeSubscriptionIdMap[wsConnection.id][exchangeSubscriptionId][CCAPI_CHANNEL_ID];
std::string symbolId = this->channelIdSymbolIdByConnectionIdExchangeSubscriptionIdMap[wsConnection.id][exchangeSubscriptionId][CCAPI_SYMBOL_ID];
auto optionMap = this->optionMapByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId];
const rj::Value& data = document["data"];
marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(data["t"].GetString())));
marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(document["ts"].GetString())));
marketDataMessage.exchangeSubscriptionId = exchangeSubscriptionId;
if (channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_BOOK_TICKER) {
if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH, 0) == 0) {
const rj::Value& data = document["data"];
marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_MARKET_DEPTH;
marketDataMessage.recapType = this->processedInitialSnapshotByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId]
? MarketDataMessage::RecapType::NONE
: MarketDataMessage::RecapType::SOLICITED;
{
MarketDataMessage::TypeForDataPoint dataPoint;
dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(data["bp"].GetString())});
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(data["bq"].GetString())});
marketDataMessage.data[MarketDataMessage::DataType::BID].emplace_back(std::move(dataPoint));
}
{
MarketDataMessage::TypeForDataPoint dataPoint;
dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(data["ap"].GetString())});
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(data["aq"].GetString())});
marketDataMessage.data[MarketDataMessage::DataType::ASK].emplace_back(std::move(dataPoint));
}
marketDataMessageList.emplace_back(std::move(marketDataMessage));
} else if (channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH) {
marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_MARKET_DEPTH;
marketDataMessage.recapType = this->processedInitialSnapshotByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId]
? MarketDataMessage::RecapType::NONE
: MarketDataMessage::RecapType::SOLICITED;
const char* bidsName = "b";
int bidIndex = 0;
int maxMarketDepth = std::stoi(optionMap.at(CCAPI_MARKET_DEPTH_MAX));
for (const auto& x : data[bidsName].GetArray()) {
if (bidIndex >= maxMarketDepth) {
break;
}
std::string type = document["type"].GetString();
marketDataMessage.recapType = type == "snapshot" ? MarketDataMessage::RecapType::SOLICITED : MarketDataMessage::RecapType::NONE;
for (const auto& x : data["b"].GetArray()) {
MarketDataMessage::TypeForDataPoint dataPoint;
dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(x[0].GetString())});
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(x[1].GetString())});
marketDataMessage.data[MarketDataMessage::DataType::BID].emplace_back(std::move(dataPoint));
++bidIndex;
}
const char* asksName = "a";
int askIndex = 0;
for (const auto& x : data[asksName].GetArray()) {
if (askIndex >= maxMarketDepth) {
break;
}
for (const auto& x : data["a"].GetArray()) {
MarketDataMessage::TypeForDataPoint dataPoint;
dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(x[0].GetString())});
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(x[1].GetString())});
marketDataMessage.data[MarketDataMessage::DataType::ASK].emplace_back(std::move(dataPoint));
++askIndex;
}
marketDataMessageList.emplace_back(std::move(marketDataMessage));
} else if (channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_TRADE) {
const rj::Value& data = document["data"][0];
marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_TRADE;
marketDataMessage.recapType = MarketDataMessage::RecapType::NONE;
MarketDataMessage::TypeForDataPoint dataPoint;
dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(std::string(data["p"].GetString()))});
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(std::string(data["q"].GetString()))});
dataPoint.insert({MarketDataMessage::DataFieldType::TRADE_ID, std::string(data["v"].GetString())});
dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, data["m"].GetBool() ? "0" : "1"});
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(std::string(data["v"].GetString()))});
dataPoint.insert({MarketDataMessage::DataFieldType::TRADE_ID, std::string(data["i"].GetString())});
dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string(data["S"].GetString()) == "Buy" ? "1" : "0"});
marketDataMessage.data[MarketDataMessage::DataType::TRADE].emplace_back(std::move(dataPoint));
marketDataMessageList.emplace_back(std::move(marketDataMessage));
} else if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_CHANNEL_KLINE_2, 0) == 0) {
const rj::Value& data = document["data"][0];
MarketDataMessage marketDataMessage;
marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_CANDLESTICK;
marketDataMessage.recapType = MarketDataMessage::RecapType::NONE;
marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(data["t"].GetString())));
marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(document["ts"].GetString())));
marketDataMessage.exchangeSubscriptionId = exchangeSubscriptionId;
MarketDataMessage::TypeForDataPoint dataPoint;
dataPoint.insert({MarketDataMessage::DataFieldType::OPEN_PRICE, data["o"].GetString()});
dataPoint.insert({MarketDataMessage::DataFieldType::HIGH_PRICE, data["h"].GetString()});
dataPoint.insert({MarketDataMessage::DataFieldType::LOW_PRICE, data["l"].GetString()});
dataPoint.insert({MarketDataMessage::DataFieldType::CLOSE_PRICE, data["c"].GetString()});
dataPoint.insert({MarketDataMessage::DataFieldType::VOLUME, data["v"].GetString()});
dataPoint.insert({MarketDataMessage::DataFieldType::OPEN_PRICE, data["open"].GetString()});
dataPoint.insert({MarketDataMessage::DataFieldType::HIGH_PRICE, data["high"].GetString()});
dataPoint.insert({MarketDataMessage::DataFieldType::LOW_PRICE, data["low"].GetString()});
dataPoint.insert({MarketDataMessage::DataFieldType::CLOSE_PRICE, data["close"].GetString()});
dataPoint.insert({MarketDataMessage::DataFieldType::VOLUME, data["volume"].GetString()});
marketDataMessage.data[MarketDataMessage::DataType::CANDLESTICK].emplace_back(std::move(dataPoint));
marketDataMessageList.emplace_back(std::move(marketDataMessage));
}
Expand Down Expand Up @@ -343,7 +322,7 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase {
dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(std::string(x["price"].GetString()))});
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(std::string(x["size"].GetString()))});
dataPoint.insert({MarketDataMessage::DataFieldType::TRADE_ID, std::string(x["execId"].GetString())});
dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string(x["side"].GetString()) == "buy" ? "1" : "0"});
dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string(x["side"].GetString()) == "Buy" ? "1" : "0"});
marketDataMessage.data[MarketDataMessage::DataType::TRADE].emplace_back(std::move(dataPoint));
marketDataMessageList.emplace_back(std::move(marketDataMessage));
}
Expand Down

0 comments on commit 7fda872

Please sign in to comment.