algotradelib 0.0.0.9000
C++ header-only library for algorithmic trading
Loading...
Searching...
No Matches
live_program.hpp
Go to the documentation of this file.
1#pragma once
2
37
38#include <algotradelib/live>
39#include <algotradelib/runner>
40
41#include <chrono>
42#include <cstddef>
43#include <ostream>
44#include <string>
45#include <string_view>
46#include <utility>
47#include <vector>
48
49namespace live_program {
50
51namespace atl = algotradelib;
52namespace live = algotradelib::live;
53namespace runner = algotradelib::runner;
54namespace strategy = algotradelib::strategy;
55
57struct Settings {
59 std::vector<atl::InstrumentId> universe_;
69 atl::BarInterval bar_interval_{atl::BarInterval::ONE_DAY};
71 int bar_count_{100};
73 std::chrono::nanoseconds maximum_portfolio_age_{std::chrono::minutes(2)};
75 std::chrono::nanoseconds maximum_price_limits_age_{std::chrono::minutes(20)};
77 std::string journal_path_;
79 std::string journal_scope_;
81 std::string control_file_;
83 std::size_t max_iterations_{0};
89 std::chrono::nanoseconds closing_wait_{std::chrono::seconds(30)};
95 std::chrono::nanoseconds owed_order_limit_{std::chrono::minutes(10)};
101 atl::Venue default_route_{atl::Venue::UNSPECIFIED};
102};
103
131
143template <typename Session, typename Runner>
144bool load_reference_data(Session& session, Runner& bound, const Settings& settings, std::ostream& out) {
145 for (const auto& instrument : settings.universe_) {
146 auto reference = session.instrument(instrument);
147 if (!reference) {
148 out << "could not read reference data for " << instrument.symbol_ << ": "
149 << reference.error().message_ << '\n';
150 return false;
151 }
152 bound.context().set_instrument(std::move(reference.value()));
153 }
154 return true;
155}
156
162inline void print_recovery(const runner::RecoveryReport& recovery, std::ostream& out) {
163 if (recovery.clean()) {
164 out << "recovery: clean -- the journal owes nothing\n";
165 return;
166 }
167 out << "recovery: NOT clean\n";
168 for (const auto& instrument : recovery.marked_) {
169 out << " withheld until the broker's lists resolve it: " << instrument.symbol_ << '\n';
170 }
171 if (recovery.truncated_) {
172 out << " the last line was torn: the previous process died while writing it\n";
173 }
174 if (recovery.unreadable_lines_ > 0) {
175 out << " " << recovery.unreadable_lines_ << " line(s) this library did not write\n";
176 }
177 if (recovery.unreadable_file_) {
178 out << " the journal exists and could not be read\n";
179 }
180 if (recovery.unattributable_) {
181 out << " an unresolved instruction names no instrument\n";
182 }
183 if (!recovery.detail_.empty()) {
184 out << " detail: " << recovery.detail_ << '\n';
185 }
186}
187
193inline void print_how_to_stop(const Settings& settings, std::ostream& out) {
194 out << "to stop: Ctrl-C or SIGTERM (quiesce), or\n"
195 << " echo quiesce > " << settings.control_file_ << " # stop placing, leave orders resting\n"
196 << " echo withdraw > " << settings.control_file_ << " # cancel the account's working orders\n"
197 << " echo flatten > " << settings.control_file_ << " # withdraw, then close the account's positions\n"
198 << "the file is not deleted: a word left in it is carried out at the next start; remove it\n"
199 << "before trading again\n";
200 if (settings.closing_wait_ > std::chrono::nanoseconds::zero()) {
201 out << "a withdraw or a flatten may first wait up to "
202 << std::chrono::duration_cast<std::chrono::seconds>(settings.closing_wait_).count()
203 << " s for the order list and holdings to account for this\n"
204 << "program's own orders. ";
205 }
206 out << "A supervisor's grace period must cover that stop: the wait, the calls in\n"
207 << "flight (a paginated read, a paced request) and the closing orders themselves, with margin; a\n"
208 << "SIGTERM does not turn a pending flatten into a quiesce\n";
209}
210
216inline void print_loop(const runner::LoopReport& report, std::ostream& out) {
217 out << "\niterations : " << report.iterations_ << '\n'
218 << "order list read : " << (report.order_list_read_ ? "yes" : "no") << '\n'
219 << "polls failed : bars " << report.failed(runner::PollKind::BARS) << '/'
220 << report.attempted(runner::PollKind::BARS) << ", holdings "
222 << ", orders " << report.failed(runner::PollKind::WORKING_ORDERS) << '/'
224 << "blocked : " << report.blocked_ << " iteration(s)\n"
225 << "stopped by : "
226 << (report.stopped_by_ ? runner::stop_intent_name(*report.stopped_by_) : std::string_view("nothing"))
227 << '\n'
228 << "final gate : "
229 << (report.final_gate_ ? strategy::halt_reason_name(*report.final_gate_) : std::string_view("none"))
230 << '\n'
231 << "market phase : " << strategy::market_phase_name(report.final_phase_) << '\n'
232 // Every way an order the strategy wanted did not go out, each with its own count, so an empty
233 // placement count explains itself.
234 << "plans withheld : " << report.plans_withheld_ << " (account unread, too old or older than the order listing, or a hold while it would cancel)\n"
235 << "not buildable : " << report.orders_unbuildable_ << '\n'
236 << "withheld : " << report.orders_reconcile_withheld_ << " (unresolved earlier orders)\n"
237 << "refused by guard: " << report.orders_guard_refused_ << '\n'
238 << "refused locally : " << report.orders_binding_refused_ << " (by the adapter, before sending)\n"
239 << "refused by broker: " << report.orders_broker_refused_ << '\n'
240 << "unknown outcome : " << report.orders_indeterminate_ << " (read the broker's order list)\n"
241 // Cancellations that did not go through, which no line above counts. Accepted ones are not here.
242 << "cancels : " << report.cancels_rejected_ << " rejected (by the broker or the adapter), "
243 << report.cancels_indeterminate_ << " of unknown outcome, " << report.cancels_refused_
244 << " refused by a hold, " << report.cancels_unresolved_ << " for an order not in the picture\n"
245 << "unconfirmed : " << report.orders_unconfirmed_timed_out_
246 << " (sent, and not confirmed in time: its instrument waits for holdings or its history, or for you if "
247 "it was never named or the runner overlaps orders)\n";
248 if (report.last_unbuildable_) {
249 out << "last unbuildable: " << strategy::reject_reason_name(report.last_unbuildable_->reason_) << " for "
250 << report.last_unbuildable_->instrument_.symbol_
251 << (report.last_unbuildable_->tag_.empty() ? "" : " (" + report.last_unbuildable_->tag_ + ")") << '\n';
252 }
253 if (report.last_broker_refusal_) {
254 out << "last refusal : " << report.last_broker_refusal_->code_ << ' '
255 << report.last_broker_refusal_->message_ << '\n';
256 }
257 if (report.abandoned_) {
258 out << "ABANDONED : after repeated failures polling "
259 << (report.abandoned_kind_ ? runner::poll_kind_name(*report.abandoned_kind_) : std::string_view("?"))
260 << '\n';
261 }
262 for (const auto& note : report.notes_) {
263 out << "note : " << note << '\n';
264 }
265}
266
272inline void print_report(const runner::LoopReport& report, std::ostream& out) {
273 print_loop(report, out);
274 out << "closing complete: " << (report.closing_complete() ? "yes" : "NO")
275 << " -- whether every closing instruction was taken, not whether the account is flat;\n"
276 << " read the broker's order list and holdings to learn that\n";
277}
278
284inline bool stop_closes(const runner::LoopReport& report) noexcept {
285 return report.stopped_by_ && *report.stopped_by_ != runner::StopIntent::QUIESCE;
286}
287
294template <typename Loop>
295void configure(Loop& loop, const Settings& settings) {
296 loop.set_maximum_portfolio_age(settings.maximum_portfolio_age_);
297 loop.set_maximum_price_limits_age(settings.maximum_price_limits_age_);
298 loop.set_closing_wait(settings.closing_wait_);
299 loop.set_owed_order_limit(settings.owed_order_limit_);
300 loop.set_control_file(runner::FileStopChannel(settings.control_file_));
301}
302
324template <typename Strategy, typename Session, typename MarketData, typename Clock = runner::SteadyClock>
326 Session& session,
327 MarketData& market_data,
328 Strategy strategy_instance,
329 const Settings& settings,
330 runner::StopSource& stop,
331 std::ostream& out,
332 Clock clock = {}
333) {
334 runner::FileJournal journal(settings.journal_path_, settings.journal_scope_);
335 if (!journal.open()) {
336 out << "journal refused: " << journal.rejection() << '\n'
337 << "a runner that cannot record what it sends does not send\n";
338 return Exit::JOURNAL_REFUSED;
339 }
340
341 // One clock for the loop and the journal: lines are stamped, and recovery's sweep asks the broker
342 // about, the times the loop runs at, which are not the wall clock's in a paper run or a test.
343 runner::ClockReference<Clock> shared_clock(clock);
344 auto bound = live::bind_journaled(
345 session, journal, std::move(strategy_instance), settings.limits_, live::default_series_capacity,
346 settings.default_route_, shared_clock
347 );
348
349 const auto recovery = runner::recover_runner(bound, journal);
350 print_recovery(recovery, out);
351 if (const auto failure = bound.local_failure()) {
352 // Damage that names no single instrument halts everything, and restarting will find the same
353 // line. There is deliberately no flag to trade past it: a flag that is needed on every restart
354 // ends up in a unit file. The way out is by hand, and it keeps the evidence.
355 out << "HALTED: " << *failure << '\n'
356 << "nothing was started. This program's only way on gives up the whole journal. That is safe\n"
357 << "only once no order the journal may track can execute any more:\n"
358 << " 1. read the broker's order list, order history and holdings for this account yourself.\n"
359 << " A damaged journal may not name every order it tracks: take every order sent on this\n"
360 << " account since the journal began, on every day and every page of the order history, and\n"
361 << " every order still working;\n"
362 << " 2. each must have ended: filled in full, or cancelled with its history row saying so. A\n"
363 << " cancellation only requested, an order list that no longer shows it, and the close of the\n"
364 << " session do not show that an order has ended;\n"
365 << " 3. holdings must show the final fills of each;\n"
366 << " 4. only then move the journal aside, keeping it: mv " << settings.journal_path_ << ' '
367 << settings.journal_path_ << ".damaged-$(date +%Y%m%d)\n"
368 << "the next start then begins a new journal and sizes from the holdings and working orders the\n"
369 << "broker reports. If steps 2 and 3 cannot be shown for every order, leave the journal where it\n"
370 << "is. Do NOT move it aside while an order it may track is left working: a new journal does not\n"
371 << "know the order, and when it fills before holdings show it, the order is sent again.\n";
373 }
374
375 if (!load_reference_data(market_data, bound, settings, out)) {
377 }
378 // Said before trading as well as in the report at the end, so it is read before it matters.
379 if constexpr (decltype(bound)::on_fill_never_fires) {
380 out << "note: this strategy defines on_fill, which never fires on a live runner; anything it\n"
381 << " does there (a protective stop, say) will not happen. See writing-a-strategy.md.\n";
382 }
383
385 session, market_data, settings.universe_
386 );
387 source.set_journal(journal);
388 source.set_bar_query(settings.bar_interval_, settings.bar_count_);
389
390 runner::PollingLoop<decltype(bound), decltype(source), runner::ClockReference<Clock>> loop(
391 bound, source, settings.schedule_, shared_clock
392 );
393 configure(loop, settings);
394
395 print_how_to_stop(settings, out);
396 const auto report = loop.run(stop, settings.max_iterations_);
397 print_report(report, out);
398
399 // Checked before abandonment: sticky, set by a failed journal write as much as by recovery, and
400 // the more urgent of the two -- what the runner was sending at that moment is unknown. A run can
401 // end both ways, and the report above still says ABANDONED.
402 if (const auto failure = bound.local_failure()) {
403 out << "LOCAL FAILURE : " << *failure << '\n'
404 << " read the broker's order list before starting again\n";
405 return Exit::LOCAL_FAILURE;
406 }
407 if (report.abandoned_) {
409 }
410 if (stop_closes(report) && !report.closing_complete()) {
412 }
413 if (report.owed_stop_) {
414 out << "OWED ORDER : the run stopped placing by itself (see the note above). A supervisor must not\n"
415 << " restart it: the order is restored from the journal and the run stops again.\n"
416 << "this journal still owes:\n";
417 for (const auto& owed : bound.obligations()) {
418 out << " " << owed.instrument_.symbol_ << ' ' << (owed.side_ == atl::OrderSide::BUY ? "buy " : "sell ")
419 << owed.quantity_.str() << ", sent " << atl::format_iso8601_utc(owed.sent_at_) << ", "
420 << (owed.state_ == runner::ObligationState::OPERATOR ? "held for an operator" :
421 owed.state_ == runner::ObligationState::UNNAMED ? "no order found" :
422 "order " + owed.order_id_ + ", not yet in holdings")
423 << '\n';
424 }
425 out << "this program has no command to release one order, and its only way on gives up the whole\n"
426 << "journal. That is safe only once none of the orders above can execute any more:\n"
427 << " 1. read the broker's order list, order history and holdings for every order above;\n"
428 << " 2. each must have ended: filled in full, or cancelled with its history row saying so. A\n"
429 << " cancellation only requested, an order list that no longer shows it, and the close of the\n"
430 << " session do not show that an order has ended;\n"
431 << " 3. holdings must show the final fills of each;\n"
432 << " 4. only then move the journal aside, keeping it: mv " << settings.journal_path_ << ' '
433 << settings.journal_path_ << ".owed-$(date +%Y%m%d)\n"
434 << "the next start then begins a new journal and sizes from the account as it is.\n"
435 << "Do NOT move the journal aside while an order it tracks is left working: a new journal does not\n"
436 << "know the order, and when it fills before holdings show it, the order is sent again. A program\n"
437 << "of your own releases that instrument's obligations instead (Runner::release_obligations; see\n"
438 << "set_owed_order_limit) and keeps the rest of the journal.\n";
439 return Exit::OWED_ORDER;
440 }
441 return Exit::CLEAN;
442}
443
461template <typename Strategy, typename Session, typename Clock = runner::SteadyClock>
463 Session& session,
464 Strategy strategy_instance,
465 const Settings& settings,
466 runner::StopSource& stop,
467 std::ostream& out,
468 Clock clock = {}
469) {
470 return trade(session, session, std::move(strategy_instance), settings, stop, out, std::move(clock));
471}
472
494template <typename Strategy, typename Session, typename MarketData, typename Clock = runner::SteadyClock>
496 Session& session,
497 MarketData& market_data,
498 Strategy strategy_instance,
499 const Settings& settings,
500 runner::StopSource& stop,
501 std::ostream& out,
502 Clock clock = {}
503) {
504 auto bound = live::bind_observer(session, std::move(strategy_instance), settings.limits_);
505 if (!load_reference_data(market_data, bound, settings, out)) {
506 return Exit::REFERENCE_UNREADABLE;
507 }
508
510 session, market_data, settings.universe_
511 );
512 source.set_bar_query(settings.bar_interval_, settings.bar_count_);
513 runner::PollingLoop<decltype(bound), decltype(source), Clock> loop(
514 bound, source, settings.schedule_, std::move(clock)
515 );
516 configure(loop, settings);
517
518 out << "observing: nothing can be sent (pass --live to trade)\n";
519 print_how_to_stop(settings, out);
520 const auto report = loop.run(stop, settings.max_iterations_);
521 print_loop(report, out);
522 // `closing_complete()` is deliberately not printed: an observer's executor accepts every
523 // instruction without sending it, so "every closing instruction was taken" would be true and would
524 // say nothing about the broker.
525 out << "closing : simulated -- the observer sent nothing, so nothing at the broker was\n"
526 << " placed or withdrawn by this run\n";
527 return report.abandoned_ ? Exit::LOOP_ABANDONED : Exit::CLEAN;
528}
529
543template <typename Strategy, typename Session, typename Clock = runner::SteadyClock>
545 Session& session,
546 Strategy strategy_instance,
547 const Settings& settings,
548 runner::StopSource& stop,
549 std::ostream& out,
550 Clock clock = {}
551) {
552 return observe(session, session, std::move(strategy_instance), settings, stop, out, std::move(clock));
553}
554
555} // namespace live_program
The source a runner::PollingLoop polls: reads a broker session and feeds bars, quotes,...
A clock that reads another one, so that two holders (a loop and its journal) read one time.
Definition clock.hpp:60
The order journal on disk: one JSON object per line, appended and flushed to disk (fsync) before appe...
A control file an operator writes to stop a running loop with a given intent.
Definition stop.hpp:229
Runs a runner for a live session: polls a source on a schedule, gates placements, derives the market ...
Definition loop.hpp:976
A stop request the loop polls; safe to set from a signal handler.
Definition stop.hpp:158
void print_how_to_stop(const Settings &settings, std::ostream &out)
Prints how to stop the run, before it starts, so nobody reaches for kill -9.
Exit observe(Session &session, MarketData &market_data, Strategy strategy_instance, const Settings &settings, runner::StopSource &stop, std::ostream &out, Clock clock={})
Runs a strategy against a session's account and another's market data, with nothing to send through.
bool stop_closes(const runner::LoopReport &report) noexcept
Tests whether a run ended with a stop that sends closing instructions.
Exit
What the program exits with, so a supervisor can tell its endings apart.
@ JOURNAL_REFUSED
The journal would not open, so nothing was bound.
@ LOOP_ABANDONED
The loop gave up after repeated poll failures: it was not trading when it returned.
@ LOCAL_FAILURE
Local machinery failed during the run – usually a journal write – and the runner halted itself....
@ CLOSING_INCOMPLETE
A stop that closes ran and closing_complete() is false: read the broker's order list.
@ ACCOUNT_UNRESOLVED
The program could not settle which account to trade (used by example-live-program).
@ REFERENCE_UNREADABLE
Reference data could not be read, so no order could be priced; nothing was started.
@ OWED_ORDER
The run stopped placing by itself: an order has been owed too long with nobody able to account for it...
@ RECOVERY_HALTED
Recovery halted the runner; nothing was started.
@ CLEAN
The run ended, and any closing pass it made got every instruction out.
bool load_reference_data(Session &session, Runner &bound, const Settings &settings, std::ostream &out)
Reads each instrument's reference data (lot size, price grid) onto the runner, once; the loop does no...
void configure(Loop &loop, const Settings &settings)
Configures the loop the same way for both modes: every age stated, the control file installed.
void print_loop(const runner::LoopReport &report, std::ostream &out)
Prints the loop's own figures and notes, which read the same in both modes.
void print_report(const runner::LoopReport &report, std::ostream &out)
Prints the loop report and what closing_complete() does and does not mean.
Exit trade(Session &session, MarketData &market_data, Strategy strategy_instance, const Settings &settings, runner::StopSource &stop, std::ostream &out, Clock clock={})
Trades a strategy through a session, reading market data and reference data from another.
void print_recovery(const runner::RecoveryReport &recovery, std::ostream &out)
Prints what recovery found, in the words an operator acts on.
Connects a runner to a broker session for live trading.
Definition bind.hpp:12
constexpr std::size_t default_series_capacity
The number of trailing bars per instrument a bound runner keeps by default.
Definition bind.hpp:24
JournaledRunner< Strategy, Session, Journal, Clock > bind_journaled(Session &session, Journal &journal, Strategy strategy, strategy::RiskLimits limits={}, std::size_t series_capacity=default_series_capacity, Venue default_route=Venue::UNSPECIFIED, Clock clock={})
Binds a strategy to a broker session, recording every instruction in a journal before it is sent.
Definition bind.hpp:137
Runs a strategy: the runner, the polling loop, the order journal, and stopping.
Definition clock.hpp:7
constexpr std::string_view poll_kind_name(PollKind kind) noexcept
Returns a poll kind's lowercase name, such as "working_orders".
Definition loop.hpp:66
@ WORKING_ORDERS
Working orders, which reconciliation and cancellation need.
Definition loop.hpp:38
@ BARS
Completed bars.
Definition loop.hpp:32
@ HOLDINGS
Holdings, which orders are sized against.
Definition loop.hpp:36
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...
@ QUIESCE
Stop sending new orders; nothing is cancelled or closed. The default, and what a signal means.
Definition stop.hpp:34
@ OPERATOR
An order only an operator may account for (Runner::release_obligations).
@ UNNAMED
An order whose existence is unknown (its reply was lost and no sweep named it).
constexpr std::string_view stop_intent_name(StopIntent intent) noexcept
Returns a lowercase name for an intent, as written in the control file ("withdraw").
Definition stop.hpp:61
What a strategy sees and emits: events, intents, the portfolio, reconciliation and risk limits.
Definition concept.hpp:16
constexpr std::string_view market_phase_name(MarketPhase phase) noexcept
Returns a lowercase name for a phase, such as "pre_open", for logs and display.
Definition event.hpp:57
constexpr std::string_view reject_reason_name(RejectReason reason) noexcept
Returns a lowercase name for a reason, such as "no_reference_data", for logs.
constexpr std::string_view halt_reason_name(HaltReason reason) noexcept
Returns a lowercase name for a hold, such as "portfolio_stale", for logs.
Definition risk.hpp:144
The library: broker-neutral types (core), and the modules below.
Venue
A trading venue: an exchange, or the combined view across exchanges.
Definition types.hpp:43
BarInterval
The period one bar covers.
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.
Definition types.hpp:2453
What a whole PollingLoop::run did, and why it ended.
Definition loop.hpp:579
bool abandoned_
Whether the loop gave up because a poll kept failing.
Definition loop.hpp:662
std::size_t plans_withheld_
Whole plans held back (any WithheldPlanReason); see Runner::plans_withheld.
Definition loop.hpp:639
std::size_t orders_reconcile_withheld_
Orders held back for an instrument of unknown order state; see Runner::orders_reconcile_withheld.
Definition loop.hpp:616
std::size_t iterations_
How many iterations ran.
Definition loop.hpp:581
strategy::MarketPhase final_phase_
The phase the last iteration computed.
Definition loop.hpp:595
std::size_t orders_unconfirmed_timed_out_
Sent orders that timed out unconfirmed; see Runner::set_confirmation_timeout for what each mode then ...
Definition loop.hpp:637
std::optional< PollKind > abandoned_kind_
The poll kind that exhausted its failure budget, if one did.
Definition loop.hpp:664
std::size_t blocked_
How many iterations were blocked from placing (a named cancellation may still have gone).
Definition loop.hpp:589
std::size_t failed(PollKind kind) const noexcept
Returns how many polls of a kind failed.
Definition loop.hpp:789
std::size_t cancels_rejected_
Cancellations refused by the broker or binding; see Runner::cancels_rejected.
Definition loop.hpp:643
std::optional< strategy::Rejection > last_unbuildable_
Why reconciliation could not build the last such instruction in this run, if any.
Definition loop.hpp:653
std::optional< strategy::HaltReason > final_gate_
The most fundamental hold in force when the run ended, whoever took it.
Definition loop.hpp:593
std::size_t orders_guard_refused_
Orders the risk guard refused; see Runner::orders_guard_refused.
Definition loop.hpp:618
bool order_list_read_
Whether the broker's order list was read at least once.
Definition loop.hpp:666
std::optional< BrokerRefusal > last_broker_refusal_
The broker's code and message for the last order it refused during this run, if any.
Definition loop.hpp:651
std::size_t cancels_indeterminate_
Cancellations with an unknown outcome.
Definition loop.hpp:649
std::optional< StopIntent > stopped_by_
The stop intent that ended the run, if one did.
Definition loop.hpp:591
std::size_t orders_indeterminate_
Orders with an unknown outcome (SendOutcome::INDETERMINATE, including most KIS, Toss and Kiwoom refus...
Definition loop.hpp:631
std::size_t cancels_unresolved_
Cancellations naming an order the portfolio does not list.
Definition loop.hpp:647
std::size_t attempted(PollKind kind) const noexcept
Returns how many polls of a kind were attempted.
Definition loop.hpp:780
bool closing_complete() const noexcept
Tests whether every closing instruction the loop asked for was accepted, and the loop knew what to as...
Definition loop.hpp:761
std::size_t cancels_refused_
Cancellations a hold refused.
Definition loop.hpp:645
std::size_t orders_unbuildable_
Orders reconciliation could not build during this run's polling; see Runner::orders_unbuildable.
Definition loop.hpp:614
std::vector< std::string > notes_
Notes for the operator, such as an unrecognized control-file word or a missing policy.
Definition loop.hpp:773
std::size_t orders_binding_refused_
Orders the binding refused to send; see Runner::orders_binding_refused.
Definition loop.hpp:620
std::size_t orders_broker_refused_
Orders the broker refused (SendOutcome::REJECTED); see last_broker_refusal_. At KIS,...
Definition loop.hpp:626
How often a PollingLoop polls each kind; an unset interval disables that poll.
Definition loop.hpp:169
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::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.
The limits the operator sets for RiskGuard.
Definition risk.hpp:268
What a run is told, gathered so main states it once and a test can state it differently.
atl::BarInterval bar_interval_
Which bars the source asks for, and how many: the history read at start is this many.
std::vector< atl::InstrumentId > universe_
The instruments the source polls.
std::string journal_path_
Where the order journal lives.
std::chrono::nanoseconds maximum_portfolio_age_
How old a holdings snapshot – and the order listing – may be before the loop stops sizing against it.
std::chrono::nanoseconds closing_wait_
How long a withdraw or flatten may wait for the order list and holdings to account for this runner's ...
std::chrono::nanoseconds maximum_price_limits_age_
How old a price band may be before the loop stops clamping to it.
std::string control_file_
The file an operator writes quiesce, withdraw or flatten into to stop the run.
int bar_count_
How many bars each bar poll asks for.
runner::PollSchedule schedule_
How often each kind is polled. trade needs working_orders_ set, shorter than maximum_portfolio_age_: ...
atl::Venue default_route_
The venue an order is sent to, since a strategy's orders name none; UNSPECIFIED takes the broker's ow...
std::string journal_scope_
Which account the journal belongs to; a journal naming another refuses to open.
std::chrono::nanoseconds owed_order_limit_
How long an order may stay owed with nobody able to account for it (its outcome unknown and no sweep ...
std::size_t max_iterations_
Iterations before returning; 0 runs until a stop.
strategy::RiskLimits limits_
The limits the guard enforces.