Skip to content

Commit 7fda872

Browse files
committed
feat: Update WebSocket to v5 for Bybit spot market service
1 parent 491f0cb commit 7fda872

File tree

2 files changed

+37
-59
lines changed

2 files changed

+37
-59
lines changed

include/ccapi_cpp/ccapi_macro.h

+3-4
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@
212212
#define CCAPI_OPEN_PRICE "OPEN_PRICE"
213213
#endif
214214
#ifndef CCAPI_HIGH_PRICE
215-
#define CCAPI_HIGH_PRICE "HIG_PRICEH"
215+
#define CCAPI_HIGH_PRICE "HIGH_PRICE"
216216
#endif
217217
#ifndef CCAPI_LOW_PRICE
218218
#define CCAPI_LOW_PRICE "LOW_PRICE"
@@ -354,9 +354,8 @@
354354
#define CCAPI_WEBSOCKET_GATEIO_PERPETUAL_FUTURES_CHANNEL_CANDLESTICKS "futures.candlesticks"
355355
#define CCAPI_WEBSOCKET_CRYPTOCOM_CHANNEL_TRADE "trade.{instrument_name}"
356356
#define CCAPI_WEBSOCKET_CRYPTOCOM_CHANNEL_BOOK "book.{instrument_name}.{depth}"
357-
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_TRADE "trade.{symbol}"
358-
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_BOOK_TICKER "bookticker.{symbol}"
359-
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH "orderbook.40.{symbol}"
357+
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_TRADE "publicTrade.{symbol}"
358+
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH "orderbook.{depth}.{symbol}"
360359
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_KLINE "kline.{interval}.{symbol}"
361360
#define CCAPI_WEBSOCKET_BYBIT_CHANNEL_KLINE_2 "kline"
362361
#define CCAPI_WEBSOCKET_BYBIT_DERIVATIVES_CHANNEL_TRADE "publicTrade.{symbol}"

include/ccapi_cpp/service/ccapi_market_data_service_bybit.h

+34-55
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase {
1010
ServiceContext* serviceContextPtr)
1111
: MarketDataServiceBybitBase(eventHandler, sessionOptions, sessionConfigs, serviceContextPtr) {
1212
this->exchangeName = CCAPI_EXCHANGE_NAME_BYBIT;
13-
this->baseUrlWs = sessionConfigs.getUrlWebsocketBase().at(this->exchangeName) + "/spot/public/v3";
13+
this->baseUrlWs = sessionConfigs.getUrlWebsocketBase().at(this->exchangeName) + "/v5/public/spot";
1414
this->baseUrlRest = sessionConfigs.getUrlRestBase().at(this->exchangeName);
1515
this->setHostRestFromUrlRest(this->baseUrlRest);
1616
this->setHostWsFromUrlWs(this->baseUrlWs);
@@ -54,11 +54,13 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase {
5454
const Subscription& subscription, const std::map<std::string, std::string> optionMap) override {
5555
auto marketDepthRequested = std::stoi(optionMap.at(CCAPI_MARKET_DEPTH_MAX));
5656
if (field == CCAPI_MARKET_DEPTH) {
57-
if (marketDepthRequested == 1) {
58-
channelId = CCAPI_WEBSOCKET_BYBIT_CHANNEL_BOOK_TICKER;
59-
} else {
6057
channelId = CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH;
61-
}
58+
std::vector<int> depths = {1, 50, 200};
59+
int marketDepthSubscribedToExchange = 1;
60+
marketDepthSubscribedToExchange = this->calculateMarketDepthAllowedByExchange(marketDepthRequested, depths);
61+
channelId += std::string("?") + CCAPI_MARKET_DEPTH_SUBSCRIBED_TO_EXCHANGE + "=" + std::to_string(marketDepthSubscribedToExchange);
62+
this->marketDepthSubscribedToExchangeByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId] = marketDepthSubscribedToExchange;
63+
6264
} else if (field == CCAPI_CANDLESTICK) {
6365
int intervalSeconds = std::stoi(optionMap.at(CCAPI_CANDLESTICK_INTERVAL_SECONDS));
6466
std::string interval = this->convertCandlestickIntervalSecondsToInterval(intervalSeconds);
@@ -79,8 +81,15 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase {
7981
for (const auto& subscriptionListByInstrument : subscriptionListByChannelIdSymbolId.second) {
8082
auto symbolId = subscriptionListByInstrument.first;
8183
auto exchangeSubscriptionId = channelId;
82-
if (channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_BOOK_TICKER || channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH) {
84+
if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH, 0) == 0) {
85+
int marketDepthSubscribedToExchange =
86+
this->marketDepthSubscribedToExchangeByConnectionIdChannelIdSymbolIdMap.at(wsConnection.id).at(channelId).at(symbolId);
8387
this->l2UpdateIsReplaceByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId] = true;
88+
exchangeSubscriptionId = CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH;
89+
std::string toReplace = "{depth}";
90+
exchangeSubscriptionId.replace(exchangeSubscriptionId.find(toReplace), toReplace.length(), std::to_string(marketDepthSubscribedToExchange));
91+
}
92+
if (channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH) {
8493
}
8594
std::string toReplace = "{symbol}";
8695
exchangeSubscriptionId.replace(exchangeSubscriptionId.find(toReplace), toReplace.length(), symbolId);
@@ -162,80 +171,50 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase {
162171
std::string channelId = this->channelIdSymbolIdByConnectionIdExchangeSubscriptionIdMap[wsConnection.id][exchangeSubscriptionId][CCAPI_CHANNEL_ID];
163172
std::string symbolId = this->channelIdSymbolIdByConnectionIdExchangeSubscriptionIdMap[wsConnection.id][exchangeSubscriptionId][CCAPI_SYMBOL_ID];
164173
auto optionMap = this->optionMapByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId];
165-
const rj::Value& data = document["data"];
166-
marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(data["t"].GetString())));
174+
marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(document["ts"].GetString())));
167175
marketDataMessage.exchangeSubscriptionId = exchangeSubscriptionId;
168-
if (channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_BOOK_TICKER) {
176+
if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH, 0) == 0) {
177+
const rj::Value& data = document["data"];
169178
marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_MARKET_DEPTH;
170-
marketDataMessage.recapType = this->processedInitialSnapshotByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId]
171-
? MarketDataMessage::RecapType::NONE
172-
: MarketDataMessage::RecapType::SOLICITED;
173-
{
174-
MarketDataMessage::TypeForDataPoint dataPoint;
175-
dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(data["bp"].GetString())});
176-
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(data["bq"].GetString())});
177-
marketDataMessage.data[MarketDataMessage::DataType::BID].emplace_back(std::move(dataPoint));
178-
}
179-
{
180-
MarketDataMessage::TypeForDataPoint dataPoint;
181-
dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(data["ap"].GetString())});
182-
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(data["aq"].GetString())});
183-
marketDataMessage.data[MarketDataMessage::DataType::ASK].emplace_back(std::move(dataPoint));
184-
}
185-
marketDataMessageList.emplace_back(std::move(marketDataMessage));
186-
} else if (channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH) {
187-
marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_MARKET_DEPTH;
188-
marketDataMessage.recapType = this->processedInitialSnapshotByConnectionIdChannelIdSymbolIdMap[wsConnection.id][channelId][symbolId]
189-
? MarketDataMessage::RecapType::NONE
190-
: MarketDataMessage::RecapType::SOLICITED;
191-
const char* bidsName = "b";
192-
int bidIndex = 0;
193-
int maxMarketDepth = std::stoi(optionMap.at(CCAPI_MARKET_DEPTH_MAX));
194-
for (const auto& x : data[bidsName].GetArray()) {
195-
if (bidIndex >= maxMarketDepth) {
196-
break;
197-
}
179+
std::string type = document["type"].GetString();
180+
marketDataMessage.recapType = type == "snapshot" ? MarketDataMessage::RecapType::SOLICITED : MarketDataMessage::RecapType::NONE;
181+
for (const auto& x : data["b"].GetArray()) {
198182
MarketDataMessage::TypeForDataPoint dataPoint;
199183
dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(x[0].GetString())});
200184
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(x[1].GetString())});
201185
marketDataMessage.data[MarketDataMessage::DataType::BID].emplace_back(std::move(dataPoint));
202-
++bidIndex;
203186
}
204-
const char* asksName = "a";
205-
int askIndex = 0;
206-
for (const auto& x : data[asksName].GetArray()) {
207-
if (askIndex >= maxMarketDepth) {
208-
break;
209-
}
187+
for (const auto& x : data["a"].GetArray()) {
210188
MarketDataMessage::TypeForDataPoint dataPoint;
211189
dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(x[0].GetString())});
212190
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(x[1].GetString())});
213191
marketDataMessage.data[MarketDataMessage::DataType::ASK].emplace_back(std::move(dataPoint));
214-
++askIndex;
215192
}
216193
marketDataMessageList.emplace_back(std::move(marketDataMessage));
217194
} else if (channelId == CCAPI_WEBSOCKET_BYBIT_CHANNEL_TRADE) {
195+
const rj::Value& data = document["data"][0];
218196
marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_TRADE;
219197
marketDataMessage.recapType = MarketDataMessage::RecapType::NONE;
220198
MarketDataMessage::TypeForDataPoint dataPoint;
221199
dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(std::string(data["p"].GetString()))});
222-
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(std::string(data["q"].GetString()))});
223-
dataPoint.insert({MarketDataMessage::DataFieldType::TRADE_ID, std::string(data["v"].GetString())});
224-
dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, data["m"].GetBool() ? "0" : "1"});
200+
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(std::string(data["v"].GetString()))});
201+
dataPoint.insert({MarketDataMessage::DataFieldType::TRADE_ID, std::string(data["i"].GetString())});
202+
dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string(data["S"].GetString()) == "Buy" ? "1" : "0"});
225203
marketDataMessage.data[MarketDataMessage::DataType::TRADE].emplace_back(std::move(dataPoint));
226204
marketDataMessageList.emplace_back(std::move(marketDataMessage));
227205
} else if (channelId.rfind(CCAPI_WEBSOCKET_BYBIT_CHANNEL_KLINE_2, 0) == 0) {
206+
const rj::Value& data = document["data"][0];
228207
MarketDataMessage marketDataMessage;
229208
marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_CANDLESTICK;
230209
marketDataMessage.recapType = MarketDataMessage::RecapType::NONE;
231-
marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(data["t"].GetString())));
210+
marketDataMessage.tp = TimePoint(std::chrono::milliseconds(std::stoll(document["ts"].GetString())));
232211
marketDataMessage.exchangeSubscriptionId = exchangeSubscriptionId;
233212
MarketDataMessage::TypeForDataPoint dataPoint;
234-
dataPoint.insert({MarketDataMessage::DataFieldType::OPEN_PRICE, data["o"].GetString()});
235-
dataPoint.insert({MarketDataMessage::DataFieldType::HIGH_PRICE, data["h"].GetString()});
236-
dataPoint.insert({MarketDataMessage::DataFieldType::LOW_PRICE, data["l"].GetString()});
237-
dataPoint.insert({MarketDataMessage::DataFieldType::CLOSE_PRICE, data["c"].GetString()});
238-
dataPoint.insert({MarketDataMessage::DataFieldType::VOLUME, data["v"].GetString()});
213+
dataPoint.insert({MarketDataMessage::DataFieldType::OPEN_PRICE, data["open"].GetString()});
214+
dataPoint.insert({MarketDataMessage::DataFieldType::HIGH_PRICE, data["high"].GetString()});
215+
dataPoint.insert({MarketDataMessage::DataFieldType::LOW_PRICE, data["low"].GetString()});
216+
dataPoint.insert({MarketDataMessage::DataFieldType::CLOSE_PRICE, data["close"].GetString()});
217+
dataPoint.insert({MarketDataMessage::DataFieldType::VOLUME, data["volume"].GetString()});
239218
marketDataMessage.data[MarketDataMessage::DataType::CANDLESTICK].emplace_back(std::move(dataPoint));
240219
marketDataMessageList.emplace_back(std::move(marketDataMessage));
241220
}
@@ -343,7 +322,7 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase {
343322
dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(std::string(x["price"].GetString()))});
344323
dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(std::string(x["size"].GetString()))});
345324
dataPoint.insert({MarketDataMessage::DataFieldType::TRADE_ID, std::string(x["execId"].GetString())});
346-
dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string(x["side"].GetString()) == "buy" ? "1" : "0"});
325+
dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, std::string(x["side"].GetString()) == "Buy" ? "1" : "0"});
347326
marketDataMessage.data[MarketDataMessage::DataType::TRADE].emplace_back(std::move(dataPoint));
348327
marketDataMessageList.emplace_back(std::move(marketDataMessage));
349328
}

0 commit comments

Comments
 (0)