From 65de4ceea5d9e9229d5506a8418f5d8eafa52b98 Mon Sep 17 00:00:00 2001 From: obergaba Date: Wed, 15 May 2024 21:00:51 +0200 Subject: [PATCH 1/3] fix: Prevent out-of-bounds access in ceilSearch function --- include/ccapi_cpp/ccapi_util_private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ccapi_cpp/ccapi_util_private.h b/include/ccapi_cpp/ccapi_util_private.h index 1f7d88e2..83644d88 100644 --- a/include/ccapi_cpp/ccapi_util_private.h +++ b/include/ccapi_cpp/ccapi_util_private.h @@ -732,7 +732,7 @@ int ceilSearch(const std::vector& c, int low, int high, T x) { if (x <= c[low]) { return low; } - for (i = low; i < high; i++) { + for (i = low; i < high - 1; i++) { if (c[i] == x) { return i; } From cbecea2d7281ea69f4568ea5072f387c7893202d Mon Sep 17 00:00:00 2001 From: obergaba Date: Thu, 16 May 2024 23:06:19 +0200 Subject: [PATCH 2/3] feat: Updated REST APIs for Bybit market services from v3 to v5 --- .../service/ccapi_market_data_service_bybit.h | 88 ++++++++++++------- 1 file changed, 54 insertions(+), 34 deletions(-) diff --git a/include/ccapi_cpp/service/ccapi_market_data_service_bybit.h b/include/ccapi_cpp/service/ccapi_market_data_service_bybit.h index 383c4b2b..30024f11 100644 --- a/include/ccapi_cpp/service/ccapi_market_data_service_bybit.h +++ b/include/ccapi_cpp/service/ccapi_market_data_service_bybit.h @@ -27,18 +27,29 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { // CCAPI_LOGGER_FATAL(std::string("e.what() = ") + e.what()); // } // #endif - this->getRecentTradesTarget = "/spot/v3/public/quote/trades"; - this->getHistoricalTradesTarget = "/spot/v3/public/quote/trades"; - this->getRecentCandlesticksTarget = "/spot/v3/public/quote/kline"; - this->getHistoricalCandlesticksTarget = "/spot/v3/public/quote/kline"; - this->getMarketDepthTarget = "/spot/v3/public/quote/depth"; - this->getInstrumentsTarget = "/spot/v3/public/symbols"; + this->getRecentTradesTarget = "/v5/market/recent-trade"; + // this->getHistoricalTradesTarget = "/spot/v3/public/quote/trades"; + this->getRecentCandlesticksTarget = "/v5/market/kline"; + // this->getHistoricalCandlesticksTarget = "/spot/v3/public/quote/kline"; + this->getMarketDepthTarget = "/v5/market/orderbook"; + this->getInstrumentsTarget = "/v5/market/instruments-info"; } virtual ~MarketDataServiceBybit() {} #ifndef CCAPI_EXPOSE_INTERNAL protected: #endif + std::string convertCandlestickIntervalSecondsToInterval(int intervalSeconds) { + std::string interval; + if (intervalSeconds < 86400) { + interval = std::to_string(intervalSeconds / 60); + } else if (intervalSeconds == 86400) { + interval = "D"; + } else { + interval = "W"; + } + return interval; + } void prepareSubscriptionDetail(std::string& channelId, std::string& symbolId, const std::string& field, const WsConnection& wsConnection, const Subscription& subscription, const std::map optionMap) override { auto marketDepthRequested = std::stoi(optionMap.at(CCAPI_MARKET_DEPTH_MAX)); @@ -49,8 +60,8 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { channelId = CCAPI_WEBSOCKET_BYBIT_CHANNEL_DEPTH; } } else if (field == CCAPI_CANDLESTICK) { - std::string interval = - this->convertCandlestickIntervalSecondsToInterval(std::stoi(optionMap.at(CCAPI_CANDLESTICK_INTERVAL_SECONDS)), "s", "m", "h", "d", "w"); + int intervalSeconds = std::stoi(optionMap.at(CCAPI_CANDLESTICK_INTERVAL_SECONDS)); + std::string interval = this->convertCandlestickIntervalSecondsToInterval(intervalSeconds); std::string toReplace = "{interval}"; channelId.replace(channelId.find(toReplace), toReplace.length(), interval); } @@ -245,6 +256,7 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { this->appendParam(queryString, param, { {CCAPI_LIMIT, "limit"}, + {CCAPI_INSTRUMENT_TYPE, "category"}, }); this->appendSymbolId(queryString, symbolId, "symbol"); req.target(target + "?" + queryString); @@ -259,13 +271,14 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { { {CCAPI_CANDLESTICK_INTERVAL_SECONDS, "interval"}, {CCAPI_LIMIT, "limit"}, - {CCAPI_START_TIME_SECONDS, "startTime"}, - {CCAPI_END_TIME_SECONDS, "endTime"}, + {CCAPI_START_TIME_SECONDS, "start"}, + {CCAPI_END_TIME_SECONDS, "end"}, + {CCAPI_INSTRUMENT_TYPE, "category"}, }, { {CCAPI_CANDLESTICK_INTERVAL_SECONDS, [that = shared_from_base()](const std::string& input) { - return that->convertCandlestickIntervalSecondsToInterval(std::stoi(input), "", "m", "h", "d", "w"); + return that->convertCandlestickIntervalSecondsToInterval(std::stoi(input)); }}, {CCAPI_START_TIME_SECONDS, [that = shared_from_base()]( const std::string& input) { return that->convertParamTimeSecondsToTimeMilliseconds(input); }}, @@ -283,32 +296,37 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { this->appendParam(queryString, param, { {CCAPI_LIMIT, "limit"}, + {CCAPI_INSTRUMENT_TYPE, "category"}, }); this->appendSymbolId(queryString, symbolId, "symbol"); req.target(target + "?" + queryString); } break; - case Request::Operation::GET_INSTRUMENT: { - req.method(http::verb::get); - auto target = this->getInstrumentsTarget; - req.target(target); - } break; + case Request::Operation::GET_INSTRUMENT: case Request::Operation::GET_INSTRUMENTS: { req.method(http::verb::get); auto target = this->getInstrumentsTarget; - req.target(target); + std::string queryString; + const std::map param = request.getFirstParamWithDefault(); + this->appendParam(queryString, param, + { + {CCAPI_LIMIT, "limit"}, + {CCAPI_INSTRUMENT_TYPE, "category"}, + }); + this->appendSymbolId(queryString, symbolId, "symbol"); + req.target(target + "?" + queryString); } break; default: this->convertRequestForRestCustom(req, request, now, symbolId, credential); } } void extractInstrumentInfo(Element& element, const rj::Value& x) { - element.insert(CCAPI_INSTRUMENT, x["name"].GetString()); + element.insert(CCAPI_INSTRUMENT, x["symbol"].GetString()); element.insert(CCAPI_BASE_ASSET, x["baseCoin"].GetString()); element.insert(CCAPI_QUOTE_ASSET, x["quoteCoin"].GetString()); - element.insert(CCAPI_ORDER_PRICE_INCREMENT, x["minPricePrecision"].GetString()); - element.insert(CCAPI_ORDER_QUANTITY_INCREMENT, x["basePrecision"].GetString()); - element.insert(CCAPI_ORDER_QUANTITY_MIN, x["minTradeQty"].GetString()); - element.insert(CCAPI_ORDER_PRICE_TIMES_QUANTITY_MIN, x["minTradeAmt"].GetString()); + element.insert(CCAPI_ORDER_PRICE_INCREMENT, x["priceFilter"]["tickSize"].GetString()); + element.insert(CCAPI_ORDER_QUANTITY_INCREMENT, x["lotSizeFilter"]["basePrecision"].GetString()); + element.insert(CCAPI_ORDER_QUANTITY_MIN, x["lotSizeFilter"]["minOrderQty"].GetString()); + element.insert(CCAPI_ORDER_PRICE_TIMES_QUANTITY_MIN, x["lotSizeFilter"]["minOrderAmt"].GetString()); } void convertTextMessageToMarketDataMessage(const Request& request, const std::string& textMessage, const TimePoint& timeReceived, Event& event, std::vector& marketDataMessageList) override { @@ -323,8 +341,9 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { marketDataMessage.tp = UtilTime::makeTimePointFromMilliseconds(std::stoll(x["time"].GetString())); MarketDataMessage::TypeForDataPoint dataPoint; dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, UtilString::normalizeDecimalString(std::string(x["price"].GetString()))}); - dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, UtilString::normalizeDecimalString(std::string(x["qty"].GetString()))}); - dataPoint.insert({MarketDataMessage::DataFieldType::IS_BUYER_MAKER, x["isBuyerMaker"].GetString() ? "0" : "1"}); + 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"}); marketDataMessage.data[MarketDataMessage::DataType::TRADE].emplace_back(std::move(dataPoint)); marketDataMessageList.emplace_back(std::move(marketDataMessage)); } @@ -334,13 +353,14 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { for (const auto& x : document["result"]["list"].GetArray()) { MarketDataMessage marketDataMessage; marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_CANDLESTICK; - marketDataMessage.tp = UtilTime::makeTimePointFromMilliseconds(std::stoll(x["t"].GetString())); + marketDataMessage.tp = UtilTime::makeTimePointFromMilliseconds(std::stoll(x[0].GetString())); MarketDataMessage::TypeForDataPoint dataPoint; - dataPoint.insert({MarketDataMessage::DataFieldType::OPEN_PRICE, x["o"].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::HIGH_PRICE, x["h"].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::LOW_PRICE, x["l"].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::CLOSE_PRICE, x["c"].GetString()}); - dataPoint.insert({MarketDataMessage::DataFieldType::VOLUME, x["v"].GetString()}); + dataPoint.insert({MarketDataMessage::DataFieldType::OPEN_PRICE, x[1].GetString()}); + dataPoint.insert({MarketDataMessage::DataFieldType::HIGH_PRICE, x[2].GetString()}); + dataPoint.insert({MarketDataMessage::DataFieldType::LOW_PRICE, x[3].GetString()}); + dataPoint.insert({MarketDataMessage::DataFieldType::CLOSE_PRICE, x[4].GetString()}); + dataPoint.insert({MarketDataMessage::DataFieldType::VOLUME, x[5].GetString()}); + dataPoint.insert({MarketDataMessage::DataFieldType::QUOTE_VOLUME, x[6].GetString()}); marketDataMessage.data[MarketDataMessage::DataType::CANDLESTICK].emplace_back(std::move(dataPoint)); marketDataMessageList.emplace_back(std::move(marketDataMessage)); } @@ -349,14 +369,14 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { MarketDataMessage marketDataMessage; marketDataMessage.type = MarketDataMessage::Type::MARKET_DATA_EVENTS_MARKET_DEPTH; const rj::Value& result = document["result"]; - marketDataMessage.tp = UtilTime::makeTimePointFromMilliseconds(std::stoll(result["time"].GetString())); - for (const auto& x : result["bids"].GetArray()) { + marketDataMessage.tp = UtilTime::makeTimePointFromMilliseconds(std::stoll(result["ts"].GetString())); + for (const auto& x : result["b"].GetArray()) { MarketDataMessage::TypeForDataPoint dataPoint; dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, x[0].GetString()}); dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, x[1].GetString()}); marketDataMessage.data[MarketDataMessage::DataType::BID].emplace_back(std::move(dataPoint)); } - for (const auto& x : result["asks"].GetArray()) { + for (const auto& x : result["a"].GetArray()) { MarketDataMessage::TypeForDataPoint dataPoint; dataPoint.insert({MarketDataMessage::DataFieldType::PRICE, x[0].GetString()}); dataPoint.insert({MarketDataMessage::DataFieldType::SIZE, x[1].GetString()}); @@ -369,7 +389,7 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { message.setTimeReceived(timeReceived); message.setType(this->requestOperationToMessageTypeMap.at(request.getOperation())); for (const auto& x : document["result"]["list"].GetArray()) { - if (std::string(x["name"].GetString()) == request.getInstrument()) { + if (std::string(x["symbol"].GetString()) == request.getInstrument()) { Element element; this->extractInstrumentInfo(element, x); message.setElementList({element}); From 7fda872b971247ccb3123f22c4756116999bdd27 Mon Sep 17 00:00:00 2001 From: obergaba Date: Mon, 20 May 2024 22:31:02 +0200 Subject: [PATCH 3/3] feat: Update WebSocket to v5 for Bybit spot market service --- include/ccapi_cpp/ccapi_macro.h | 7 +- .../service/ccapi_market_data_service_bybit.h | 89 +++++++------------ 2 files changed, 37 insertions(+), 59 deletions(-) diff --git a/include/ccapi_cpp/ccapi_macro.h b/include/ccapi_cpp/ccapi_macro.h index d4851958..c48f3d3a 100644 --- a/include/ccapi_cpp/ccapi_macro.h +++ b/include/ccapi_cpp/ccapi_macro.h @@ -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" @@ -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}" diff --git a/include/ccapi_cpp/service/ccapi_market_data_service_bybit.h b/include/ccapi_cpp/service/ccapi_market_data_service_bybit.h index 30024f11..64204dfc 100644 --- a/include/ccapi_cpp/service/ccapi_market_data_service_bybit.h +++ b/include/ccapi_cpp/service/ccapi_market_data_service_bybit.h @@ -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); @@ -54,11 +54,13 @@ class MarketDataServiceBybit : public MarketDataServiceBybitBase { const Subscription& subscription, const std::map 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 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); @@ -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); @@ -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)); } @@ -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)); }