8#define ATL_BROKER_ADAPTER_PRESENT 1
10#include <algotradelib/network>
12#include <boost/json.hpp>
23namespace algotradelib::broker_detail {
28inline std::optional<std::int64_t> integer_header(
32 const auto value = response.base().find(name);
33 if (value == response.base().end()) {
36 const auto text = value->value();
37 std::int64_t number{0};
38 const auto parsed = std::from_chars(text.data(), text.data() + text.size(), number);
39 if (parsed.ec != std::errc{} || parsed.ptr != text.data() + text.size() || number < 0) {
48inline constexpr std::size_t reply_excerpt_limit = 512;
54inline std::string reply_excerpt(std::string_view body, std::size_t limit = reply_excerpt_limit) {
55 if (body.size() <= limit) {
56 return std::string(body);
58 std::size_t cut = limit;
59 while (cut > 0 && limit - cut < 3 && (
static_cast<unsigned char>(body[cut]) & 0xC0U) == 0x80U) {
62 return std::string(body.substr(0, cut)) +
"... (" + std::to_string(body.size() - cut) +
" more bytes)";
67 error.http_status_ = response.result_int();
68 error.category_ = response.result() == http::status::too_many_requests ?
71 error.message_ = reply_excerpt(response.body());
77inline std::optional<json::object> parse_object(
const network::HttpResponse& response, BrokerError& error) {
78 boost::system::error_code ec;
79 auto value = json::parse(response.body(), ec);
80 if (ec || !value.is_object()) {
82 error.http_status_ = response.result_int();
83 error.broker_code_ =
"invalid_json";
84 error.message_ = ec ? ec.message() :
"JSON response is not an object";
87 return std::move(value.as_object());
90inline std::string json_string(
const json::object&
object, std::string_view key) {
91 const auto value =
object.if_contains(key);
92 if (!value || !value->is_string()) {
93 throw std::runtime_error(
"missing string field: " + std::string(key));
95 return std::string(value->as_string());
98inline std::int64_t json_int64(
const json::object&
object, std::string_view key) {
99 const auto value =
object.if_contains(key);
101 throw std::runtime_error(
"missing integer field: " + std::string(key));
103 if (value->is_int64()) {
104 return value->as_int64();
107 value->is_uint64() &&
108 value->as_uint64() <=
static_cast<std::uint64_t
>(std::numeric_limits<std::int64_t>::max())
110 return static_cast<std::int64_t
>(value->as_uint64());
112 throw std::runtime_error(
"invalid integer field: " + std::string(key));
115inline bool valid_symbol(std::string_view symbol) {
116 if (symbol.empty()) {
119 for (
const char ch : symbol) {
120 const bool valid = (ch >=
'0' && ch <=
'9') || (ch >=
'A' && ch <=
'Z') ||
121 (ch >=
'a' && ch <=
'z') || ch ==
'.' || ch ==
'-';
129inline std::string query_encode(std::string_view value) {
130 static constexpr char hex[] =
"0123456789ABCDEF";
132 result.reserve(value.size());
133 for (
const unsigned char ch : value) {
134 const bool unreserved = (ch >=
'A' && ch <=
'Z') || (ch >=
'a' && ch <=
'z') ||
135 (ch >=
'0' && ch <=
'9') || ch ==
'-' || ch ==
'.' || ch ==
'_' || ch ==
'~';
137 result.push_back(
static_cast<char>(ch));
139 result.push_back(
'%');
140 result.push_back(hex[ch >> 4]);
141 result.push_back(hex[ch & 0x0F]);
147inline bool decimal_digits(std::string_view value, std::size_t length) {
148 if (value.size() != length) {
151 for (
const char ch : value) {
152 if (ch <
'0' || ch >
'9') {
163inline std::optional<Date> compact_date(std::string_view text) {
164 if (!decimal_digits(text, 8)) {
167 const auto number = [text](std::size_t offset, std::size_t length) {
169 for (std::size_t index = offset; index < offset + length; ++index) {
170 value = value * 10 + (text[index] -
'0');
175 number(0, 4),
static_cast<unsigned>(number(4, 2)),
static_cast<unsigned>(number(6, 2))
177 return is_valid_date(date) ? std::optional<Date>(date) : std::nullopt;
180inline Decimal64 percent_to_ratio(std::string_view value) {
182 if (percent.scale() > std::numeric_limits<std::uint8_t>::max() - 2) {
183 throw std::out_of_range(
"percentage scale exceeds Decimal64 range");
186 auto coefficient = percent.coefficient();
187 auto scale =
static_cast<std::uint8_t
>(percent.scale() + 2);
188 while (scale > 0 && coefficient % 10 == 0) {
Decimal64()=default
Constructs zero.
static Decimal64 parse(std::string_view input)
Parses a decimal string exactly (no binary floating point).
http::response< http::string_body > HttpResponse
The HTTP response type the transport clients return.
@ RATE_LIMIT
The broker refused the request for its rate limit.
@ HTTP
The broker answered with an HTTP error status (http_status_).
@ PROTOCOL
The reply could not be read, or the broker declared a failure this client does not classify further: ...
bool is_valid_date(const Date &date)
Tests whether a date exists in the Gregorian calendar, in years 1 to 9999.