diff --git a/cppsrc/StarQuant/CMakeLists.txt b/cppsrc/StarQuant/CMakeLists.txt index 2384d7fc4..a2f5aa70d 100644 --- a/cppsrc/StarQuant/CMakeLists.txt +++ b/cppsrc/StarQuant/CMakeLists.txt @@ -2,6 +2,7 @@ include_directories(${StarQuantCpp_SOURCE_DIR}/StarQuant) set(StarQuantCppSrc ./Common/datastruct.cpp ./Common/util.cpp + ./Common/fxmacrodata.cpp ./Common/logger.cpp ./Common/config.cpp ./Common/msgq.cpp @@ -43,4 +44,4 @@ target_link_libraries(starquant m) set(SQSERVER_SRC ./Services/sqserver.cpp ) add_executable(apiserver.exe ${SQSERVER_SRC}) -TARGET_LINK_LIBRARIES(apiserver.exe starquant) \ No newline at end of file +TARGET_LINK_LIBRARIES(apiserver.exe starquant) diff --git a/cppsrc/StarQuant/Common/fxmacrodata.cpp b/cppsrc/StarQuant/Common/fxmacrodata.cpp new file mode 100644 index 000000000..478901e8b --- /dev/null +++ b/cppsrc/StarQuant/Common/fxmacrodata.cpp @@ -0,0 +1,200 @@ +/***************************************************************************** + * Copyright [2026] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + *****************************************************************************/ + +#include + +#include +#include +#include +#include + +namespace StarQuant { +namespace { + +std::string trimTrailingSlash(std::string value) { + while (!value.empty() && value.back() == '/') { + value.pop_back(); + } + return value; +} + +std::string ensureLeadingSlash(const std::string& path) { + if (!path.empty() && path.front() == '/') { + return path; + } + return "/" + path; +} + +std::string lower(std::string value) { + for (char& ch : value) { + ch = static_cast(std::tolower(static_cast(ch))); + } + return value; +} + +std::string encode(const std::string& value) { + std::ostringstream out; + out << std::uppercase << std::hex; + for (unsigned char ch : value) { + if (std::isalnum(ch) || ch == '-' || ch == '_' || ch == '.' || ch == '~') { + out << static_cast(ch); + } else { + out << '%' << std::setw(2) << std::setfill('0') << static_cast(ch); + } + } + return out.str(); +} + +} // namespace + +FxMacroDataClient::FxMacroDataClient(std::string api_key, + std::string base_url, + Transport transport) + : api_key_(std::move(api_key)), + base_url_(trimTrailingSlash(std::move(base_url))), + transport_(std::move(transport)) {} + +std::string FxMacroDataClient::buildUrl(const std::string& path, QueryParams params) const { + if (!api_key_.empty()) { + params.push_back({"api_key", api_key_}); + } + + std::ostringstream url; + url << base_url_ << ensureLeadingSlash(path); + char separator = '?'; + for (const auto& param : params) { + url << separator << encode(param.first) << '=' << encode(param.second); + separator = '&'; + } + return url.str(); +} + +std::string FxMacroDataClient::send(const std::string& method, + const std::string& path, + QueryParams params, + const std::string& body) const { + if (!transport_) { + throw std::runtime_error("FxMacroDataClient requires a transport callback"); + } + return transport_(method, buildUrl(path, std::move(params)), body); +} + +std::string FxMacroDataClient::dataCatalogue(const std::string& currency, + QueryParams params) const { + return send("GET", "/data_catalogue/" + encode(lower(currency)), std::move(params)); +} + +std::string FxMacroDataClient::announcements(const std::string& currency, + const std::string& indicator, + QueryParams params) const { + return send("GET", "/announcements/" + encode(lower(currency)) + "/" + encode(indicator), + std::move(params)); +} + +std::string FxMacroDataClient::latestAnnouncements(const std::string& currency, + QueryParams params) const { + return send("GET", "/announcements/" + encode(lower(currency)) + "/latest", + std::move(params)); +} + +std::string FxMacroDataClient::announcementChanges(QueryParams params) const { + return send("GET", "/announcements/changes", std::move(params)); +} + +std::string FxMacroDataClient::calendar(const std::string& currency, + QueryParams params) const { + return send("GET", "/calendar/" + encode(lower(currency)), std::move(params)); +} + +std::string FxMacroDataClient::predictions(const std::string& currency, + const std::string& indicator, + QueryParams params) const { + return send("GET", "/predictions/" + encode(lower(currency)) + "/" + encode(indicator), + std::move(params)); +} + +std::string FxMacroDataClient::forex(const std::string& base, + const std::string& quote, + QueryParams params) const { + return send("GET", "/forex/" + encode(lower(base)) + "/" + encode(lower(quote)), + std::move(params)); +} + +std::string FxMacroDataClient::cot(const std::string& currency, + QueryParams params) const { + return send("GET", "/cot/" + encode(lower(currency)), std::move(params)); +} + +std::string FxMacroDataClient::commodity(const std::string& indicator, + QueryParams params) const { + return send("GET", "/commodities/" + encode(indicator), std::move(params)); +} + +std::string FxMacroDataClient::commoditiesLatest(QueryParams params) const { + return send("GET", "/commodities/latest", std::move(params)); +} + +std::string FxMacroDataClient::curves(const std::string& currency, + QueryParams params) const { + return send("GET", "/curves/" + encode(lower(currency)), std::move(params)); +} + +std::string FxMacroDataClient::curveProxies(const std::string& currency, + QueryParams params) const { + return send("GET", "/curve_proxies/" + encode(lower(currency)), std::move(params)); +} + +std::string FxMacroDataClient::forwardCurves(const std::string& currency, + QueryParams params) const { + return send("GET", "/forward_curves/" + encode(lower(currency)), std::move(params)); +} + +std::string FxMacroDataClient::rateDifferentials(const std::string& base, + const std::string& quote, + QueryParams params) const { + return send("GET", "/rate_differentials/" + encode(lower(base)) + "/" + encode(lower(quote)), + std::move(params)); +} + +std::string FxMacroDataClient::forwardDifferentials(const std::string& base, + const std::string& quote, + QueryParams params) const { + return send("GET", "/forward_differentials/" + encode(lower(base)) + "/" + encode(lower(quote)), + std::move(params)); +} + +std::string FxMacroDataClient::marketSessions(QueryParams params) const { + return send("GET", "/market_sessions", std::move(params)); +} + +std::string FxMacroDataClient::riskSentiment(QueryParams params) const { + return send("GET", "/risk_sentiment", std::move(params)); +} + +std::string FxMacroDataClient::news(const std::string& currency, + QueryParams params) const { + return send("GET", "/news/" + encode(lower(currency)), std::move(params)); +} + +std::string FxMacroDataClient::pressReleases(const std::string& currency, + QueryParams params) const { + return send("GET", "/press-releases/" + encode(lower(currency)), std::move(params)); +} + +std::string FxMacroDataClient::graphql(const std::string& query, + const std::string& variables_json) const { + return send("POST", "/graphql", {}, + "{\"query\":\"" + query + "\",\"variables\":" + variables_json + "}"); +} + +std::string FxMacroDataClient::request(const std::string& path, + QueryParams params, + std::string method, + std::string body) const { + return send(std::move(method), path, std::move(params), body); +} + +} // namespace StarQuant diff --git a/cppsrc/StarQuant/Common/fxmacrodata.h b/cppsrc/StarQuant/Common/fxmacrodata.h new file mode 100644 index 000000000..c2b10b18f --- /dev/null +++ b/cppsrc/StarQuant/Common/fxmacrodata.h @@ -0,0 +1,77 @@ +/***************************************************************************** + * Copyright [2026] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + *****************************************************************************/ +#ifndef CPPSRC_STARQUANT_COMMON_FXMACRODATA_H_ +#define CPPSRC_STARQUANT_COMMON_FXMACRODATA_H_ + +#include +#include +#include +#include + +namespace StarQuant { + +class FxMacroDataClient { + public: + using QueryParams = std::vector>; + using Transport = std::function; + + explicit FxMacroDataClient(std::string api_key = "", + std::string base_url = "https://api.fxmacrodata.com/v1", + Transport transport = nullptr); + + std::string dataCatalogue(const std::string& currency, QueryParams params = {}) const; + std::string announcements(const std::string& currency, + const std::string& indicator, + QueryParams params = {}) const; + std::string latestAnnouncements(const std::string& currency, QueryParams params = {}) const; + std::string announcementChanges(QueryParams params = {}) const; + std::string calendar(const std::string& currency, QueryParams params = {}) const; + std::string predictions(const std::string& currency, + const std::string& indicator, + QueryParams params = {}) const; + std::string forex(const std::string& base, const std::string& quote, + QueryParams params = {}) const; + std::string cot(const std::string& currency, QueryParams params = {}) const; + std::string commodity(const std::string& indicator, QueryParams params = {}) const; + std::string commoditiesLatest(QueryParams params = {}) const; + std::string curves(const std::string& currency, QueryParams params = {}) const; + std::string curveProxies(const std::string& currency, QueryParams params = {}) const; + std::string forwardCurves(const std::string& currency, QueryParams params = {}) const; + std::string rateDifferentials(const std::string& base, + const std::string& quote, + QueryParams params = {}) const; + std::string forwardDifferentials(const std::string& base, + const std::string& quote, + QueryParams params = {}) const; + std::string marketSessions(QueryParams params = {}) const; + std::string riskSentiment(QueryParams params = {}) const; + std::string news(const std::string& currency, QueryParams params = {}) const; + std::string pressReleases(const std::string& currency, QueryParams params = {}) const; + std::string graphql(const std::string& query, + const std::string& variables_json = "{}") const; + std::string request(const std::string& path, + QueryParams params = {}, + std::string method = "GET", + std::string body = "") const; + + std::string buildUrl(const std::string& path, QueryParams params = {}) const; + + private: + std::string api_key_; + std::string base_url_; + Transport transport_; + + std::string send(const std::string& method, + const std::string& path, + QueryParams params, + const std::string& body = "") const; +}; + +} // namespace StarQuant + +#endif // CPPSRC_STARQUANT_COMMON_FXMACRODATA_H_