From 2f08e3dc89623f402768c12351c1e94e813e9c58 Mon Sep 17 00:00:00 2001 From: Stephane Janel Date: Sun, 24 Nov 2024 16:17:52 +0100 Subject: [PATCH] Use glaze for binance public api --- .../{src => include}/binance-schema.hpp | 118 +++++++++ .../exchanges/include/binancepublicapi.hpp | 4 +- .../exchanges/include/bithumbpublicapi.hpp | 1 + src/api/exchanges/src/binanceprivateapi.cpp | 17 +- src/api/exchanges/src/binancepublicapi.cpp | 239 ++++++++---------- src/basic-objects/include/writer.hpp | 5 - src/basic-objects/src/writer.cpp | 17 -- src/engine/src/queryresultprinter.cpp | 2 +- 8 files changed, 230 insertions(+), 173 deletions(-) rename src/api/exchanges/{src => include}/binance-schema.hpp (60%) delete mode 100644 src/basic-objects/src/writer.cpp diff --git a/src/api/exchanges/src/binance-schema.hpp b/src/api/exchanges/include/binance-schema.hpp similarity index 60% rename from src/api/exchanges/src/binance-schema.hpp rename to src/api/exchanges/include/binance-schema.hpp index 2451b3bd..26c0164d 100644 --- a/src/api/exchanges/src/binance-schema.hpp +++ b/src/api/exchanges/include/binance-schema.hpp @@ -9,8 +9,126 @@ #include "cct_vector.hpp" #include "monetaryamount.hpp" +namespace cct::api { +template +using has_msg_t = decltype(std::declval().msg); + +template +using has_code_t = decltype(std::declval().code); +} // namespace cct::api + namespace cct::schema::binance { +// PUBLIC + +// https://binance-docs.github.io/apidocs/spot/en/#exchange-information + +struct V3ExchangeInfo { + struct Symbol { + string baseAsset; + string quoteAsset; + string status; + int8_t baseAssetPrecision; + int8_t quoteAssetPrecision; + + struct Filter { + string filterType; + MonetaryAmount maxPrice; + MonetaryAmount minPrice; + MonetaryAmount tickSize; + MonetaryAmount minNotional; + MonetaryAmount maxNotional; + MonetaryAmount maxQty; + MonetaryAmount minQty; + MonetaryAmount stepSize; + int32_t avgPriceMins; + bool applyToMarket; + bool applyMinToMarket; + bool applyMaxToMarket; + + using trivially_relocatable = is_trivially_relocatable::type; + + auto operator<=>(const Filter&) const = default; + }; + + vector filters; + + vector permissions; + + using trivially_relocatable = is_trivially_relocatable::type; + + auto operator<=>(const Symbol&) const = default; + }; + + vector symbols; + + std::optional code; + std::optional msg; +}; + +// https://binance-docs.github.io/apidocs/spot/en/#current-average-price +struct V3AvgPrice { + MonetaryAmount price; + std::optional code; + std::optional msg; +}; + +// https://binance-docs.github.io/apidocs/spot/en/#symbol-order-book-ticker +struct V3TickerBookTickerElem { + string symbol; + MonetaryAmount bidPrice; + MonetaryAmount bidQty; + MonetaryAmount askPrice; + MonetaryAmount askQty; + + using trivially_relocatable = is_trivially_relocatable::type; +}; + +using V3TickerBookTicker = vector; + +// https://binance-docs.github.io/apidocs/spot/en/#order-book +struct V3OrderBook { + using Line = std::array; // price is first, then volume + + vector asks; + vector bids; + + std::optional code; + std::optional msg; +}; + +// https://binance-docs.github.io/apidocs/spot/en/#24hr-ticker-price-change-statistics +struct V3Ticker24hr { + MonetaryAmount volume; + + std::optional code; + std::optional msg; +}; + +// https://binance-docs.github.io/apidocs/spot/en/#recent-trades-list +struct V3Trade { + MonetaryAmount price; + MonetaryAmount qty; + int64_t time{}; + bool isBuyerMaker; + + using trivially_relocatable = is_trivially_relocatable::type; + + auto operator<=>(const V3Trade&) const = default; +}; + +using V3Trades = vector; + +// https://binance-docs.github.io/apidocs/spot/en/#symbol-price-ticker +struct V3TickerPrice { + MonetaryAmount price; + + std::optional code; + std::optional msg; +}; + +// PRIVATE + using OrderId = uint64_t; // https://binance-docs.github.io/apidocs/spot/en/#account-status-user_data diff --git a/src/api/exchanges/include/binancepublicapi.hpp b/src/api/exchanges/include/binancepublicapi.hpp index ba06fd8f..e6aa4113 100644 --- a/src/api/exchanges/include/binancepublicapi.hpp +++ b/src/api/exchanges/include/binancepublicapi.hpp @@ -4,8 +4,8 @@ #include #include +#include "binance-schema.hpp" #include "cachedresult.hpp" -#include "cct_json-container.hpp" #include "curlhandle.hpp" #include "currencycode.hpp" #include "currencyexchange.hpp" @@ -81,7 +81,7 @@ class BinancePublic : public ExchangePublic { }; struct ExchangeInfoFunc { - using ExchangeInfoDataByMarket = std::unordered_map; + using ExchangeInfoDataByMarket = std::unordered_map; ExchangeInfoDataByMarket operator()(); diff --git a/src/api/exchanges/include/bithumbpublicapi.hpp b/src/api/exchanges/include/bithumbpublicapi.hpp index ce705aad..3a1b1d0f 100644 --- a/src/api/exchanges/include/bithumbpublicapi.hpp +++ b/src/api/exchanges/include/bithumbpublicapi.hpp @@ -4,6 +4,7 @@ #include #include "cachedresult.hpp" +#include "cct_json-container.hpp" #include "curlhandle.hpp" #include "currencyexchange.hpp" #include "exchange-asset-config.hpp" diff --git a/src/api/exchanges/src/binanceprivateapi.cpp b/src/api/exchanges/src/binanceprivateapi.cpp index 7c3b9ee0..1351932f 100644 --- a/src/api/exchanges/src/binanceprivateapi.cpp +++ b/src/api/exchanges/src/binanceprivateapi.cpp @@ -49,6 +49,7 @@ #include "orderid.hpp" #include "ordersconstraints.hpp" #include "permanentcurloptions.hpp" +#include "read-json.hpp" #include "recentdeposit.hpp" #include "ssl_sha.hpp" #include "stringconv.hpp" @@ -152,12 +153,6 @@ bool CheckErrorMsg(std::string_view msg, QueryDelayDir& queryDelayDir, Duration& return false; } -template -using has_msg_t = decltype(std::declval().msg); - -template -using has_code_t = decltype(std::declval().code); - template bool CheckErrorDoRetry(int statusCode, const T& ret, QueryDelayDir& queryDelayDir, Duration& sleepingTime, Duration& queryDelay) { @@ -187,9 +182,7 @@ bool CheckErrorDoRetry(int statusCode, const T& ret, QueryDelayDir& queryDelayDi return false; } -template +template T PrivateQuery(CurlHandle& curlHandle, const APIKey& apiKey, HttpRequestType requestType, std::string_view endpoint, Duration& queryDelay, CurlPostDataT&& curlPostData = CurlPostData(), bool throwIfError = true) { CurlOptions opts(requestType, std::forward(curlPostData)); @@ -210,11 +203,9 @@ T PrivateQuery(CurlHandle& curlHandle, const APIKey& apiKey, HttpRequestType req auto resStr = curlHandle.query(endpoint, opts); - auto ec = json::read(ret, resStr); + auto ec = ReadJson( + resStr, "binance private", ret); if (ec) { - std::string_view prefixJsonContent = resStr.substr(0, std::min(resStr.size(), 20)); - log::error("Error while reading json content '{}{}': {}", prefixJsonContent, - prefixJsonContent.size() < resStr.size() ? "..." : "", json::format_error(ec, resStr)); statusCode = -1; continue; } diff --git a/src/api/exchanges/src/binancepublicapi.cpp b/src/api/exchanges/src/binancepublicapi.cpp index d828e62a..2a931f8a 100644 --- a/src/api/exchanges/src/binancepublicapi.cpp +++ b/src/api/exchanges/src/binancepublicapi.cpp @@ -17,7 +17,6 @@ #include "cachedresult.hpp" #include "cct_const.hpp" #include "cct_exception.hpp" -#include "cct_json-container.hpp" #include "cct_log.hpp" #include "cct_string.hpp" #include "coincenterinfo.hpp" @@ -40,6 +39,7 @@ #include "order-book-line.hpp" #include "permanentcurloptions.hpp" #include "public-trade-vector.hpp" +#include "read-json.hpp" #include "request-retry.hpp" #include "timedef.hpp" #include "tradeside.hpp" @@ -48,8 +48,8 @@ namespace cct::api { namespace { -json::container PublicQuery(CurlHandle& curlHandle, std::string_view method, - const CurlPostData& curlPostData = CurlPostData()) { +template +T PublicQuery(CurlHandle& curlHandle, std::string_view method, const CurlPostData& curlPostData = CurlPostData()) { string endpoint(method); if (!curlPostData.empty()) { endpoint.push_back('?'); @@ -57,20 +57,21 @@ json::container PublicQuery(CurlHandle& curlHandle, std::string_view method, } RequestRetry requestRetry(curlHandle, CurlOptions(HttpRequestType::kGet)); - return requestRetry.queryJson(endpoint, [](const json::container& jsonResponse) { - const auto foundErrorIt = jsonResponse.find("code"); - const auto foundMsgIt = jsonResponse.find("msg"); - if (foundErrorIt != jsonResponse.end() && foundMsgIt != jsonResponse.end()) { - const int statusCode = foundErrorIt->get(); // "1100" for instance - log::warn("Binance error ({}), full: '{}'", statusCode, jsonResponse.dump()); - return RequestRetry::Status::kResponseError; - } - return RequestRetry::Status::kResponseOK; - }); + return requestRetry.query( + endpoint, [](const T& response) { + if constexpr (amc::is_detected::value && amc::is_detected::value) { + if (response.code && response.msg) { + const int statusCode = *response.code; // "1100" for instance + log::warn("Binance error ({}), msg: '{}'", statusCode, *response.msg); + return RequestRetry::Status::kResponseError; + } + } + + return RequestRetry::Status::kResponseOK; + }); } -template -const json::container& RetrieveMarketData(const ExchangeInfoDataByMarket& exchangeInfoData, Market mk) { +const auto& RetrieveMarketData(const auto& exchangeInfoData, Market mk) { auto it = exchangeInfoData.find(mk); if (it == exchangeInfoData.end()) { throw exception("Unable to retrieve {} data", mk); @@ -78,10 +79,9 @@ const json::container& RetrieveMarketData(const ExchangeInfoDataByMarket& exchan return it->second; } -template -VolAndPriNbDecimals QueryVolAndPriNbDecimals(const ExchangeInfoDataByMarket& exchangeInfoData, Market mk) { - const json::container& marketData = RetrieveMarketData(exchangeInfoData, mk); - return {marketData["baseAssetPrecision"].get(), marketData["quoteAssetPrecision"].get()}; +VolAndPriNbDecimals QueryVolAndPriNbDecimals(const auto& exchangeInfoData, Market mk) { + const auto& marketData = RetrieveMarketData(exchangeInfoData, mk); + return {marketData.baseAssetPrecision, marketData.quoteAssetPrecision}; } } // namespace @@ -114,17 +114,8 @@ BinancePublic::BinancePublic(const CoincenterInfo& coincenterInfo, FiatConverter _commonInfo) {} bool BinancePublic::healthCheck() { - static constexpr bool kAllowExceptions = false; - json::container result = json::container::parse( - _commonInfo._curlHandle.query("/api/v3/ping", CurlOptions(HttpRequestType::kGet)), nullptr, kAllowExceptions); - if (result.is_discarded()) { - log::error("{} health check response is badly formatted: {}", name(), result.dump()); - return false; - } - if (!result.empty()) { - log::error("{} health check is not empty: {}", name(), result.dump()); - } - return result.empty(); + auto result = _commonInfo._curlHandle.query("/api/v3/ping", CurlOptions(HttpRequestType::kGet)); + return result == "{}"; } CurrencyExchangeFlatSet BinancePublic::queryTradableCurrencies() { @@ -144,22 +135,17 @@ std::optional BinancePublic::queryWithdrawalFee(CurrencyCode cur } MarketSet BinancePublic::MarketsFunc::operator()() { - BinancePublic::ExchangeInfoFunc::ExchangeInfoDataByMarket exchangeInfoData = _exchangeConfigCache.get(); + const auto& exchangeInfoData = _exchangeConfigCache.get(); const CurrencyCodeSet& excludedCurrencies = _assetConfig.allExclude; MarketVector markets; markets.reserve(static_cast(exchangeInfoData.size())); - for (const auto& marketJsonPair : exchangeInfoData) { - const json::container& symbol = marketJsonPair.second; - std::string_view baseAsset = symbol["baseAsset"].get(); - std::string_view quoteAsset = symbol["quoteAsset"].get(); - CurrencyCode base(baseAsset); - CurrencyCode quote(quoteAsset); - if (excludedCurrencies.contains(base) || excludedCurrencies.contains(quote)) { + for (const auto& [mk, _] : exchangeInfoData) { + if (excludedCurrencies.contains(mk.base()) || excludedCurrencies.contains(mk.quote())) { continue; } - markets.emplace_back(base, quote); + markets.push_back(mk); } MarketSet ret(std::move(markets)); log::debug("Retrieved {} markets from binance", ret.size()); @@ -168,52 +154,45 @@ MarketSet BinancePublic::MarketsFunc::operator()() { BinancePublic::ExchangeInfoFunc::ExchangeInfoDataByMarket BinancePublic::ExchangeInfoFunc::operator()() { ExchangeInfoDataByMarket ret; - json::container exchangeInfoData = PublicQuery(_commonInfo._curlHandle, "/api/v3/exchangeInfo"); - auto symbolsIt = exchangeInfoData.find("symbols"); - if (symbolsIt == exchangeInfoData.end()) { - return ret; - } - for (auto it = std::make_move_iterator(symbolsIt->begin()), endIt = std::make_move_iterator(symbolsIt->end()); - it != endIt; ++it) { - std::string_view baseAsset = (*it)["baseAsset"].get(); - std::string_view quoteAsset = (*it)["quoteAsset"].get(); - if ((*it)["status"].get() != "TRADING") { - log::trace("Discard {}-{} as not trading status {}", baseAsset, quoteAsset, - (*it)["status"].get()); + auto data = PublicQuery(_commonInfo._curlHandle, "/api/v3/exchangeInfo"); + for (auto& symbol : data.symbols) { + if (symbol.status != "TRADING") { + log::trace("Discard {}-{} as not trading status {}", symbol.baseAsset, symbol.quoteAsset, symbol.status); continue; } - if ((*it)["permissions"].size() == 1 && (*it)["permissions"].front().get() == "LEVERAGED") { + if (symbol.permissions.size() == 1 && symbol.permissions.front() == "LEVERAGED") { // These are '*DOWN' and '*UP' assets, do not take them into account for now - log::trace("Discard {}-{} as coincenter does not support leveraged markets", baseAsset, quoteAsset); + log::trace("Discard {}-{} as coincenter does not support leveraged markets", symbol.baseAsset, symbol.quoteAsset); continue; } - if (baseAsset.size() > CurrencyCode::kMaxLen || quoteAsset.size() > CurrencyCode::kMaxLen) { - log::trace("Discard {}-{} as one asset is too long", baseAsset, quoteAsset); + if (symbol.baseAsset.size() > CurrencyCode::kMaxLen || symbol.quoteAsset.size() > CurrencyCode::kMaxLen) { + log::trace("Discard {}-{} as one asset is too long", symbol.baseAsset, symbol.quoteAsset); continue; } - log::trace("Accept {}-{} Binance asset pair", baseAsset, quoteAsset); - ret.insert_or_assign(Market(baseAsset, quoteAsset), std::move(*it)); + log::trace("Accept {}-{} Binance asset pair", symbol.baseAsset, symbol.quoteAsset); + Market market(CurrencyCode{symbol.baseAsset}, CurrencyCode{symbol.quoteAsset}); + ret.insert_or_assign(std::move(market), std::move(symbol)); } return ret; } MonetaryAmount BinancePublic::sanitizePrice(Market mk, MonetaryAmount pri) { - const json::container& marketData = RetrieveMarketData(_exchangeConfigCache.get(), mk); + const auto& exchangeConfigCache = _exchangeConfigCache.get(); + const auto& marketData = RetrieveMarketData(exchangeConfigCache, mk); - const json::container* pPriceFilter = nullptr; + const schema::binance::V3ExchangeInfo::Symbol::Filter* pPriceFilter = nullptr; MonetaryAmount ret(pri); - for (const json::container& filter : marketData["filters"]) { - const std::string_view filterType = filter["filterType"].get(); - if (filterType == "PRICE_FILTER") { + for (const auto& filter : marketData.filters) { + if (filter.filterType == "PRICE_FILTER") { pPriceFilter = std::addressof(filter); break; } } if (pPriceFilter != nullptr) { - MonetaryAmount maxPrice((*pPriceFilter)["maxPrice"].get(), ret.currencyCode()); - MonetaryAmount minPrice((*pPriceFilter)["minPrice"].get(), ret.currencyCode()); - MonetaryAmount tickSize((*pPriceFilter)["tickSize"].get(), ret.currencyCode()); + MonetaryAmount maxPrice(pPriceFilter->maxPrice, ret.currencyCode()); + MonetaryAmount minPrice(pPriceFilter->minPrice, ret.currencyCode()); + MonetaryAmount tickSize(pPriceFilter->tickSize, ret.currencyCode()); if (ret > maxPrice) { log::debug("Too big price {} capped to {} for {}", ret, maxPrice, mk); @@ -229,7 +208,7 @@ MonetaryAmount BinancePublic::sanitizePrice(Market mk, MonetaryAmount pri) { } } - VolAndPriNbDecimals volAndPriNbDecimals = QueryVolAndPriNbDecimals(_exchangeConfigCache.get(), mk); + VolAndPriNbDecimals volAndPriNbDecimals = QueryVolAndPriNbDecimals(exchangeConfigCache, mk); ret.truncate(volAndPriNbDecimals.priNbDecimals); if (pri != ret) { log::warn("Sanitize price {} -> {}", pri, ret); @@ -247,45 +226,42 @@ MonetaryAmount BinancePublic::computePriceForNotional(Market mk, int avgPriceMin log::error("Unable to retrieve last trades from {}, use average price instead for notional", mk); } - const json::container result = - PublicQuery(_commonInfo._curlHandle, "/api/v3/avgPrice", {{"symbol", mk.assetsPairStrUpper()}}); - const auto priceIt = result.find("price"); - const std::string_view priceStr = priceIt == result.end() ? std::string_view() : priceIt->get(); + const auto result = PublicQuery(_commonInfo._curlHandle, "/api/v3/avgPrice", + {{"symbol", mk.assetsPairStrUpper()}}); - return {priceStr, mk.quote()}; + return {result.price, mk.quote()}; } MonetaryAmount BinancePublic::sanitizeVolume(Market mk, MonetaryAmount vol, MonetaryAmount priceForNotional, bool isTakerOrder) { - const json::container& marketData = RetrieveMarketData(_exchangeConfigCache.get(), mk); + const auto& marketData = RetrieveMarketData(_exchangeConfigCache.get(), mk); MonetaryAmount ret(vol); - const json::container* pMinNotionalFilter = nullptr; - const json::container* pNotionalFilter = nullptr; - const json::container* pLotSizeFilter = nullptr; - const json::container* pMarketLotSizeFilter = nullptr; + const schema::binance::V3ExchangeInfo::Symbol::Filter* pMinNotionalFilter = nullptr; + const schema::binance::V3ExchangeInfo::Symbol::Filter* pNotionalFilter = nullptr; + const schema::binance::V3ExchangeInfo::Symbol::Filter* pLotSizeFilter = nullptr; + const schema::binance::V3ExchangeInfo::Symbol::Filter* pMarketLotSizeFilter = nullptr; - for (const json::container& filter : marketData["filters"]) { - const std::string_view filterType = filter["filterType"].get(); - if (filterType == "LOT_SIZE") { + for (const auto& filter : marketData.filters) { + if (filter.filterType == "LOT_SIZE") { pLotSizeFilter = std::addressof(filter); - } else if (filterType == "MARKET_LOT_SIZE") { + } else if (filter.filterType == "MARKET_LOT_SIZE") { if (isTakerOrder) { pMarketLotSizeFilter = std::addressof(filter); } - } else if (filterType == "MIN_NOTIONAL") { + } else if (filter.filterType == "MIN_NOTIONAL") { if (isTakerOrder) { - if (filter["applyToMarket"].get()) { - priceForNotional = computePriceForNotional(mk, filter["avgPriceMins"].get()); + if (filter.applyToMarket) { + priceForNotional = computePriceForNotional(mk, filter.avgPriceMins); pMinNotionalFilter = std::addressof(filter); } } else { pMinNotionalFilter = std::addressof(filter); } - } else if (filterType == "NOTIONAL") { + } else if (filter.filterType == "NOTIONAL") { if (isTakerOrder) { - if (filter["applyMinToMarket"].get() || filter["applyMaxToMarket"].get()) { - priceForNotional = computePriceForNotional(mk, filter["avgPriceMins"].get()); + if (filter.applyMinToMarket || filter.applyMaxToMarket) { + priceForNotional = computePriceForNotional(mk, filter.avgPriceMins); pNotionalFilter = std::addressof(filter); } } else { @@ -296,7 +272,7 @@ MonetaryAmount BinancePublic::sanitizeVolume(Market mk, MonetaryAmount vol, Mone MonetaryAmount minVolumeAfterMinNotional(0, ret.currencyCode()); if (pMinNotionalFilter != nullptr) { - MonetaryAmount minNotional((*pMinNotionalFilter)["minNotional"].get()); + MonetaryAmount minNotional(pMinNotionalFilter->minNotional); MonetaryAmount priceTimesQuantity = ret.toNeutral() * priceForNotional.toNeutral(); minVolumeAfterMinNotional = MonetaryAmount(minNotional / priceForNotional, ret.currencyCode()); @@ -309,9 +285,9 @@ MonetaryAmount BinancePublic::sanitizeVolume(Market mk, MonetaryAmount vol, Mone if (pNotionalFilter != nullptr) { MonetaryAmount priceTimesQuantity = ret.toNeutral() * priceForNotional.toNeutral(); - if (!isTakerOrder || (*pNotionalFilter)["applyMinToMarket"].get()) { + if (!isTakerOrder || pNotionalFilter->applyMinToMarket) { // min notional applies - MonetaryAmount minNotional((*pNotionalFilter)["minNotional"].get()); + MonetaryAmount minNotional(pNotionalFilter->minNotional); minVolumeAfterMinNotional = std::max(minVolumeAfterMinNotional, MonetaryAmount(minNotional / priceForNotional, ret.currencyCode())); @@ -320,9 +296,9 @@ MonetaryAmount BinancePublic::sanitizeVolume(Market mk, MonetaryAmount vol, Mone log::debug("Too small (price * quantity). {} increased to {} for {}", ret, minVolumeAfterMinNotional, mk); ret = minVolumeAfterMinNotional; } - } else if (!isTakerOrder || (*pNotionalFilter)["applyMaxToMarket"].get()) { + } else if (!isTakerOrder || pNotionalFilter->applyMaxToMarket) { // max notional applies - MonetaryAmount maxNotional((*pNotionalFilter)["maxNotional"].get()); + MonetaryAmount maxNotional(pNotionalFilter->maxNotional); MonetaryAmount maxVolumeAfterMaxNotional = MonetaryAmount(maxNotional / priceForNotional, ret.currencyCode()); if (priceTimesQuantity > maxNotional) { @@ -332,14 +308,14 @@ MonetaryAmount BinancePublic::sanitizeVolume(Market mk, MonetaryAmount vol, Mone } } - for (const json::container* pLotFilterPtr : {pMarketLotSizeFilter, pLotSizeFilter}) { + for (const auto* pLotFilterPtr : {pMarketLotSizeFilter, pLotSizeFilter}) { if (pLotFilterPtr != nullptr) { // "maxQty": "9000000.00000000", // "minQty": "1.00000000", // "stepSize": "1.00000000" - MonetaryAmount maxQty((*pLotFilterPtr)["maxQty"].get(), ret.currencyCode()); - MonetaryAmount minQty((*pLotFilterPtr)["minQty"].get(), ret.currencyCode()); - MonetaryAmount stepSize((*pLotFilterPtr)["stepSize"].get(), ret.currencyCode()); + MonetaryAmount maxQty(pLotFilterPtr->maxQty, ret.currencyCode()); + MonetaryAmount minQty(pLotFilterPtr->minQty, ret.currencyCode()); + MonetaryAmount stepSize(pLotFilterPtr->stepSize, ret.currencyCode()); if (ret > maxQty) { log::debug("Too big volume {} capped to {} for {}", ret, maxQty, mk); @@ -370,7 +346,7 @@ MonetaryAmount BinancePublic::sanitizeVolume(Market mk, MonetaryAmount vol, Mone MarketOrderBookMap BinancePublic::AllOrderBooksFunc::operator()(int depth) { MarketOrderBookMap ret; const MarketSet& markets = _marketsCache.get(); - json::container result = PublicQuery(_commonInfo._curlHandle, "/api/v3/ticker/bookTicker"); + auto result = PublicQuery(_commonInfo._curlHandle, "/api/v3/ticker/bookTicker"); using BinanceAssetPairToStdMarketMap = std::unordered_map; BinanceAssetPairToStdMarketMap binanceAssetPairToStdMarketMap; binanceAssetPairToStdMarketMap.reserve(markets.size()); @@ -378,17 +354,16 @@ MarketOrderBookMap BinancePublic::AllOrderBooksFunc::operator()(int depth) { binanceAssetPairToStdMarketMap.insert_or_assign(mk.assetsPairStrUpper(), mk); } const auto time = Clock::now(); - for (const json::container& tickerDetails : result) { - string assetsPairStr = tickerDetails["symbol"]; - auto it = binanceAssetPairToStdMarketMap.find(assetsPairStr); + for (const auto& elem : result) { + auto it = binanceAssetPairToStdMarketMap.find(elem.symbol); if (it == binanceAssetPairToStdMarketMap.end()) { continue; } Market mk = it->second; - MonetaryAmount askPri(tickerDetails["askPrice"].get(), mk.quote()); - MonetaryAmount bidPri(tickerDetails["bidPrice"].get(), mk.quote()); - MonetaryAmount askVol(tickerDetails["askQty"].get(), mk.base()); - MonetaryAmount bidVol(tickerDetails["bidQty"].get(), mk.base()); + MonetaryAmount askPri(elem.askPrice, mk.quote()); + MonetaryAmount bidPri(elem.bidPrice, mk.quote()); + MonetaryAmount askVol(elem.askQty, mk.base()); + MonetaryAmount bidVol(elem.bidQty, mk.base()); ret.insert_or_assign(mk, MarketOrderBook(time, askPri, askVol, bidPri, bidVol, QueryVolAndPriNbDecimals(_exchangeConfigCache.get(), mk), depth)); @@ -410,22 +385,20 @@ MarketOrderBook BinancePublic::OrderBookFunc::operator()(Market mk, int depth) { MarketOrderBookLines orderBookLines; const CurlPostData postData{{"symbol", mk.assetsPairStrUpper()}, {"limit", *lb}}; - const json::container asksAndBids = PublicQuery(_commonInfo._curlHandle, "/api/v3/depth", postData); + const auto asksAndBids = + PublicQuery(_commonInfo._curlHandle, "/api/v3/depth", postData); const auto nowTime = Clock::now(); - const auto asksIt = asksAndBids.find("asks"); - const auto bidsIt = asksAndBids.find("bids"); - - if (asksIt != asksAndBids.end() && bidsIt != asksAndBids.end()) { - orderBookLines.reserve(std::min(static_cast(asksIt->size()), depth) + - std::min(static_cast(bidsIt->size()), depth)); - for (const auto& asksOrBids : {asksIt, bidsIt}) { - const auto type = asksOrBids == asksIt ? OrderBookLine::Type::kAsk : OrderBookLine::Type::kBid; - for (const auto& priceQuantityPair : *asksOrBids | std::ranges::views::take(depth)) { - MonetaryAmount amount(priceQuantityPair.back().get(), mk.base()); - MonetaryAmount price(priceQuantityPair.front().get(), mk.quote()); - - orderBookLines.push(amount, price, type); - } + + orderBookLines.reserve(std::min(static_cast(asksAndBids.asks.size()), depth) + + std::min(static_cast(asksAndBids.bids.size()), depth)); + + for (const auto asksOrBids : {&asksAndBids.asks, &asksAndBids.bids}) { + const auto type = asksOrBids == &asksAndBids.asks ? OrderBookLine::Type::kAsk : OrderBookLine::Type::kBid; + for (const auto& [pri, vol] : *asksOrBids | std::ranges::views::take(depth)) { + MonetaryAmount price(pri, mk.quote()); + MonetaryAmount amount(vol, mk.base()); + + orderBookLines.push(amount, price, type); } } @@ -433,12 +406,10 @@ MarketOrderBook BinancePublic::OrderBookFunc::operator()(Market mk, int depth) { } MonetaryAmount BinancePublic::TradedVolumeFunc::operator()(Market mk) { - const json::container result = - PublicQuery(_commonInfo._curlHandle, "/api/v3/ticker/24hr", {{"symbol", mk.assetsPairStrUpper()}}); - const auto volumeIt = result.find("volume"); - const std::string_view last24hVol = volumeIt == result.end() ? std::string_view() : volumeIt->get(); + const auto result = PublicQuery(_commonInfo._curlHandle, "/api/v3/ticker/24hr", + {{"symbol", mk.assetsPairStrUpper()}}); - return {last24hVol, mk.base()}; + return {result.volume, mk.base()}; } PublicTradeVector BinancePublic::queryLastTrades(Market mk, int nbTrades) { @@ -449,17 +420,17 @@ PublicTradeVector BinancePublic::queryLastTrades(Market mk, int nbTrades) { nbTrades = kMaxNbLastTrades; } - json::container result = PublicQuery(_commonInfo._curlHandle, "/api/v3/trades", - {{"symbol", mk.assetsPairStrUpper()}, {"limit", nbTrades}}); + const auto result = PublicQuery( + _commonInfo._curlHandle, "/api/v3/trades", {{"symbol", mk.assetsPairStrUpper()}, {"limit", nbTrades}}); PublicTradeVector ret; ret.reserve(static_cast(result.size())); - for (const json::container& detail : result) { - MonetaryAmount amount(detail["qty"].get(), mk.base()); - MonetaryAmount price(detail["price"].get(), mk.quote()); - int64_t millisecondsSinceEpoch = detail["time"].get(); - TradeSide tradeSide = detail["isBuyerMaker"].get() ? TradeSide::kSell : TradeSide::kBuy; + for (const auto& elem : result) { + MonetaryAmount price(elem.price, mk.quote()); + MonetaryAmount amount(elem.qty, mk.base()); + int64_t millisecondsSinceEpoch = elem.time; + TradeSide tradeSide = elem.isBuyerMaker ? TradeSide::kSell : TradeSide::kBuy; ret.emplace_back(tradeSide, amount, price, TimePoint(milliseconds(millisecondsSinceEpoch))); } @@ -468,11 +439,9 @@ PublicTradeVector BinancePublic::queryLastTrades(Market mk, int nbTrades) { } MonetaryAmount BinancePublic::TickerFunc::operator()(Market mk) { - const json::container data = - PublicQuery(_commonInfo._curlHandle, "/api/v3/ticker/price", {{"symbol", mk.assetsPairStrUpper()}}); - const auto priceIt = data.find("price"); - const std::string_view lastPrice = priceIt == data.end() ? std::string_view() : priceIt->get(); - return {lastPrice, mk.quote()}; + const auto data = PublicQuery(_commonInfo._curlHandle, "/api/v3/ticker/price", + {{"symbol", mk.assetsPairStrUpper()}}); + return {data.price, mk.quote()}; } } // namespace cct::api diff --git a/src/basic-objects/include/writer.hpp b/src/basic-objects/include/writer.hpp index a3559c84..0276fb0a 100644 --- a/src/basic-objects/include/writer.hpp +++ b/src/basic-objects/include/writer.hpp @@ -3,8 +3,6 @@ #include #include -#include "cct_json-container.hpp" - namespace cct { class Writer { @@ -17,9 +15,6 @@ class Writer { virtual int write([[maybe_unused]] std::string_view data, [[maybe_unused]] Mode mode = Mode::FromStart) const { return 0; } - - // Write json and return number of bytes written - int writeJson(const json::container &data, Mode mode = Mode::FromStart) const; }; } // namespace cct \ No newline at end of file diff --git a/src/basic-objects/src/writer.cpp b/src/basic-objects/src/writer.cpp deleted file mode 100644 index 9f9269cc..00000000 --- a/src/basic-objects/src/writer.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "writer.hpp" - -#include - -#include "cct_json-container.hpp" -namespace cct { - -int Writer::writeJson(const json::container &data, Mode mode) const { - if (data.empty()) { - return this->write(std::string_view("{}"), mode); - } - const int indent = mode == Writer::Mode::FromStart ? 2 : -1; - const auto str = data.dump(indent); - return this->write(static_cast(str), mode); -} - -} // namespace cct \ No newline at end of file diff --git a/src/engine/src/queryresultprinter.cpp b/src/engine/src/queryresultprinter.cpp index ec62affb..0b46bf2c 100644 --- a/src/engine/src/queryresultprinter.cpp +++ b/src/engine/src/queryresultprinter.cpp @@ -1734,7 +1734,7 @@ void QueryResultPrinter::logActivity(CoincenterCommandType commandType, const js if (_loggingInfo.isCommandTypeTracked(commandType) && (!isSimulationMode || _loggingInfo.alsoLogActivityForSimulatedCommands())) { File activityFile = _loggingInfo.getActivityFile(); - activityFile.writeJson(jsonData, Writer::Mode::Append); + activityFile.write(jsonData.dump(), Writer::Mode::Append); } }