36inline constexpr std::array<Country, 2>
all_countries{Country::KR, Country::US};
51inline constexpr std::array<Venue, 4>
all_venues{Venue::UNSPECIFIED, Venue::KRX, Venue::NXT, Venue::COMBINED};
60 case Venue::UNSPECIFIED:
74 "all_venues must list every Venue"
130template <std::
size_t N>
142 explicit Code(std::string_view text) {
143 if (text.size() > N) {
144 throw std::invalid_argument(
"code is longer than its capacity");
146 for (std::size_t i = 0; i < text.size(); ++i) {
147 const char ch = text[i];
148 const bool valid = (ch >=
'A' && ch <=
'Z') || (ch >=
'0' && ch <=
'9') || ch ==
'_';
150 throw std::invalid_argument(
"code must use uppercase ASCII, digits, or underscore");
154 size_ =
static_cast<std::uint8_t
>(text.size());
163 static std::optional<Code>
try_make(std::string_view text) {
164 if (text.size() > N) {
168 for (std::size_t i = 0; i < text.size(); ++i) {
169 const char ch = text[i];
170 const bool valid = (ch >=
'A' && ch <=
'Z') || (ch >=
'0' && ch <=
'9') || ch ==
'_';
174 code.characters_[i] = ch;
176 code.size_ =
static_cast<std::uint8_t
>(text.size());
184 std::string_view
view() const noexcept {
185 return std::string_view(characters_.data(), size_);
192 std::string
str()
const {
return std::string(
view()); }
198 bool empty() const noexcept {
return size_ == 0; }
204 std::size_t
size() const noexcept {
return size_; }
210 static constexpr std::size_t
capacity() noexcept {
return N; }
229 std::array<char, N> characters_{};
230 std::uint8_t size_{0};
246 if (code.size() != code_.size()) {
247 throw std::invalid_argument(
"currency code must contain three characters");
249 for (std::size_t i = 0; i < code_.size(); ++i) {
250 const char ch = code[i];
251 if (ch <
'A' || ch >
'Z') {
252 throw std::invalid_argument(
"currency code must use uppercase ASCII characters");
274 std::string
str()
const {
return std::string(code_.begin(), code_.end()); }
283 return lhs.code_ == rhs.code_;
287 std::array<char, 3> code_{};
325 throw std::invalid_argument(
"decimal value is empty");
328 bool negative =
false;
329 std::size_t index = 0;
330 if (input.front() ==
'+' || input.front() ==
'-') {
331 negative = input.front() ==
'-';
334 if (index == input.size()) {
335 throw std::invalid_argument(
"decimal value has no digits");
338 std::uint64_t magnitude = 0;
339 std::uint8_t
scale = 0;
340 bool decimal_point =
false;
341 bool has_digit =
false;
342 const std::uint64_t limit = negative ?
343 static_cast<std::uint64_t
>(std::numeric_limits<std::int64_t>::max()) + 1ULL :
344 static_cast<std::uint64_t
>(std::numeric_limits<std::int64_t>::max());
346 for (; index < input.size(); ++index) {
347 const char ch = input[index];
350 throw std::invalid_argument(
"decimal value contains multiple points");
352 decimal_point =
true;
355 if (ch <
'0' || ch >
'9') {
356 throw std::invalid_argument(
"decimal value contains an invalid character");
359 const auto digit =
static_cast<std::uint64_t
>(ch -
'0');
360 if (magnitude > (limit - digit) / 10ULL) {
361 throw std::out_of_range(
"decimal value exceeds Decimal64 range");
363 magnitude = magnitude * 10ULL + digit;
365 if (
scale == std::numeric_limits<std::uint8_t>::max()) {
366 throw std::out_of_range(
"decimal scale exceeds Decimal64 range");
372 throw std::invalid_argument(
"decimal value has no digits");
375 while (
scale > 0 && magnitude % 10ULL == 0) {
383 magnitude ==
static_cast<std::uint64_t
>(std::numeric_limits<std::int64_t>::max()) + 1ULL
385 coefficient = std::numeric_limits<std::int64_t>::min();
387 coefficient =
static_cast<std::int64_t
>(magnitude);
399 std::int64_t
coefficient() const noexcept {
return coefficient_; }
405 std::uint8_t
scale() const noexcept {
return scale_; }
412 const bool negative = coefficient_ < 0;
413 std::uint64_t magnitude = negative ?
414 static_cast<std::uint64_t
>(-(coefficient_ + 1)) + 1ULL :
415 static_cast<std::uint64_t
>(coefficient_);
416 std::string digits = std::to_string(magnitude);
418 if (digits.size() <= scale_) {
419 digits.insert(0,
static_cast<std::size_t
>(scale_) - digits.size() + 1,
'0');
421 digits.insert(digits.size() - scale_, 1,
'.');
424 digits.insert(digits.begin(),
'-');
442 return lhs.coefficient_ == rhs.coefficient_ && lhs.scale_ == rhs.scale_;
446 std::int64_t coefficient_{0};
447 std::uint8_t scale_{0};
457 auto scale = value.
scale();
458 while (scale > 0 && coefficient % 10 == 0) {
465namespace decimal_detail {
474 const Decimal64& lhs,
475 const Decimal64& rhs,
480 scale = lhs.scale() > rhs.scale() ? lhs.scale() : rhs.scale();
481 left = lhs.coefficient();
482 right = rhs.coefficient();
483 for (std::uint8_t step = lhs.scale(); step < scale; ++step) {
484 if (left > std::numeric_limits<std::int64_t>::max() / 10 ||
485 left < std::numeric_limits<std::int64_t>::min() / 10) {
490 for (std::uint8_t step = rhs.scale(); step < scale; ++step) {
491 if (right > std::numeric_limits<std::int64_t>::max() / 10 ||
492 right < std::numeric_limits<std::int64_t>::min() / 10) {
510 std::int64_t left = 0;
511 std::int64_t right = 0;
512 std::uint8_t scale = 0;
513 if (!decimal_detail::align(lhs, rhs, left, right, scale)) {
516 if ((right > 0 && left > std::numeric_limits<std::int64_t>::max() - right) ||
517 (right < 0 && left < std::numeric_limits<std::int64_t>::min() - right)) {
531 std::int64_t left = 0;
532 std::int64_t right = 0;
533 std::uint8_t scale = 0;
534 if (!decimal_detail::align(lhs, rhs, left, right, scale)) {
537 if ((right < 0 && left > std::numeric_limits<std::int64_t>::max() + right) ||
538 (right > 0 && left < std::numeric_limits<std::int64_t>::min() + right)) {
551 if (coefficient % 2 == 0) {
554 if (value.
scale() == std::numeric_limits<std::uint8_t>::max() ||
555 coefficient > std::numeric_limits<std::int64_t>::max() / 5 ||
556 coefficient < std::numeric_limits<std::int64_t>::min() / 5) {
560 Decimal64(coefficient * 5,
static_cast<std::uint8_t
>(value.
scale() + 1))
574 const int scale =
static_cast<int>(lhs.
scale()) +
static_cast<int>(rhs.
scale());
575 if (scale > std::numeric_limits<std::uint8_t>::max()) {
578 if (left != 0 && right != 0) {
581 if (left > 0 ? (right > 0 ? left > std::numeric_limits<std::int64_t>::max() / right
582 : right < std::numeric_limits<std::int64_t>::min() / left)
583 : (right > 0 ? left < std::numeric_limits<std::int64_t>::min() / right
584 : left < std::numeric_limits<std::int64_t>::max() / right)) {
607 std::int64_t left = 0;
608 std::int64_t right = 0;
609 std::uint8_t common = 0;
610 if (!decimal_detail::align(lhs, rhs, left, right, common) || right == 0) {
615 for (std::uint8_t step = 0; step < scale; ++step) {
616 if (left > std::numeric_limits<std::int64_t>::max() / 10 ||
617 left < std::numeric_limits<std::int64_t>::min() / 10) {
622 if (left == std::numeric_limits<std::int64_t>::min() && right == -1) {
643 std::int64_t left = 0;
644 std::int64_t right = 0;
645 std::uint8_t scale = 0;
646 if (!decimal_detail::align(value, step, left, right, scale) || right <= 0) {
649 auto rest = left % right;
653 if (left < std::numeric_limits<std::int64_t>::min() + rest) {
666 std::int64_t left = 0;
667 std::int64_t right = 0;
668 std::uint8_t scale = 0;
669 if (decimal_detail::align(lhs, rhs, left, right, scale)) {
677 return left_negative;
815 bool has_value() const noexcept {
return std::holds_alternative<T>(value_); }
821 explicit operator bool() const noexcept {
return has_value(); }
828 T&
value() {
return std::get<T>(value_); }
835 const T&
value()
const {
return std::get<T>(value_); }
852 std::variant<T, BrokerError> value_;
970 bool treated =
false;
978 return covers && treated;
991 return "unestablished";
993 return "whole_holding";
997 return "unestablished";
1007 switch (treatment) {
1009 return "unestablished";
1013 return "unestablished";
1189 const std::int64_t shifted = days + 719468;
1190 const std::int64_t era = (shifted >= 0 ? shifted : shifted - 146096) / 146097;
1191 const std::int64_t day_of_era = shifted - era * 146097;
1192 const std::int64_t year_of_era =
1193 (day_of_era - day_of_era / 1460 + day_of_era / 36524 - day_of_era / 146096) / 365;
1194 const std::int64_t year = year_of_era + era * 400;
1195 const std::int64_t day_of_year =
1196 day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100);
1197 const std::int64_t shifted_month = (5 * day_of_year + 2) / 153;
1198 const std::int64_t day = day_of_year - (153 * shifted_month + 2) / 5 + 1;
1199 const std::int64_t month = shifted_month + (shifted_month < 10 ? 3 : -9);
1201 static_cast<int>(year + (month <= 2 ? 1 : 0)),
1202 static_cast<unsigned>(month),
1203 static_cast<unsigned>(day),
1216 static constexpr unsigned days_per_month[] = {
1217 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
1219 unsigned days = days_per_month[date.
month_ - 1];
1220 const bool leap = date.
year_ % 4 == 0 && (date.
year_ % 100 != 0 || date.
year_ % 400 == 0);
1221 if (date.
month_ == 2 && leap) {
1224 return date.
day_ >= 1 && date.
day_ <= days;
1261 if (
venue == Venue::UNSPECIFIED) {
1262 return std::nullopt;
1275 static std::optional<VenueKey>
raw(std::string_view code) {
1277 if (!stored || stored->empty()) {
1278 return std::nullopt;
1320 if (kind_ != asserted.kind_) {
1327 return venue_ == asserted.venue_;
1329 return raw_ == asserted.raw_;
1352 Venue venue_{Venue::UNSPECIFIED};
1366 return "normalized";
1419 return "broker_published";
1421 return "caller_stated";
1600 return rate.normalized() && rate.country_ == country;
1608 const auto pass = [&](
bool venue_specific) -> std::optional<RateSelection> {
1609 for (
const auto& rate :
rates_) {
1610 if (!in_market(rate)) {
1613 if (!(venue_specific ? rate.venue_.matches(asserted) : rate.venue_.is_any())) {
1616 switch (rate.applies_on(date)) {
1631 return std::nullopt;
1635 if (
const auto venue_row = pass(
true)) {
1638 if (
const auto neutral_row = pass(
false)) {
1639 return *neutral_row;
1650 for (
const auto& rate :
rates_) {
1655 const bool dated = rate.from_ || rate.until_ || rate.from_unreadable_ || rate.until_unreadable_;
1656 if (rate.normalized() && rate.country_ == country && dated) {
1689inline constexpr std::array<OrderSide, 2>
all_order_sides{OrderSide::BUY, OrderSide::SELL};
1696 "OrderSide is buy or sell: a short-sale mark or a cover belongs in its own field, not in OrderSide"
1754 case OrderType::UNKNOWN:
1778 case OrderType::UNKNOWN:
1809 TimeInForce::UNKNOWN,
1823 switch (condition) {
1826 case TimeInForce::UNKNOWN:
1861 OrderSession::UNKNOWN,
1901 case OrderStatus::PENDING_CANCEL:
1903 case OrderStatus::UNKNOWN:
1904 case OrderStatus::PENDING:
1905 case OrderStatus::PENDING_REPLACE:
1906 case OrderStatus::PARTIAL_FILLED:
1907 case OrderStatus::FILLED:
1908 case OrderStatus::CANCELED:
1909 case OrderStatus::REJECTED:
1910 case OrderStatus::CANCEL_REJECTED:
1911 case OrderStatus::REPLACE_REJECTED:
1912 case OrderStatus::REPLACED:
2110template <
typename Broker>
2121 static constexpr std::string_view
broker() noexcept {
return Broker::name; }
2128 std::string text(
broker());
2164template <
typename Broker>
2193 std::optional<Decimal64> price,
2194 std::optional<TimeInForce> time_in_force = std::nullopt,
2195 std::optional<OrderSession> session = std::nullopt,
2196 std::optional<Decimal64> trigger_price = std::nullopt
2199 price_(std::move(price)),
2279 return instruction.
resting_->trigger_price_;
2281 return std::nullopt;
2289template <
typename Broker>
2311template <
typename Broker>
2344template <
typename Broker>
2361template <
typename Broker>
2384template <
typename Broker>
2413template <
typename Broker>
2417 std::optional<Decimal64> quantity = std::nullopt
2420 instruction.
shape_ = std::move(shape);
2437template <
typename Broker>
2441 std::optional<Decimal64> quantity = std::nullopt
2444 shape.price_ = std::move(price);
2445 return replace_with(order, std::move(shape), std::move(quantity));
2454 const auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(value);
2455 const std::time_t raw = std::chrono::system_clock::to_time_t(seconds);
2458 gmtime_s(&utc, &raw);
2460 gmtime_r(&raw, &utc);
2463 const auto written = std::strftime(buffer,
sizeof(buffer),
"%Y-%m-%dT%H:%M:%SZ", &utc);
2464 return std::string(buffer, written);
2479 std::string_view value
2482 const auto digits = [value](std::size_t from, std::size_t count) {
2483 for (std::size_t index = from; index < from + count; ++index) {
2484 if (value[index] <
'0' || value[index] >
'9') {
2490 if (value.size() < 20 || !digits(0, 4) || value[4] !=
'-' || !digits(5, 2) || value[7] !=
'-' ||
2491 !digits(8, 2) || value[10] !=
'T' || !digits(11, 2) || value[13] !=
':' || !digits(14, 2) ||
2492 value[16] !=
':' || !digits(17, 2)) {
2493 return std::nullopt;
2495 int year, month, day, hour, minute, second;
2498 std::string(value).c_str(),
"%d-%d-%dT%d:%d:%d", &year, &month, &day, &hour, &minute, &second
2501 return std::nullopt;
2503 if (!
is_valid_date(
Date{year,
static_cast<unsigned>(month),
static_cast<unsigned>(day)}) ||
2504 hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 60) {
2505 return std::nullopt;
2509 std::size_t timezone_position = 19;
2510 if (value[19] ==
'.') {
2511 timezone_position = 20;
2512 while (timezone_position < value.size() && value[timezone_position] >=
'0' &&
2513 value[timezone_position] <=
'9') {
2514 ++timezone_position;
2516 if (timezone_position == 20) {
2517 return std::nullopt;
2520 if (timezone_position >= value.size()) {
2521 return std::nullopt;
2523 const char zone = value[timezone_position];
2525 if (timezone_position + 1 != value.size()) {
2526 return std::nullopt;
2528 }
else if (zone ==
'+' || zone ==
'-') {
2529 if (value.size() != timezone_position + 6 || !digits(timezone_position + 1, 2) ||
2530 value[timezone_position + 3] !=
':' || !digits(timezone_position + 4, 2)) {
2531 return std::nullopt;
2534 return std::nullopt;
2538 time.tm_year = year - 1900;
2539 time.tm_mon = month - 1;
2541 time.tm_hour = hour;
2542 time.tm_min = minute;
2543 time.tm_sec = second;
2546 std::time_t utc = _mkgmtime(&time);
2548 std::time_t utc = timegm(&time);
2550 if (utc ==
static_cast<std::time_t
>(-1)) {
2551 return std::nullopt;
2554 if (value[timezone_position] !=
'Z') {
2555 int offset_hour, offset_minute;
2558 std::string(value.substr(timezone_position + 1)).c_str(),
2564 return std::nullopt;
2566 if (offset_hour > 23 || offset_minute > 59) {
2567 return std::nullopt;
2569 const int offset = offset_hour * 3600 + offset_minute * 60;
2570 utc += value[timezone_position] ==
'+' ? -offset : offset;
2573 auto result = std::chrono::system_clock::from_time_t(utc);
2574 const auto dot = value.find(
'.', 19);
2575 if (dot != std::string_view::npos && dot < timezone_position) {
2576 std::uint32_t milliseconds = 0;
2577 std::uint32_t factor = 100;
2578 for (std::size_t index = dot + 1; index < timezone_position && factor > 0; ++index) {
2579 if (value[index] <
'0' || value[index] >
'9') {
2580 return std::nullopt;
2582 milliseconds +=
static_cast<std::uint32_t
>(value[index] -
'0') * factor;
2585 result += std::chrono::milliseconds(milliseconds);
2598template <
typename Broker>
2606 std::size_t seed = hash<std::string>{}(ref.primary_);
2607 seed ^= hash<std::string>{}(ref.secondary_) + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
A short uppercase code (letters, digits, _), stored inline without allocation.
bool empty() const noexcept
Tests whether the code is empty.
friend bool operator!=(const Code &lhs, const Code &rhs)
Compares two codes for inequality.
Code()=default
Constructs an empty code.
static std::optional< Code > try_make(std::string_view text)
Makes a code without throwing.
friend bool operator==(const Code &lhs, const Code &rhs)
Compares two codes' characters.
std::size_t size() const noexcept
Returns the number of characters.
static constexpr std::size_t capacity() noexcept
Returns the maximum number of characters.
std::string str() const
Returns the code as a string.
Code(std::string_view text)
Constructs a code.
std::string_view view() const noexcept
Returns the code's characters.
A three-letter currency code, such as KRW or USD (ISO 4217 style).
static Currency usd()
Returns United States dollars.
Currency()
Constructs Korean won.
static Currency krw()
Returns Korean won.
friend bool operator==(const Currency &lhs, const Currency &rhs)
Compares two currencies.
Currency(std::string_view code)
Constructs a currency from its code.
std::string str() const
Returns the code.
An exact signed decimal: an int64 coefficient times 10^-scale.
std::uint8_t scale() const noexcept
Returns the scale.
friend bool operator==(const Decimal64 &lhs, const Decimal64 &rhs)
Compares the coefficient and the scale.
std::int64_t coefficient() const noexcept
Returns the coefficient.
Decimal64(std::int64_t coefficient, std::uint8_t scale)
Constructs coefficient * 10^-scale, keeping the scale as given.
std::string str() const
Formats the value as a decimal string.
Decimal64()=default
Constructs zero.
static Decimal64 parse(std::string_view input)
Parses a decimal string exactly (no binary floating point).
Result(T value)
Constructs a successful result.
const BrokerError & error() const
Returns the error.
BrokerError & error()
Returns the error.
const T & value() const
Returns the value.
bool has_value() const noexcept
Tests whether the result holds a value.
T & value()
Returns the value.
Result(BrokerError error)
Constructs a failed result.
Which venue a commission rate applies to: any venue, a normalized Venue, or a raw code.
friend bool operator!=(const VenueKey &lhs, const VenueKey &rhs) noexcept
Compares two keys for inequality.
static VenueKey any() noexcept
Returns the key for any venue.
bool matches(const VenueKey &asserted) const noexcept
Tests whether two keys name the same venue, exactly in kind and value.
static std::optional< VenueKey > raw(std::string_view code)
Makes a key for a venue code the library does not normalize.
friend bool operator==(const VenueKey &lhs, const VenueKey &rhs) noexcept
Compares kind and value; same as matches.
static std::optional< VenueKey > normalized(Venue venue)
Makes a key for a normalized venue.
bool is_any() const noexcept
Tests whether the key is for any venue.
const Code< 8 > & raw_code() const noexcept
Returns the raw venue code.
VenueKey()=default
Constructs the key for any venue.
VenueKeyKind kind() const noexcept
Returns the key's kind.
Venue venue() const noexcept
Returns the normalized venue.
A compile-time check that a list names every value of an enum, and a parser over the list.
The library: broker-neutral types (core), and the modules below.
OrderStatus
Where an order stands in its lifecycle.
std::optional< Decimal64 > decimal_floor_to_multiple(const Decimal64 &value, const Decimal64 &step)
Rounds down to a multiple of a step (toward negative infinity).
SellableSource
Where the library gets a broker's sellable quantity.
@ HOLDINGS
On the holdings snapshot, at no extra request.
@ INQUIRY
From a per-instrument inquiry, made only where the snapshot has no figure.
@ NONE
No per-instrument inquiry: any figure comes on the holdings rows (Kiwoom), or there is none.
AmendInstruction reprice(const WorkingOrder< Broker > &order, Decimal64 price, std::optional< Decimal64 > quantity=std::nullopt)
Makes an instruction changing only a working order's price.
VenueKeyKind
Which kind of venue a VenueKey names.
@ NORMALIZED
A Venue the library normalizes.
@ RAW
A broker venue code the library does not normalize, kept verbatim.
@ ANY
Any venue (a venue-neutral rate).
constexpr std::array< Venue, 4 > all_venues
Every Venue, in declaration order.
CommissionOrigin
Where a commission schedule's numbers came from.
@ CALLER_STATED
The caller stated them, from a fee page or their own agreement.
@ BROKER_PUBLISHED
The broker published them, through an inquiry the library made.
std::optional< std::chrono::system_clock::time_point > parse_iso8601_timestamp(std::string_view value)
Parses an ISO 8601 timestamp, YYYY-MM-DDTHH:MM:SS[.fraction] followed by Z or a +hh:mm/-hh:mm offset.
constexpr bool commission_origin_names_tariff(CommissionOrigin origin) noexcept
Tests whether a schedule of this origin must name the tariff it was read from.
constexpr bool lists_every_value(const std::array< Enum, N > &values, IsEnumerator is_enumerator) noexcept
Tests whether a list holds every value of an enum exactly once, in any order.
std::optional< Decimal64 > decimal_add(const Decimal64 &lhs, const Decimal64 &rhs)
Adds two decimals exactly.
constexpr std::string_view commission_origin_name(CommissionOrigin origin) noexcept
Returns a lowercase name for an origin, such as "caller_stated", for reports.
ErrorCategory
The kind of failure a BrokerError reports.
@ 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: ...
@ CANCELLED
The operation was cancelled, such as by closing the client or a local deadline.
@ AUTHENTICATION
The credentials or token were refused.
@ TRANSPORT
The connection failed (DNS, connect, TLS, write or read).
@ INVALID_ARGUMENT
The request was invalid, refused by the client before sending or by the broker.
OrderType
How an order's price is decided (its price basis).
@ LIMIT
지정가: fills at the stated price or better.
@ BEST_LIMIT
최유리지정가: priced at the best quote on the opposite side.
@ PRIMARY_LIMIT
최우선지정가: priced at the best quote on the order's own side.
@ CONDITIONAL_LIMIT
조건부지정가: a limit that becomes a market order at the close.
@ MID_PRICE
중간가: priced at the midpoint of the spread.
@ STOP_LIMIT
스톱지정가: a limit released when a trigger price is reached.
@ CLOSING_PRICE
시간외종가: priced at a session's closing price.
@ MARKET
시장가: fills at whatever the book offers.
constexpr bool carries_trigger_price(OrderType type) noexcept
Tests whether an order type takes a trigger price.
RateApplicability
Whether a commission rate applies on a date.
@ UNREADABLE
Unknown: no readable bound excludes the date, but a bound could not be read.
@ YES
Every bound was readable, and they cover the date.
@ NO
A readable date bound puts the date outside the rate's window.
constexpr bool carries_caller_price(OrderType type) noexcept
Tests whether an order type takes a price from the caller.
std::optional< Decimal64 > decimal_halve(const Decimal64 &value)
Halves a decimal exactly, adding a decimal place when needed.
AmendInstruction cancel_all(const WorkingOrder< Broker > &order)
Makes an instruction cancelling all that remains of an order.
constexpr std::array< OrderType, 9 > all_order_types
Every OrderType, in declaration order.
constexpr std::string_view sellable_coverage_name(SellableBasis::Coverage coverage) noexcept
Returns a lowercase name for a coverage, such as "whole_holding", for logs.
constexpr bool order_status_cancel_pending(OrderStatus status) noexcept
Tests whether a cancellation of an order in this status is already on its way, so asking again can on...
AmendInstruction cancel_quantity(const WorkingOrder< Broker > &order, Decimal64 quantity)
Makes an instruction cancelling part of an order.
constexpr bool error_category_is_rate_limit(ErrorCategory category) noexcept
Tests whether an error of this category is a broker asking the caller to slow down,...
@ UNREADABLE
No readable date rules it out, but an unreadable date might.
constexpr std::string_view sellable_resting_name(SellableBasis::RestingTreatment treatment) noexcept
Returns a lowercase name for a treatment, such as "deduct", for logs.
Country
A country (market) the library supports.
Decimal64 decimal_normalized(const Decimal64 &value)
Removes trailing fractional zeros, so equal numbers compare equal with ==.
OrderSession
The trading session an order is submitted into.
@ OPENING_AUCTION
The opening single-price auction (시가단일가; Toss OPG).
@ AFTER_HOURS
After the close, at that session's close (장후 시간외).
@ CLOSING_AUCTION
The closing auction (Toss CLS, a US limit-on-close).
@ REGULAR
The continuous regular session.
@ AFTER_MARKET
KRX's after-market session (애프터마켓).
@ AFTER_HOURS_AUCTION
The after-hours single-price auction (시간외 단일가).
@ PRE_MARKET
Before the open, at the previous session's close (장전 시간외).
constexpr bool commission_origin_is_broker(CommissionOrigin origin) noexcept
Tests whether commission rates of this origin came from the broker.
constexpr std::string_view venue_name(Venue venue) noexcept
Returns a display name for a venue, such as "KRX".
constexpr std::array< OrderSide, 2 > all_order_sides
Every OrderSide, in declaration order.
AmendInstruction replace_with(const WorkingOrder< Broker > &order, OrderShape shape, std::optional< Decimal64 > quantity=std::nullopt)
Makes an instruction changing a working order to a new shape.
std::optional< Decimal64 > decimal_subtract(const Decimal64 &lhs, const Decimal64 &rhs)
Subtracts two decimals exactly.
std::optional< Decimal64 > decimal_divide(const Decimal64 &lhs, const Decimal64 &rhs, std::uint8_t scale)
Divides two decimals, truncating toward zero at a given scale.
constexpr std::array< OrderSession, 9 > all_order_sessions
Every OrderSession, in declaration order.
constexpr bool time_in_force_expires_at_close(TimeInForce condition) noexcept
Tests whether an order with this condition expires at the session's close, so its filled quantity is ...
Venue
A trading venue: an exchange, or the combined view across exchanges.
constexpr std::array< TimeInForce, 4 > all_times_in_force
Every TimeInForce, in declaration order.
constexpr std::string_view venue_key_kind_name(VenueKeyKind kind) noexcept
Returns a lowercase name for a key kind, such as "raw", for reports.
bool decimal_less(const Decimal64 &lhs, const Decimal64 &rhs)
Compares two decimals numerically, whatever their scales.
std::optional< Decimal64 > decimal_multiply(const Decimal64 &lhs, const Decimal64 &rhs)
Multiplies two decimals exactly; the scales add.
Date date_from_epoch_days(std::int64_t days)
Converts a day count since 1970-01-01 to a Gregorian date.
constexpr std::string_view sellable_source_name(SellableSource source) noexcept
Returns a lowercase name for a source, such as "inquiry", for logs.
bool is_valid_date(const Date &date)
Tests whether a date exists in the Gregorian calendar, in years 1 to 9999.
TimeInForce
How long an order stays valid and what happens to an unfilled remainder.
@ FILL_OR_KILL
Fills entirely and immediately, or not at all.
@ DAY
Rests until the session ends; an unfilled remainder is cancelled then.
@ IMMEDIATE_OR_CANCEL
Fills what it can immediately and cancels the remainder.
OrderSide
The side of an order: its economic direction, buy or sell.
std::optional< Decimal64 > amended_trigger_price(const AmendInstruction &instruction)
Returns the trigger price an amendment leaves the order with.
std::string format_iso8601_utc(std::chrono::system_clock::time_point value)
Formats an instant as an ISO 8601 timestamp in UTC, such as 2026-10-06T00:30:00Z.
constexpr std::array< Country, 2 > all_countries
Every Country, in declaration order.
OrderShape resting_shape(const WorkingOrder< Broker > &order)
Returns a working order's current shape.
A modification or cancellation of a working order.
Venue venue_
The venue the order rests at, for a broker whose amendment needs it (KIS).
bool confirm_high_value_
Confirms a high-value amendment, where the broker asks for that confirmation.
std::optional< OrderShape > resting_
The shape the order has now, or unset if unknown.
std::optional< Decimal64 > quantity_
The quantity affected; never substituted if a broker needs one and it is missing.
OrderShape shape_
The shape the order should have afterwards.
bool all_remaining_
Whether the whole remaining quantity is affected, rather than quantity_.
The error a broker or transport operation returns.
std::string request_id_
The broker's request id for this call, if it gave one.
int http_status_
The HTTP status of the reply: set for an HTTP error and for a reply whose body is not a JSON object....
std::string message_
A human-readable description, for logs; do not parse it.
ErrorCategory category_
The kind of failure; the field to branch on.
std::string broker_code_
The broker's own error code, verbatim (for example KIS's EGW00201).
std::optional< int > rate_limit_
The rate limit the broker says it enforces, in requests per second, for a rate-limit error.
std::optional< std::chrono::seconds > retry_after_
How long the broker asked the caller to wait (Retry-After), for a rate-limit error.
bool reached_broker_
Whether a request was sent before this error.
What the broker says may be spent, in one currency.
Money cash_
The amount that may be spent.
std::chrono::system_clock::time_point received_at_
When the figure was received, on this machine's clock (brokers do not timestamp it).
One commission rate, the market and venue it applies to, and the dates it applies on.
bool until_unreadable_
Whether the source stated an end date that could not be read (so until_ is empty).
bool normalized() const noexcept
Tests whether the rate's market is a normalized Country.
Country country_
The market the rate applies to; meaningless when other_ is set.
RateApplicability applies_on(const Date &date) const
Tests whether the rate applies on a date.
std::optional< Date > from_
The first date the rate applies on; empty is open-ended unless from_unreadable_ is set.
VenueKey venue_
The venue the rate applies to.
std::optional< Date > until_
The last date the rate applies on; empty is open-ended unless until_unreadable_ is set.
bool excluded_on(const Date &date) const
Tests whether a readable bound puts a date outside the rate's window.
Decimal64 rate_
The broker's commission, as a fraction of traded value (0.00015, not 0.015).
std::optional< Decimal64 > institution_rate_
The institution fee (유관기관제비용), as a fraction of traded value, if stated separately.
bool from_unreadable_
Whether the source stated a start date that could not be read (so from_ is empty).
Code< 16 > other_
The broker's market code, when the library does not normalize that market.
The result of rate_for: a rate, or why there is none.
bool bounds_unreadable_
Whether an unreadable date window is why no rate was returned.
std::optional< CommissionRate > rate_
The rate in force, if one definitely is.
The commission rates a broker published for an account, or a caller stated.
RateSelection rate_for(Country country, const VenueKey &asserted, const Date &date) const
Finds the rate in force for a market and venue on a date.
RateSelection rate_for(Country country, const Date &date) const
Finds the venue-neutral rate in force for a market on a date.
std::vector< CommissionRate > rates_
The rate rows.
CommissionOrigin origin_
Whether a broker published the rates or a caller stated them.
std::chrono::system_clock::time_point received_at_
When the schedule was received, on this machine's clock.
std::size_t unreadable_bounds_
How many rows have a date bound that could not be read.
std::size_t dropped_
How many broker rows were skipped because their market code could not be stored.
bool any_dated_rate_for(Country country) const
Tests whether any rate for a market has a date bound, readable or not.
std::string tariff_
Which tariff the rates are, in the source's own words.
A calendar date with no time of day or time zone.
friend bool operator==(const Date &lhs, const Date &rhs)
Compares two dates.
friend bool operator<(const Date &lhs, const Date &rhs)
Orders dates chronologically.
unsigned month_
The month, 1-12; zero means unset.
unsigned day_
The day of the month, 1-31; zero means unset.
One position row in a holdings snapshot.
std::string name_
The instrument's name as the broker spells it.
Money unrealized_profit_loss_
The unrealized profit or loss, as the broker reported it.
Money purchase_value_
The cost basis, as the broker computed it.
Decimal64 average_purchase_price_
The broker's average purchase price.
std::optional< Decimal64 > sellable_quantity_
How much the broker says may be sold now.
Decimal64 unrealized_return_
The broker's return figure, as a fraction (0.05 is 5%).
Decimal64 quantity_
The quantity in this row.
InstrumentId instrument_
The instrument held (brokers report holdings without a trading venue).
Money market_value_
The market value, as the broker computed it.
Decimal64 last_price_
The price the broker valued the position at.
std::optional< SellableBasis > sellable_basis_
What this row's sellable_quantity_ covers, when the figure came from a per-instrument inquiry rather ...
An account's positions and per-currency totals at one point in time.
std::chrono::system_clock::time_point received_at_
When this process received the snapshot (brokers do not timestamp holdings).
std::vector< HoldingsTotals > totals_
One totals row per currency held.
SellableBasis sellable_basis_
What the rows' sellable_quantity_ figures cover (unless a row carries its own).
std::vector< HoldingPosition > positions_
Every position row; one instrument may appear in more than one.
The broker's holdings totals for one currency.
Decimal64 unrealized_profit_loss_
The broker's total unrealized profit or loss in currency_, from its summary.
Decimal64 market_value_
The broker's total market value in currency_, from its summary.
Decimal64 purchase_value_
The broker's total cost basis in currency_.
Currency currency_
The currency; there is one totals row per currency, never converted.
Identifies an instrument: its country, venue and symbol.
Country country_
The listing country, which selects the symbol convention and the price grid.
friend bool operator==(const InstrumentId &lhs, const InstrumentId &rhs)
Compares country, venue and symbol.
Venue venue_
The venue that is part of the identity (two venues are two instruments).
std::string symbol_
The symbol as the venue spells it, such as "005930".
friend bool operator<(const InstrumentId &lhs, const InstrumentId &rhs)
Orders identities by country, venue, then symbol, so they can key a map.
An exact amount of money in one currency.
Currency currency_
The currency; the library never adds amounts in different currencies.
friend bool operator==(const Money &lhs, const Money &rhs)
Compares amount (with Decimal64::operator==) and currency.
Decimal64 amount_
The amount.
The broker's acknowledgement that it accepted an instruction.
std::optional< std::string > client_order_id_
The idempotency key, if the broker echoed one.
std::chrono::system_clock::time_point accepted_at_
When the acknowledgement was received.
std::string order_id_
The order number the broker assigned.
What has filled on one order, and what it cost.
std::optional< Money > commission_
Commission charged so far, if the broker reports it per order.
Decimal64 filled_quantity_
The quantity filled so far; zero if nothing has filled. Always present.
std::optional< Decimal64 > average_filled_price_
The average fill price, only where the broker supplies it.
std::optional< std::chrono::system_clock::time_point > filled_at_
When the most recent fill happened, if the broker timestamps it.
std::optional< Money > filled_amount_
The value of the fills, as the broker computed it.
std::optional< Money > tax_
Transaction tax charged so far, if the broker reports it per order.
std::optional< Date > settlement_date_
The settlement date the broker stated.
The filter for an order-history inquiry.
std::optional< std::string > symbol_
Only this symbol, where the broker supports the filter.
std::optional< int > limit_
The most rows to return, where the broker supports a limit.
std::optional< std::string > cursor_
A previous page's next_cursor_, passed back unchanged.
std::optional< Date > from_
The first date to include; required where BrokerCaps::history_requires_range.
std::optional< Date > to_
The last date to include.
One page of an order listing.
std::vector< OrderSnapshot > orders_
The rows, in the broker's order.
std::optional< std::string > next_cursor_
The cursor for the next page, to pass back unchanged; empty if there is none.
std::chrono::system_clock::time_point received_at_
When the page was received, on this machine's clock.
A broker's reference to one order, used to amend or cancel it.
friend bool operator==(const OrderRef &lhs, const OrderRef &rhs)
Compares two references.
friend bool operator!=(const OrderRef &lhs, const OrderRef &rhs)
Compares two references for inequality.
std::string str() const
Formats the reference for logs, as broker:primary[:secondary].
static constexpr std::string_view broker() noexcept
Returns the issuing broker's name.
An order to place, in broker-neutral form.
std::optional< std::string > client_order_id_
An idempotency key, if the broker offers one; never generated by the library.
Decimal64 quantity_
The quantity, in the instrument's units (shares).
OrderType type_
The price basis: limit, market, best-limit, and so on.
InstrumentId instrument_
The instrument; its venue_ is identity, and routing is this struct's venue_.
Venue venue_
Where to route the order, if the broker offers a choice.
std::optional< Decimal64 > price_
The price; required when carries_caller_price(type_), refused otherwise.
OrderSide side_
Buy or sell.
OrderSession session_
The trading session.
TimeInForce time_in_force_
How long the order stays valid.
bool confirm_high_value_
Confirms a high-value order, where the broker asks for that confirmation.
std::optional< Decimal64 > trigger_price_
The trigger price; required for STOP_LIMIT, refused otherwise.
An order's type, price, trigger, time in force and session, as an amendment states them.
std::optional< TimeInForce > time_in_force_
The time in force; unset means "keep the resting order's".
std::optional< Decimal64 > price_
The price, for a basis that takes one.
std::optional< OrderSession > session_
The session; unset means "keep the resting order's".
OrderShape()=default
Constructs a limit shape with nothing else stated.
OrderShape(OrderType type, std::optional< Decimal64 > price, std::optional< TimeInForce > time_in_force=std::nullopt, std::optional< OrderSession > session=std::nullopt, std::optional< Decimal64 > trigger_price=std::nullopt)
Constructs a shape, stating only what the caller means to set.
std::optional< Decimal64 > trigger_price_
The trigger price, for STOP_LIMIT.
OrderType type_
The price basis.
The state of one order, as a broker listing reported it.
Venue venue_
Where the broker says the order was routed; UNSPECIFIED if it does not say.
OrderSide side_
Buy or sell.
OrderStatus status_
The status; UNKNOWN where the broker publishes none.
OrderSession session_
The session; UNKNOWN if the broker's code was not recognized.
std::optional< std::chrono::system_clock::time_point > canceled_at_
When the order was cancelled, if the broker reports it.
OrderType type_
The price basis; UNKNOWN if the broker's code was not recognized.
OrderExecution execution_
What has filled and what it cost; see OrderExecution before testing a fill.
std::string name_
The instrument's name as the broker spells it.
std::optional< Decimal64 > trigger_price_
The trigger price, whenever the broker reports a positive one.
bool ordered_date_assumed_
Whether the broker stated only the time of day, and the date in ordered_at_ is the day the adapter re...
Currency currency_
The currency of the row's prices and amounts.
Decimal64 quantity_
The quantity ordered; the remainder is this minus execution_.filled_quantity_.
std::string order_id_
The broker's order number.
std::optional< std::chrono::system_clock::time_point > ordered_at_
When the broker recorded the order, if it gives a time. KIS's real-server working orders have none; o...
std::optional< Decimal64 > order_amount_
The money an amount-based order committed (Toss US market orders), if any.
InstrumentId instrument_
The instrument (identity venue; the routing venue is venue_).
TimeInForce time_in_force_
The time in force; UNKNOWN if the broker's code was not recognized.
Code< 8 > shape_code_
The broker's own order-type code, verbatim (KIS two digits, Toss three letters).
std::optional< Decimal64 > price_
The limit price, if the order has one.
An accepted order: the acknowledgement and the reference to address it with.
OrderRef< BrokerTag > ref_
A quote: the last traded price of an instrument at a point in time.
Currency currency_
The currency of last_price_.
Decimal64 last_price_
The last traded price, as the broker reported it.
std::chrono::system_clock::time_point received_at_
When this process received the quote, on this machine's clock.
std::optional< TopOfBook > top_of_book_
The best bid and offer.
std::optional< std::chrono::system_clock::time_point > exchange_time_
When the exchange says the quote was true, if the broker gave a timestamp.
InstrumentId instrument_
The instrument.
std::optional< Decimal64 > volume_
The session's cumulative traded volume, if the broker gave it.
What a sellable quantity covers, and how RiskGuard must treat resting sells against it.
bool verified_
Whether the basis was confirmed from a real broker response, rather than stated as a policy.
constexpr bool is_enforceable() const noexcept
Tests whether RiskGuard may check a sell against a figure with this basis.
RestingTreatment resting_
How resting sells are treated; unestablished by default.
Coverage
Whether the figure covers the whole holding.
@ WHOLE_HOLDING
Every share held of the instrument, whatever venue or lot.
@ UNESTABLISHED
Not established; RiskGuard refuses.
@ PARTIAL
Only part of the holding, such as one venue's shares; RiskGuard refuses, reporting SELLABLE_BASIS_UNE...
Coverage coverage_
What the figure covers; unestablished by default.
RestingTreatment
How RiskGuard treats resting sell orders against the figure.
@ DEDUCT
Subtract every resting sell from the figure.
@ UNESTABLISHED
Not stated; RiskGuard refuses.
The best bid and offer, with the size at each. An absent field was not stated, never zero.
std::optional< Decimal64 > bid_price_
The best bid price.
std::optional< Decimal64 > ask_price_
The best offer price.
std::optional< Decimal64 > bid_quantity_
The size at the best bid.
std::optional< Decimal64 > ask_quantity_
The size at the best offer.
An order still open to modification or cancellation.
OrderRef< BrokerTag > ref_
std::optional< Decimal64 > amendable_quantity_
The orders open to modification or cancellation at one point in time.
std::vector< WorkingOrder< BrokerTag > > orders_
std::chrono::system_clock::time_point received_at_
std::size_t operator()(const algotradelib::OrderRef< Broker > &ref) const noexcept
Hashes both parts of the reference.