3#include "./execution.hpp"
4#include "./journal.hpp"
6#include <algotradelib/core>
16#include <unordered_map>
75 return "needs_operator";
120namespace reconcile_detail {
134inline bool same_value(
135 const std::optional<Decimal64>& lhs,
const std::optional<Decimal64>& rhs
137 if (!lhs.has_value() || !rhs.has_value()) {
138 return lhs.has_value() == rhs.has_value();
140 return same_value(*lhs, *rhs);
168 std::chrono::system_clock::time_point now,
175namespace shape_detail {
178template <
typename Enum>
179inline constexpr bool both_known(Enum left, Enum right)
noexcept {
180 return left != Enum::UNKNOWN && right != Enum::UNKNOWN;
258template <
typename Enum>
260 if (row_value == Enum::UNKNOWN || entry_value == Enum::UNKNOWN) {
282 const std::optional<Decimal64>& row_trigger,
283 const std::optional<Decimal64>& entry_trigger
288 if (!entry_trigger) {
291 return reconcile_detail::same_value(*row_trigger, *entry_trigger) ?
345 if (*asked == Venue::UNSPECIFIED) {
349 if (row.
venue_ == Venue::UNSPECIFIED) {
371 const auto& entry = instruction.
entry_;
375 if (row.
side_ != entry.side_) {
382 if (!shape_detail::both_known(row.
type_, entry.type_)) {
385 if (row.
type_ != entry.type_) {
388 if (!reconcile_detail::same_value(row.
quantity_, entry.quantity_)) {
391 if (!reconcile_detail::same_value(row.
price_, entry.price_)) {
395 for (
const auto evidence : {
426 const auto& entry = instruction.
entry_;
431 for (
const auto evidence : {
444 const bool anything_silent =
451 if (!anything_silent) {
460 if (!reconcile_detail::same_value(row.
quantity_, entry.quantity_)) {
468 if (!reconcile_detail::same_value(row.
price_, entry.price_)) {
487 if (by_order_id && entry.
order_id_.empty()) {
488 return "a cancellation was journalled without the order id it names, so no row can be "
489 "identified as its target; an operator must say what became of it";
499 if (!by_order_id && entry.
type_ != OrderType::UNKNOWN) {
501 if (priced && !entry.
price_) {
502 return "a price-bearing order was journalled without its price, so no row can be matched "
503 "on it; an operator must say what became of it";
505 if (!priced && entry.
price_) {
506 return "an order whose price the market sets was journalled carrying one, so no row can "
507 "be matched on it; an operator must say what became of it";
513 return "a stop-limit order was journalled without its trigger price, so no row can be "
514 "matched on it; an operator must say what became of it";
517 return "an order that takes no trigger price was journalled carrying one, so no row can be "
518 "matched on it; an operator must say what became of it";
536 std::vector<OpenInstruction> found;
537 std::unordered_map<std::string, std::size_t> found_at;
538 std::vector<std::string> refused;
539 for (
const auto& entry : replay.
entries_) {
543 auto known = found_at.find(entry.id_);
544 if (known == found_at.end()) {
549 known = found_at.emplace(entry.id_, found.size()).first;
550 found.push_back(std::move(placement));
552 auto& placement = found[known->second];
554 refused.push_back(entry.id_);
557 placement.closed_at_ = entry.recorded_at_;
560 placement.entry_.order_id_ = entry.order_id_;
565 found.begin(), found.end(),
567 return placement.entry_.order_id_.empty() ||
568 std::find(refused.begin(), refused.end(), placement.entry_.id_) != refused.end();
591 const std::vector<OpenInstruction>& recorded,
597 for (
const auto& other : recorded) {
598 if (other.entry_.id_ == instruction.
entry_.
id_ || other.entry_.order_id_ != row.
order_id_) {
628 const std::vector<OrderSnapshot>& rows,
629 const std::vector<OpenInstruction>& recorded,
630 std::chrono::system_clock::time_point now,
635 match.entry_id_ = instruction.
entry_.
id_;
649 match.detail_ = reason;
658 "recovered from a damaged journal line; only an operator can resolve it" :
663 const OrderSnapshot* found =
nullptr;
664 std::size_t unreadable = 0;
665 std::size_t claimed = 0;
666 const OrderSnapshot* claimed_row =
nullptr;
670 for (
const auto& row : rows) {
677 for (
const auto& row : rows) {
683 if (!fits && !might_fit) {
688 if (
const auto* owner =
recorded_owner(instruction, row, recorded, window)) {
709 const auto others = match.candidates_ + unreadable;
710 match.candidates_ = others + claimed;
711 match.detail_ =
"order " + claimed_row->order_id_ +
" in the broker's listing fits this instruction, and the "
712 "journal records it for another placement (" + claimant->entry_.id_ +
")" +
713 (claimed > 1 ?
", as it does " + std::to_string(claimed - 1) +
" more such row(s)" : std::string()) +
714 (others > 0 ?
"; " + std::to_string(others) +
" other row(s) fit as well" : std::string()) +
715 "; nothing was resolved";
718 if (match.candidates_ > 1) {
720 match.detail_ = std::to_string(match.candidates_) +
721 " orders in the broker's listing could be this instruction; nothing was resolved";
724 if (unreadable > 0) {
736 match.candidates_ += unreadable;
737 match.order_id_.clear();
741 match.detail_ = std::to_string(unreadable) +
742 " order(s) in the broker's listing match this instruction on instrument, side, quantity "
743 "and time, but this library cannot confirm which order they are: an order code it does "
744 "not recognize, a route nobody recorded, or both" +
745 (found !=
nullptr ?
", and another row matches it fully" :
"") +
746 "; nothing was resolved. Each row carries the broker's own code as shape_code_ and its "
750 if (match.candidates_ == 1) {
752 match.order_id_ = found->order_id_;
753 match.detail_ = by_order_id ?
754 "the order named by the cancellation is still in the broker's listing" :
755 "the broker's listing carries this order";
761 "no matching order yet, and not enough time has passed for the listing to show one";
765 match.detail_ = by_order_id ?
766 "the order named by the cancellation is no longer in the broker's listing" :
767 "no matching order was found among the rows read, within the matching window";
786 const std::vector<OrderSnapshot>& rows,
787 std::chrono::system_clock::time_point now,
816 std::vector<OrderSnapshot>& rows,
818 const std::vector<OpenInstruction>& matching,
822 std::vector<OpenInstruction> others;
823 std::unordered_map<std::string, std::size_t> other_of;
824 std::vector<std::string> refused;
825 for (
const auto& entry : replay.
entries_) {
829 const bool on_an_instrument_matched = std::any_of(
830 matching.begin(), matching.end(),
831 [&](
const OpenInstruction& instruction) { return instruction.entry_.instrument_ == entry.instrument_; }
833 const bool being_matched = std::any_of(matching.begin(), matching.end(), [&](
const OpenInstruction& instruction) {
834 return instruction.entry_.id_ == entry.id_;
836 if (!on_an_instrument_matched || being_matched) {
840 refused.push_back(entry.id_);
842 auto known = other_of.find(entry.id_);
843 if (known == other_of.end()) {
847 known = other_of.emplace(entry.id_, others.size()).first;
848 others.push_back(std::move(other));
850 auto& other = others[known->second];
852 other.closed_at_ = entry.recorded_at_;
857 rows.begin(), rows.end(),
858 [&](
const OrderSnapshot& row) {
859 return std::any_of(others.begin(), others.end(), [&](const OpenInstruction& other) {
860 return std::find(refused.begin(), refused.end(), other.entry_.id_) == refused.end() &&
861 (row_corresponds(other, row, window) || row_might_correspond(other, row, window));
904 std::chrono::system_clock::time_point now
906 auto entry = instruction.
entry_;
909 entry.recorded_at_ = now;
910 entry.requires_operator_ =
false;
912 entry.detail_ =
"resolved by reading the broker's order list: " + match.
detail_;
936template <
typename Journal>
938 const std::vector<OpenInstruction>& open,
939 const std::vector<OrderSnapshot>& rows,
941 std::chrono::system_clock::time_point now,
945 std::string_view unplaced_note = {}
947 ReconcileReport report;
948 report.matches_.reserve(open.size());
949 const auto recorded = recorded_placements(replay);
951 std::vector<InstrumentId> undecided;
952 std::vector<InstrumentId> seen;
953 for (
const auto& instruction : open) {
954 auto match = match_against(instruction, rows, recorded, now, window);
955 const auto instrument = match.instrument_;
956 if (match.verdict_ == MatchVerdict::AMBIGUOUS) {
957 report.ambiguous_ =
true;
959 if (match.verdict_ == MatchVerdict::NOT_PLACED && instruction.
entry_.
action_ == JournalAction::PLACE) {
960 match.detail_ += unplaced_note;
970 report.unrecorded_ =
true;
971 undecided.push_back(instrument);
972 match.detail_ +=
" (not recorded: the journal could not be written, so the hold stands)";
975 undecided.push_back(instrument);
977 report.matches_.push_back(std::move(match));
980 for (
const auto&
id : seen) {
981 if (
id == instrument) {
987 seen.push_back(instrument);
991 for (
const auto& instrument : seen) {
992 bool blocked =
false;
993 for (
const auto&
id : undecided) {
994 if (
id == instrument) {
1000 report.withheld_.push_back(instrument);
1004 report.resolved_.push_back(instrument);
1022template <
typename Journal>
1024 const std::vector<OpenInstruction>& open,
1025 const std::vector<OrderSnapshot>& rows,
1026 std::chrono::system_clock::time_point now,
1030 std::string_view unplaced_note = {}
1032 return reconcile_against(open, rows, journal.replay(), now, ledger, journal, window, unplaced_note);
1045 const std::vector<OpenInstruction>& open, std::string detail
1048 report.
matches_.reserve(open.size());
1049 for (
const auto& instruction : open) {
1055 report.
matches_.push_back(std::move(match));
1058 for (
const auto&
id : report.
withheld_) {
1078 for (
const auto& instruction : open) {
1121 const bool venues_differ = row.
venue_ != Venue::UNSPECIFIED && assumed.
venue_ != Venue::UNSPECIFIED &&
1125 reconcile_detail::same_value(row.
price_, assumed.
price_) &&
1147 std::vector<OrderSnapshot>& into,
1148 const std::vector<OrderSnapshot>& rows
1150 std::vector<OrderSnapshot> listing;
1151 listing.reserve(into.size() + rows.size());
1152 for (
auto& listed : into) {
1153 const bool dated_by_history = listed.ordered_date_assumed_ &&
1154 std::any_of(rows.begin(), rows.end(), [&](
const OrderSnapshot& row) { return dates_listed_order(listed, row); });
1155 if (!dated_by_history) {
1156 listing.push_back(std::move(listed));
1159 into = std::move(listing);
1160 const auto listed = into.size();
1161 for (
const auto& row : rows) {
1163 for (std::size_t at = 0; at < listed; ++at) {
1165 if (!into[at].ordered_date_assumed_ &&
exact_copy(into[at], row)) {
1171 into.push_back(row);
1181inline void merge_rows(std::vector<OrderSnapshot>& into,
const std::vector<OrderSnapshot>& rows) {
1182 for (
const auto& row : rows) {
1184 for (
const auto& held : into) {
1188 if (!row.order_id_.empty() && held.order_id_ == row.order_id_) {
1194 into.push_back(row);
1205 const auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(at);
1206 const std::time_t raw = std::chrono::system_clock::to_time_t(seconds);
1209 gmtime_s(&utc, &raw);
1211 gmtime_r(&raw, &utc);
1214 date.
year_ = utc.tm_year + 1900;
1215 date.
month_ =
static_cast<unsigned>(utc.tm_mon + 1);
1216 date.
day_ =
static_cast<unsigned>(utc.tm_mday);
1231 const std::vector<OpenInstruction>& open,
const MatchWindow& window
1233 auto earliest = open.front().opened_at_;
1235 for (
const auto& instruction : open) {
1240 if (sent_by > latest) {
1245 date_of(earliest - std::chrono::hours(24)),
1246 date_of(latest + std::chrono::hours(24)),
1299 for (
const auto& instruction : report.
open_) {
1301 if (instrument.symbol_.empty()) {
1309 "a previous process left this instruction unresolved in the order journal" :
1313 report.
marked_.push_back(instrument);
1329template <
typename Journal>
1349template <
typename Runner,
typename Journal>
1351 const auto replay = journal.replay();
1356 if (report.unattributable_) {
1358 report.detail_.empty() ?
1359 "the order journal was damaged in a way that names no instrument" :
1363 if (report.unreadable_lines_ > 0) {
1365 "the order journal holds " + std::to_string(report.unreadable_lines_) +
1366 " line(s) this library did not write, so it cannot account for what was in flight"
1369 if (report.unreadable_file_) {
1373 report.detail_.empty() ?
1374 "the order journal exists and could not be read" :
An exact signed decimal: an int64 coefficient times 10^-scale.
The instruments with an order of unknown outcome, where nothing may be placed until the broker's orde...
bool requires_reconcile(const InstrumentId &id) const
Tests whether an instrument is waiting for reconciliation.
void mark(const InstrumentId &id, std::string detail)
Records an instrument's order of unknown outcome; an existing entry keeps its first detail.
void resolve_after_reading_orders(const InstrumentId &id)
Clears an instrument; call it only after reading the broker's order list for it.
Runs one strategy: feeds it events, reconciles what it asks for, checks the risk limits,...
void halt_locally(std::string detail)
Stops this runner sending, with the LOCAL_FAILURE hold, until resume_after_local_failure.
void restore_obligations(const JournalReplay &replay)
Restores the orders a previous process left unaccounted for; runner::recover_runner calls it,...
const IndeterminacyLedger & ledger() const noexcept
Returns the ledger of instruments withheld pending reconciliation or an operator: an order of unknown...
Runs a strategy: the runner, the polling loop, the order journal, and stopping.
MatchVerdict
What the broker's order list says about one instruction of unknown outcome.
@ AMBIGUOUS
Several rows could correspond, a row that fits is another placement's, or a row that fits cannot be c...
@ NOT_PLACED
No row corresponds and the window has closed: no order was found.
@ UNDECIDED
No row yet, and the window is still open; wait for the next sweep.
@ MATCHED
Exactly one row corresponds; the order exists.
@ NEEDS_OPERATOR
The instruction came from a damaged journal line, or its line cannot be matched (unresolvable_reason)...
void merge_rows(std::vector< OrderSnapshot > &into, const std::vector< OrderSnapshot > &rows)
Adds rows to a listing, skipping any whose order id is already there.
Date date_of(std::chrono::system_clock::time_point at)
Returns the UTC calendar date of an instant.
constexpr bool journal_outcome_answers_send(JournalOutcome outcome) noexcept
Tests whether a line with this outcome answers the send itself (the broker's reply,...
bool any_placement(const std::vector< OpenInstruction > &open)
Tests whether any instruction is a placement a listing could decide (which needs history,...
void merge_dated_history(std::vector< OrderSnapshot > &into, const std::vector< OrderSnapshot > &rows)
Adds history rows to a working-order listing, dropping established copies (exact_copy) and listing ro...
bool row_might_correspond(const OpenInstruction &instruction, const OrderSnapshot &row, const MatchWindow &window)
Tests whether a row might be an instruction's order although some attributes could not be read (such ...
ReconcileReport decide_nothing(const std::vector< OpenInstruction > &open, std::string detail)
Makes a report that decides nothing, for a sweep that could not see a complete listing.
std::pair< Date, Date > history_range_for(const std::vector< OpenInstruction > &open, const MatchWindow &window)
Returns the date range an order-history inquiry must cover for some instructions.
std::chrono::system_clock::time_point instruction_sent_by(const OpenInstruction &instruction, const MatchWindow &window)
Returns the last instant an instruction could have reached the broker.
Evidence time_evidence(const OpenInstruction &instruction, const OrderSnapshot &row, const MatchWindow &window)
Compares a row's order time with the window in which the instruction was sent.
std::vector< OpenInstruction > recorded_placements(const JournalReplay &replay)
Returns the placements a journal records an order id for, each with the span of its send.
const OpenInstruction * recorded_owner(const OpenInstruction &instruction, const OrderSnapshot &row, const std::vector< OpenInstruction > &recorded, const MatchWindow &window)
Returns the recorded placement whose order a row is: the row carries the order id the journal records...
bool match_window_closed(const OpenInstruction &instruction, std::chrono::system_clock::time_point now, const MatchWindow &window)
Tests whether a missing row may now be read as "not placed".
constexpr bool journal_action_matches_by_order_id(JournalAction action) noexcept
Tests whether recovery matches an instruction of this action by the broker's order id it names,...
constexpr bool journal_outcome_refuses(JournalOutcome outcome) noexcept
Tests whether a line with this outcome says the instruction was refused, so it does not exist.
RecoveryReport recover_into(const JournalReplay &replay, IndeterminacyLedger &ledger)
Marks the ledger for every instrument a journal left an instruction of unknown outcome for.
bool dates_listed_order(const OrderSnapshot &assumed, const OrderSnapshot &row)
Tests whether a dated history row is the order a listing row whose date was assumed shows: the same o...
RecoveryReport recover_runner(Runner &runner_instance, const Journal &journal)
Recovers a journal into a runner at startup: marks each instrument left owed, restores a serialized r...
constexpr bool match_verdict_resolves(MatchVerdict verdict) noexcept
Tests whether a verdict settles the question.
@ RESOLVED
A recovery sweep read the broker's order list and closed the question.
Evidence trigger_evidence(const std::optional< Decimal64 > &row_trigger, const std::optional< Decimal64 > &entry_trigger)
Compares a row's trigger price with the instruction's.
void drop_rows_fitting_another_placement(std::vector< OrderSnapshot > &rows, const JournalReplay &replay, const std::vector< OpenInstruction > &matching, const MatchWindow &window={})
Removes the rows that could be another placement's order as well.
OrderMatch match_against(const OpenInstruction &instruction, const std::vector< OrderSnapshot > &rows, const std::vector< OpenInstruction > &recorded, std::chrono::system_clock::time_point now, const MatchWindow &window={})
Decides what a broker listing says about one instruction of unknown outcome.
std::string unresolvable_reason(const JournalEntry &entry)
Returns why an instruction cannot be resolved from a broker listing.
ReconcileReport reconcile_against(const std::vector< OpenInstruction > &open, const std::vector< OrderSnapshot > &rows, const JournalReplay &replay, std::chrono::system_clock::time_point now, IndeterminacyLedger &ledger, Journal &journal, const MatchWindow &window={}, std::string_view unplaced_note={})
Resolves what a broker listing settles, records it in the journal, and clears the ledger for each ins...
constexpr std::string_view match_verdict_name(MatchVerdict verdict) noexcept
Returns a verdict's lowercase name, such as "not_placed", for logs.
constexpr bool evidence_contradicts(Evidence evidence) noexcept
Tests whether a piece of evidence rules the row out.
Evidence RouteEvidence
Evidence, under the name used for the route comparison.
JournalEntry resolution_entry(const OpenInstruction &instruction, const OrderMatch &match, std::chrono::system_clock::time_point now)
Makes the RESOLVED journal line that records a sweep's verdict.
std::vector< OpenInstruction > unresolved_instructions(const JournalReplay &replay)
Lists the instructions whose outcome is unknown, in the order they were opened.
constexpr bool evidence_leaves_open(Evidence evidence) noexcept
Tests whether a piece of evidence leaves the question open, so the row might still be the order and c...
bool exact_copy(const OrderSnapshot &a, const OrderSnapshot &b)
Tests whether two rows are established copies of one order: the same order id and instrument,...
Evidence
What one attribute of a broker row says about an instruction.
@ CONFIRMS
The row agrees.
@ CONTRADICTS
The row disagrees; it is not this order.
@ ABSENT
Not enough was recorded to say.
constexpr bool evidence_confirms(Evidence evidence) noexcept
Tests whether a piece of evidence confirms the row is the instruction's order.
constexpr bool journal_outcome_names_order(JournalOutcome outcome) noexcept
Tests whether a line with this outcome may carry the broker's id for the order.
Evidence attribute_evidence(Enum row_value, Enum entry_value) noexcept
Compares an enumerated attribute of a row with the instruction's.
bool row_corresponds(const OpenInstruction &instruction, const OrderSnapshot &row, const MatchWindow &window)
Tests whether a row is established to be an instruction's order.
RouteEvidence route_evidence(const OpenInstruction &instruction, const OrderSnapshot &row)
Compares a row's venue with the route the instruction recorded.
The library: broker-neutral types (core), and the modules below.
constexpr bool carries_trigger_price(OrderType type) noexcept
Tests whether an order type takes a trigger price.
constexpr bool carries_caller_price(OrderType type) noexcept
Tests whether an order type takes a price from the caller.
bool decimal_less(const Decimal64 &lhs, const Decimal64 &rhs)
Compares two decimals numerically, whatever their scales.
A calendar date with no time of day or time zone.
unsigned month_
The month, 1-12; zero means unset.
unsigned day_
The day of the month, 1-31; zero means unset.
Identifies an instrument: its country, venue and symbol.
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.
OrderSession session_
The session; UNKNOWN if the broker's code was not recognized.
OrderType type_
The price basis; UNKNOWN if the broker's code was not recognized.
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...
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...
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.
std::optional< Decimal64 > price_
The limit price, if the order has one.
One journal line: an instruction and what was known about it when the line was written.
std::string id_
Links an answering line to its PENDING line.
std::chrono::system_clock::time_point recorded_at_
When the line was written (for PENDING, before the request went out).
OrderType type_
The order type; recovery matches on type, time in force and session together.
std::optional< Decimal64 > price_
The price, if the order has one.
JournalOutcome outcome_
What the line says about the instruction.
std::string detail_
Why an answering line says what it says, for the operator.
std::optional< Venue > venue_
The venue the order actually goes to, as the binding resolves it.
InstrumentId instrument_
The instrument (identity; the routing venue is venue_).
std::optional< Decimal64 > trigger_price_
The trigger price of a stop-limit order; empty otherwise (and on lines that predate it).
bool requires_operator_
Whether only an operator may resolve this instruction.
JournalAction action_
Whether the instruction is a placement or a cancellation.
std::string order_id_
The broker's order id, if known: from the start on a CANCEL line, and only on an answering line for a...
The contents of a journal, including what could not be read.
bool readable() const noexcept
Tests whether the file could be read.
std::size_t unreadable_lines_
How many lines did not parse (on an append-only file, writes that did not finish).
std::vector< JournalEntry > entries_
Every line that parsed, in file order.
std::string detail_
What was wrong, for the operator.
bool truncated_tail_
Whether the last line was incomplete, as after a kill during a write.
The time allowances used when matching journalled instructions against a broker listing.
std::chrono::seconds clock_skew_
How far the broker's clock may differ from this machine's, either way.
std::chrono::seconds listing_delay_
How long the broker's listing may lag an order it accepted.
std::chrono::seconds maximum_request_
How long a request could have been in flight when no answering line records its end.
One instruction of unknown outcome, and the span in which it may have reached the broker.
JournalEntry entry_
The instruction, as its first line recorded it (its order_id_ may come from a later line).
std::string detail_
What the journal says went wrong.
std::optional< std::chrono::system_clock::time_point > closed_at_
When the send was answered: by an INDETERMINATE line in unresolved_instructions, by any answer in rec...
std::chrono::system_clock::time_point opened_at_
When the send began.
One instruction's verdict, with context for the operator.
InstrumentId instrument_
The instruction's instrument.
std::string entry_id_
The journal line id.
std::string detail_
What was decided and why, for the operator.
std::string order_id_
The broker's order id, on MATCHED.
MatchVerdict verdict_
The verdict.
std::size_t candidates_
How many rows could correspond (one or more on AMBIGUOUS).
What one sweep of the broker's order list settled, and what is still withheld.
std::vector< OrderMatch > matches_
One verdict per instruction, in journal order.
bool settled() const noexcept
Tests whether the sweep settled everything.
bool unrecorded_
Whether a resolution could not be written to the journal; its instrument then stays withheld.
std::size_t recorded_
How many resolutions were written to the journal.
std::vector< InstrumentId > resolved_
Instruments cleared; they may trade again.
bool ambiguous_
Whether any instruction was AMBIGUOUS.
std::vector< InstrumentId > withheld_
Instruments still withheld (their reasons are in matches_).
What a journal showed was left owed by the process that wrote it.
bool unattributable_
Whether something was in flight that names no instrument (a torn line too short to name one); recover...
std::vector< InstrumentId > marked_
Instruments marked in the ledger, withheld until the order list resolves them.
bool truncated_
Whether the last line was torn.
std::string detail_
What was found, for the operator.
std::vector< OpenInstruction > open_
The instructions of unknown outcome.
std::size_t unreadable_lines_
Complete lines that did not parse.
bool clean() const noexcept
Tests whether nothing was owed or damaged.
bool unreadable_file_
Whether the journal exists and could not be read; it may hold orders left in flight.