algotradelib 0.0.0.9000
C++ header-only library for algorithmic trading
Loading...
Searching...
No Matches
algotradelib::runner::PollingLoop< Runner, Source, Clock > Class Template Reference

Runs a runner for a live session: polls a source on a schedule, gates placements, derives the market phase, and honours stop requests. More...

#include <algotradelib/src/runner/loop.hpp>

Public Member Functions

 PollingLoop (Runner &runner, Source &source, PollSchedule schedule={}, Clock clock={})
 Constructs a loop.
const Clock & clock () const noexcept
 Returns the clock.
Clock & clock () noexcept
 Returns the clock, for a test to advance.
const PollSchedule & schedule () const noexcept
 Returns the schedule.
void set_maximum_portfolio_age (std::chrono::nanoseconds age)
 Sets how old the holdings (and, for a runner that sends to a broker, the working-order listing) may be before placements are held (PORTFOLIO_STALE).
void set_closing_wait (std::chrono::nanoseconds limit)
 Sets how long a WITHDRAW or FLATTEN may wait before its closing pass (30 seconds by default).
void set_owed_order_limit (std::chrono::nanoseconds limit)
 Sets how long a serialized runner's order may stay owed with nobody able to account for it before the loop stops placing (ten minutes by default).
void allow_unbounded_portfolio_age ()
 Accepts holdings of any age, instead of stating a maximum.
void set_maximum_price_limits_age (std::optional< std::chrono::nanoseconds > age)
 Sets how old a price band may be and still be applied; passed to the runner (see Runner::set_maximum_price_limits_age).
void allow_unbounded_price_limits_age ()
 Applies price bands of any age (see Runner::allow_unbounded_price_limits_age).
void allow_absent_portfolio ()
 Lets the run place orders before holdings have ever been read (for a broker without a holdings inquiry, or a run deliberately without that poll).
void set_session_window (strategy::SessionWindow window)
 Sets the session window the market phase is derived from (KRX by default, as in a backtest).
const strategy::SessionWindow & session_window () const noexcept
 Returns the session window.
void set_failure_policy (FailurePolicy policy)
 Sets how many consecutive failures of one poll kind stop the loop.
void set_control_file (FileStopChannel channel)
 Sets the control file an operator writes a stop intent into.
LoopReport run (StopSource &stop, std::size_t max_iterations=0)
 Runs until a stop is requested, a poll kind exhausts its failure budget, or max_iterations iterations have run.
const IterationReport & last () const noexcept
 Returns what the last iteration did.
void set_maximum_buying_power_age (std::chrono::nanoseconds age)
 Sets how old buying-power readings may be.

Detailed Description

template<typename Runner, typename Source, typename Clock = SteadyClock>
class algotradelib::runner::PollingLoop< Runner, Source, Clock >

Runs a runner for a live session: polls a source on a schedule, gates placements, derives the market phase, and honours stop requests.

Each iteration: check for a stop (a StopSource or the control file); derive and install the market phase from the SessionWindow and the polled calendar; make sure the broker's order list has been read once (until it has, placements are held); poll whatever is due, in all_poll_kinds order, deriving the phase again before each poll; announce a phase change; retry retained targets.

Placements are held through holds on the runner's strategy::RiskGuard: ORDER_LIST_UNREAD, PORTFOLIO_ABSENT, PORTFOLIO_STALE, BUYING_POWER_UNREAD and POLL_FAILING are data holds, under which named cancellations still go (see strategy::halt_blocks), so a stop can withdraw known orders even when the order list could not be read. STOPPING, taken while a stop is honoured and left in place after it, blocks cancellations too; the loop lifts it only for its own closing instructions. The loop never takes or releases OPERATOR or LOCAL_FAILURE, which also block cancellations.

State a holdings age policy (set_maximum_portfolio_age or allow_unbounded_portfolio_age): without one, every placement is held as PORTFOLIO_STALE.

A phase the loop cannot establish is reported (final_phase_ and a note), not held. A stop is honoured with a closing pass (see StopIntent); LoopReport::closing_complete() says whether everything asked for was accepted, which is not the same as being flat. The closing orders are allowed in any phase but still face every other risk check (sellable quantity, ceilings).

Note
If you write a source: install the trading calendar during PollKind::CALENDAR only, and do not feed the runner from that poll.
Template Parameters
RunnerThe runner type (usually runner::Runner).
SourceThe source type (live::SessionSource, or ManualSource in tests).
ClockThe clock (SteadyClock, or ManualClock in tests). One of your own provides now() (wall-clock time), monotonic_now() (a steady_clock reading that only moves forward) and wait_for(std::chrono::nanoseconds).

Definition at line 976 of file loop.hpp.

Constructor & Destructor Documentation

◆ PollingLoop()

template<typename Runner, typename Source, typename Clock = SteadyClock>
algotradelib::runner::PollingLoop< Runner, Source, Clock >::PollingLoop ( Runner & runner,
Source & source,
PollSchedule schedule = {},
Clock clock = {} )
inline

Constructs a loop.

Parameters
runnerThe runner; borrowed, and must outlive the loop.
sourceThe source; borrowed, and must outlive the loop.
scheduleHow often to poll each kind.
clockThe clock.

Definition at line 985 of file loop.hpp.

Member Function Documentation

◆ allow_absent_portfolio()

template<typename Runner, typename Source, typename Clock = SteadyClock>
void algotradelib::runner::PollingLoop< Runner, Source, Clock >::allow_absent_portfolio ( )
inline

Lets the run place orders before holdings have ever been read (for a broker without a holdings inquiry, or a run deliberately without that poll).

It lifts the PORTFOLIO_ABSENT hold, but not an age limit on holdings that were read, and a plan that would cancel something is still held back until holdings are read.

Definition at line 1209 of file loop.hpp.

◆ allow_unbounded_portfolio_age()

template<typename Runner, typename Source, typename Clock = SteadyClock>
void algotradelib::runner::PollingLoop< Runner, Source, Clock >::allow_unbounded_portfolio_age ( )
inline

Accepts holdings of any age, instead of stating a maximum.

It does not lift the hold for holdings never read, nor the one for a runner whose working-order listing never arrives.

Warning
A listing is then never too old. If account polls keep being refused for the rate limit (which never exhausts the failure budget), the loop keeps placing against the last listing, and sent orders can stay counted as working indefinitely.

Definition at line 1182 of file loop.hpp.

Referenced by main().

◆ allow_unbounded_price_limits_age()

template<typename Runner, typename Source, typename Clock = SteadyClock>
void algotradelib::runner::PollingLoop< Runner, Source, Clock >::allow_unbounded_price_limits_age ( )
inline

Applies price bands of any age (see Runner::allow_unbounded_price_limits_age).

Definition at line 1198 of file loop.hpp.

◆ clock() [1/2]

template<typename Runner, typename Source, typename Clock = SteadyClock>
const Clock & algotradelib::runner::PollingLoop< Runner, Source, Clock >::clock ( ) const
inlinenoexcept

Returns the clock.

Returns
The clock.

Definition at line 992 of file loop.hpp.

◆ clock() [2/2]

template<typename Runner, typename Source, typename Clock = SteadyClock>
Clock & algotradelib::runner::PollingLoop< Runner, Source, Clock >::clock ( )
inlinenoexcept

Returns the clock, for a test to advance.

Returns
The clock.

Definition at line 997 of file loop.hpp.

◆ last()

template<typename Runner, typename Source, typename Clock = SteadyClock>
const IterationReport & algotradelib::runner::PollingLoop< Runner, Source, Clock >::last ( ) const
inlinenoexcept

Returns what the last iteration did.

Returns
The iteration report.

Definition at line 1425 of file loop.hpp.

◆ run()

template<typename Runner, typename Source, typename Clock = SteadyClock>
LoopReport algotradelib::runner::PollingLoop< Runner, Source, Clock >::run ( StopSource & stop,
std::size_t max_iterations = 0 )
inline

Runs until a stop is requested, a poll kind exhausts its failure budget, or max_iterations iterations have run.

On a stop it honours the intent (cancelling and closing as asked) and calls the strategy's on_stop under the STOPPING hold.

Parameters
stopThe stop source to poll (and that a signal handler may set).
max_iterationsThe most iterations to run; zero means unbounded.
Returns
The run's report.

Definition at line 1254 of file loop.hpp.

◆ schedule()

template<typename Runner, typename Source, typename Clock = SteadyClock>
const PollSchedule & algotradelib::runner::PollingLoop< Runner, Source, Clock >::schedule ( ) const
inlinenoexcept

Returns the schedule.

Returns
The schedule.

Definition at line 1003 of file loop.hpp.

Referenced by PollingLoop().

◆ session_window()

template<typename Runner, typename Source, typename Clock = SteadyClock>
const strategy::SessionWindow & algotradelib::runner::PollingLoop< Runner, Source, Clock >::session_window ( ) const
inlinenoexcept

Returns the session window.

Returns
The window.

Definition at line 1229 of file loop.hpp.

◆ set_closing_wait()

template<typename Runner, typename Source, typename Clock = SteadyClock>
void algotradelib::runner::PollingLoop< Runner, Source, Clock >::set_closing_wait ( std::chrono::nanoseconds limit)
inline

Sets how long a WITHDRAW or FLATTEN may wait before its closing pass (30 seconds by default).

A closing pass is built from the holdings and the order list in hand. Right after this runner sent an order they do not show it yet, and a pass built then withdraws what it can see and closes nothing (LoopReport::closing_source_unverified_, closing_unconfirmed_). So a stop that closes first lets the loop read both again – working orders, then holdings, no sooner than five seconds after each was last asked, by anything – while it holds STOPPING, and runs its pass as soon as:

  • the picture is verified;
  • no order of this runner awaits an account a read could give (Runner::obligations_awaiting_account);
  • and no instrument that is held is marked in the indeterminacy ledger.

The iteration that takes the stop up reads as any other; with none of those missing its pass runs in that iteration. Only the wait's further reads are paced.

or when this limit has passed, measured on the clock's monotonic reading. The pass then runs as it always did and the report says the wait ran out (closing_wait_expired_). The limit bounds the wait, not the stop: a call already in flight, and the pass itself, take what they take.

It does not wait for a QUIESCE; for an order of this runner that is simply resting (the pass withdraws it, and a holding behind it is then closed by asking again); for an order held for an operator, or its mark; under an OPERATOR or LOCAL_FAILURE hold, when the pass can send nothing; or with a source that lists no working orders. It cannot tell every case no read will change: a mark made within the last minute or two, a mark nothing sweeps (a source with no journal), or an order whose reply was lost wait the whole limit. Zero or a negative limit disables the wait.

A supervisor's grace period has to cover this limit on top of the calls in flight and the pass.

Parameters
limitThe longest wait.

Definition at line 1118 of file loop.hpp.

◆ set_control_file()

template<typename Runner, typename Source, typename Clock = SteadyClock>
void algotradelib::runner::PollingLoop< Runner, Source, Clock >::set_control_file ( FileStopChannel channel)
inline

Sets the control file an operator writes a stop intent into.

Parameters
channelThe channel.

Definition at line 1241 of file loop.hpp.

◆ set_failure_policy()

template<typename Runner, typename Source, typename Clock = SteadyClock>
void algotradelib::runner::PollingLoop< Runner, Source, Clock >::set_failure_policy ( FailurePolicy policy)
inline

Sets how many consecutive failures of one poll kind stop the loop.

Parameters
policyThe policy.

Definition at line 1235 of file loop.hpp.

◆ set_maximum_buying_power_age()

template<typename Runner, typename Source, typename Clock = SteadyClock>
void algotradelib::runner::PollingLoop< Runner, Source, Clock >::set_maximum_buying_power_age ( std::chrono::nanoseconds age)
inline

Sets how old buying-power readings may be.

Used only with RiskLimits::enforce_buying_power_ on. Then a missing or too-old reading holds every placement, sells included (BUYING_POWER_UNREAD); named cancellations still go. The readings needed: one per currency the source names (live::SessionSource::set_buying_power_currencies), or, if it names none, at least one, with every reading present fresh.

Parameters
ageThe maximum age.

Definition at line 2114 of file loop.hpp.

◆ set_maximum_portfolio_age()

template<typename Runner, typename Source, typename Clock = SteadyClock>
void algotradelib::runner::PollingLoop< Runner, Source, Clock >::set_maximum_portfolio_age ( std::chrono::nanoseconds age)
inline

Sets how old the holdings (and, for a runner that sends to a broker, the working-order listing) may be before placements are held (PORTFOLIO_STALE).

One of this and allow_unbounded_portfolio_age is required: without either, every placement is held as PORTFOLIO_STALE (and a note says so). The same age bounds a stop's closing pass (see LoopReport::closing_source_unverified_).

Parameters
ageThe maximum age.

Definition at line 1079 of file loop.hpp.

◆ set_maximum_price_limits_age()

template<typename Runner, typename Source, typename Clock = SteadyClock>
void algotradelib::runner::PollingLoop< Runner, Source, Clock >::set_maximum_price_limits_age ( std::optional< std::chrono::nanoseconds > age)
inline

Sets how old a price band may be and still be applied; passed to the runner (see Runner::set_maximum_price_limits_age).

Parameters
ageThe maximum age; std::nullopt withdraws the policy, so no band is applied.

Definition at line 1193 of file loop.hpp.

◆ set_owed_order_limit()

template<typename Runner, typename Source, typename Clock = SteadyClock>
void algotradelib::runner::PollingLoop< Runner, Source, Clock >::set_owed_order_limit ( std::chrono::nanoseconds limit)
inline

Sets how long a serialized runner's order may stay owed with nobody able to account for it before the loop stops placing (ten minutes by default).

Such an order is one whose outcome is unknown and that no sweep has named (ObligationState::UNNAMED), or one held for an operator (ObligationState::OPERATOR). Its instrument is withheld until Runner::release_obligations, and a strategy trading other instruments would go on beside it: a pair with one leg, a rotation holding a name it meant to sell. So the loop acts in two steps.

The hold. Once the limit has passed on the loop's clock, the loop holds strategy::HaltReason::STOPPING: nothing a strategy asks for goes out, a withdrawal included. It is taken where the loop checks its holds, before every poll and once more after an iteration's polls, and is not a deadline enforced at each send: everything up to the next check runs on the last one. A poll already begun when the limit passes (a slow read that then feeds a handler) is not interrupted, and neither is the end of an iteration, where the phase handler and then the retry of kept targets both follow the last check. So an order can still go out after the limit, until the loop's next check. Every order-list read that begins after a check that found the limit passed runs under the hold, its handlers included, and the hold stays through a failed read, a start that cannot read the list, and a run abandoned for failing polls.

The stop. Once an order-list read has succeeded in an iteration that began after the limit and has still left the order so, the loop requests StopIntent::QUIESCE on the run's StopSource: nothing more is placed, nothing is cancelled or closed, and the order is still owed. LoopReport::owed_stop_ names it. If that read's sweep names the order instead, the hold is lifted and the run goes on. So the stop comes later than the limit, with the first successful order-list read in an iteration that began after it: a read in an iteration that began before the limit does not decide, and how long the next takes to come depends on the list's cadence, on the time the loop's own calls take, and on reads failing. The request stays on the StopSource, as a control-file word does, and stops any other loop sharing it. A restart restores the order and stops again at its first order-list read, having placed nothing, until a sweep names the order or it is released.

To trade again in this process, after reading the broker's order list and holdings for the order: Runner::release_obligations for its instrument, ledger().resolve_after_reading_orders if the ledger still holds the instrument (an unnamed order past the confirmation timeout and one held for an operator each mark it, and a release leaves the ledger alone), StopSource::clear, and guard().resume(strategy::HaltReason::STOPPING); the next order there waits for a holdings read received after the release.

An order the broker named never causes this, however old; it stays owed until holdings or its own history account for it, which is not guaranteed to happen (Runner::obligations). An order whose reply was lost and that the broker did accept causes it too, if no list shows it within the limit: the stop says nobody could account for the order, not that it failed. The limit has to exceed the time a sweep needs to name such an order: its matching window (two minutes by default) and whatever a listing lags. The ages are on the loop's wall clock against the journal's stamps; a clock set forward shortens them and one set back lengthens them.

Parameters
limitThe longest such an order may be owed; zero or negative never holds or stops for one.

Definition at line 1170 of file loop.hpp.

◆ set_session_window()

template<typename Runner, typename Source, typename Clock = SteadyClock>
void algotradelib::runner::PollingLoop< Runner, Source, Clock >::set_session_window ( strategy::SessionWindow window)
inline

Sets the session window the market phase is derived from (KRX by default, as in a backtest).

Parameters
windowThe window.

Definition at line 1221 of file loop.hpp.


The documentation for this class was generated from the following file: