Skip to content

Commit

Permalink
feat: Updated REST APIs for Bybit market services from v3 to v5
Browse files Browse the repository at this point in the history
  • Loading branch information
obergaba committed May 16, 2024
1 parent 65de4ce commit cbecea2
Showing 1 changed file with 54 additions and 34 deletions.
88 changes: 54 additions & 34 deletions include/ccapi_cpp/service/ccapi_market_data_service_bybit.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string, std::string> optionMap) override {
auto marketDepthRequested = std::stoi(optionMap.at(CCAPI_MARKET_DEPTH_MAX));
Expand All @@ -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);
}
Expand Down Expand Up @@ -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);
Expand All @@ -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<MarketDataServiceBybit>()](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<MarketDataServiceBybit>()](
const std::string& input) { return that->convertParamTimeSecondsToTimeMilliseconds(input); }},
Expand All @@ -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<std::string, std::string> 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<MarketDataMessage>& marketDataMessageList) override {
Expand All @@ -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));
}
Expand All @@ -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));
}
Expand All @@ -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()});
Expand All @@ -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});
Expand Down

0 comments on commit cbecea2

Please sign in to comment.