5#include "./portfolio.hpp"
53namespace strategy_detail {
68#define ATL_DETECT_DECLARED(NAME) \
69 struct NAME##_fallback { \
72 template <typename S> \
73 struct NAME##_probe : S, NAME##_fallback {}; \
74 template <typename S, typename = void> \
75 struct NAME##_is_ambiguous : std::true_type {}; \
76 template <typename S> \
77 struct NAME##_is_ambiguous<S, std::void_t<decltype(&NAME##_probe<S>::NAME)>> \
78 : std::false_type {}; \
79 template <typename S> \
80 constexpr bool declares_##NAME = std::conjunction_v< \
81 std::is_class<S>, std::negation<std::is_final<S>>, NAME##_is_ambiguous<S> \
93#undef ATL_DETECT_DECLARED
97constexpr bool declares_any_handler =
98 declares_on_start<S> || declares_on_stop<S> || declares_on_bar<S> || declares_on_bars<S> ||
99 declares_on_quote<S> || declares_on_fill<S> || declares_on_order_update<S> || declares_on_phase<S>;
103constexpr bool names_can_be_read = std::is_class_v<S> && !std::is_final_v<S>;
107void require_readable_names() {
109 names_can_be_read<S>,
110 "a final strategy cannot be checked for a handler the runner would skip, because its member names "
111 "cannot be looked up; remove `final` from the strategy class"
120void require_one_bar_handler() {
122 !(declares_on_bar<S> && declares_on_bars<S>),
123 "a strategy declares on_bar or on_bars, not both: on_bar receives each bar, on_bars every "
124 "instrument's bar of a period at once; keep the one this strategy decides with"
148template <
typename S,
typename Ctx>
150 strategy_detail::require_readable_names<S>();
151 if constexpr (strategy_detail::declares_on_start<S>) {
155 static_cast<void>(context);
167template <
typename S,
typename Ctx>
169 strategy_detail::require_readable_names<S>();
170 if constexpr (strategy_detail::declares_on_stop<S>) {
174 static_cast<void>(context);
190template <
typename S,
typename Ctx>
192 strategy_detail::require_readable_names<S>();
193 strategy_detail::require_one_bar_handler<S>();
195 strategy_detail::declares_on_bar<S> || !strategy_detail::declares_on_bars<S>,
196 "this strategy declares on_bars, so its bars are delivered in slices: feed it through "
197 "Runner::record and Runner::feed(const BarSlice&), not one BarEvent at a time"
199 if constexpr (strategy_detail::declares_on_bar<S>) {
203 static_cast<void>(context);
204 static_cast<void>(event);
217template <
typename S,
typename Ctx>
219 strategy_detail::require_readable_names<S>();
220 strategy_detail::require_one_bar_handler<S>();
221 if constexpr (strategy_detail::declares_on_bars<S>) {
225 static_cast<void>(context);
226 static_cast<void>(slice);
239template <
typename S,
typename Ctx>
241 strategy_detail::require_readable_names<S>();
242 if constexpr (strategy_detail::declares_on_quote<S>) {
246 static_cast<void>(context);
247 static_cast<void>(event);
260template <
typename S,
typename Ctx>
262 strategy_detail::require_readable_names<S>();
263 if constexpr (strategy_detail::declares_on_fill<S>) {
267 static_cast<void>(context);
268 static_cast<void>(event);
281template <
typename S,
typename Ctx>
283 strategy_detail::require_readable_names<S>();
284 if constexpr (strategy_detail::declares_on_order_update<S>) {
285 strategy.on_order_update(context, event);
288 static_cast<void>(context);
289 static_cast<void>(event);
303template <
typename S,
typename Ctx>
305 strategy_detail::require_readable_names<S>();
306 if constexpr (strategy_detail::declares_on_phase<S>) {
310 static_cast<void>(context);
311 static_cast<void>(event);
317namespace strategy_detail {
320template <
typename S,
typename Ctx>
321void call_every_handler(
322 S&
strategy, Ctx& context,
const BarEvent& bar,
const BarSlice& slice,
const QuoteEvent& quote,
328 if constexpr (declares_on_bars<S>) {
329 static_cast<void>(bar);
332 static_cast<void>(slice);
345template <
typename S,
typename Ctx>
346constexpr bool compiles_every_handler_call() {
347 if constexpr (names_can_be_read<S>) {
348 static_cast<void>(&call_every_handler<S, Ctx>);
492 static constexpr std::string_view
name =
"probe";
508template <
typename CapsType>
540 void note(std::string_view message) { lines_.emplace_back(message); }
545 const std::vector<std::string>&
lines() const noexcept {
return lines_; }
548 std::vector<std::string> lines_;
555 std::chrono::system_clock::time_point
now() const noexcept {
return now_; }
583 if (known.id_ ==
id) {
614 Log&
log() noexcept {
return log_; }
620 const std::vector<OrderIntent>&
submitted() const noexcept {
return submitted_; }
625 const std::vector<TargetPosition>&
targets() const noexcept {
return targets_; }
630 const std::vector<CancelIntent<ProbeBroker>>&
canceled() const noexcept {
return canceled_; }
633 std::chrono::system_clock::time_point
now_{};
644 std::vector<OrderIntent> submitted_;
645 std::vector<TargetPosition> targets_;
646 std::vector<CancelIntent<ProbeBroker>> canceled_;
662template <
typename S,
typename Ctx>
667 strategy_detail::names_can_be_read<S>,
668 "a final strategy cannot be checked for a handler the runner would skip, because its member names "
669 "cannot be looked up; remove `final` from the strategy class"
675 !std::is_copy_constructible_v<Ctx> && !std::is_constructible_v<Ctx, Ctx&>,
676 "a strategy context must not be copyable, so that a handler taking it by value does not compile "
677 "(what it submitted would go to the copy and be lost); delete the context's copy constructor"
680 strategy_detail::declares_any_handler<S>,
681 "This type declares no strategy handler. Expected at least one of on_start, on_bar, on_bars, "
682 "on_quote, on_fill, on_order_update, on_phase, or on_stop"
685 !(strategy_detail::declares_on_bar<S> && strategy_detail::declares_on_bars<S>),
686 "a strategy declares on_bar or on_bars, not both: on_bar receives each bar, on_bars every "
687 "instrument's bar of a period at once; keep the one this strategy decides with"
691 static constexpr bool value = strategy_detail::compiles_every_handler_call<S, Ctx>();
712#define ATL_ASSERT_STRATEGY(S, Ctx) \
714 ::algotradelib::strategy::StrategyConformance<S, Ctx>::value, \
715 "Strategy does not satisfy the algotradelib strategy surface; see the messages above" \
One order a strategy asks the runner to place.
A log that keeps each line in memory for a test to read.
void note(std::string_view message)
Records one line.
const std::vector< std::string > & lines() const noexcept
Returns the recorded lines.
A context for testing a strategy without a runner: it records what the strategy asks for.
ProbeContextT & operator=(const ProbeContextT &)=delete
Deleted: a probe cannot be copied.
std::chrono::system_clock::time_point now() const noexcept
Returns the current instant.
const std::vector< CancelIntent< ProbeBroker > > & canceled() const noexcept
Returns the cancellations requested.
void cancel(const OrderRef< ProbeBroker > &ref)
Records a cancellation.
TradingCalendar calendar_
const std::vector< TargetPosition > & targets() const noexcept
Returns the target positions submitted.
const Portfolio< ProbeBroker > & portfolio() const noexcept
Returns the portfolio.
CapsType caps
The broker capabilities the strategy sees.
ProbeContextT & operator=(ProbeContextT &&)=default
Move assignment: takes the other probe's state.
ProbeBroker Broker
The broker tag; see ProbeBroker.
Log & log() noexcept
Returns the log.
ProbeContextT(ProbeContextT &&)=default
Move constructor: takes the other probe's state.
const TradingCalendar & calendar() const noexcept
Returns the trading calendar.
void submit(TargetPosition target)
Records a target position.
std::vector< Instrument > instruments_
Portfolio< ProbeBroker > portfolio_
std::chrono::system_clock::time_point now_
void submit(OrderIntent intent)
Records an order intent.
MarketPhase phase() const noexcept
Returns the market phase.
const std::vector< OrderIntent > & submitted() const noexcept
Returns the order intents submitted.
ProbeContextT()=default
Constructs an empty probe.
std::optional< Instrument > instrument(const InstrumentId &id) const
Looks up an instrument's reference data in instruments_.
ProbeContextT(const ProbeContextT &)=delete
Deleted: like a runner's context, a probe cannot be copied.
#define ATL_DETECT_DECLARED(NAME)
The events a runner delivers to a strategy's handlers, and the market phase.
What a strategy asks the runner for: orders (OrderIntent), target positions (TargetPosition) and canc...
Calls a strategy's handlers, skipping the ones it does not declare.
void phase(S &strategy, Ctx &context, const MarketPhase &event)
Calls strategy.on_phase(context, event) if the strategy declares on_phase; otherwise does nothing.
void quote(S &strategy, Ctx &context, const QuoteEvent &event)
Calls strategy.on_quote(context, event) if the strategy declares on_quote; otherwise does nothing.
void start(S &strategy, Ctx &context)
Calls strategy.on_start(context) if the strategy declares on_start; otherwise does nothing.
void stop(S &strategy, Ctx &context)
Calls strategy.on_stop(context) if the strategy declares on_stop; otherwise does nothing.
void bar(S &strategy, Ctx &context, const BarEvent &event)
Calls strategy.on_bar(context, event) if the strategy declares on_bar; otherwise does nothing.
void fill(S &strategy, Ctx &context, const Fill &event)
Calls strategy.on_fill(context, event) if the strategy declares on_fill; otherwise does nothing.
void order_update(S &strategy, Ctx &context, const OrderSnapshot &event)
Calls strategy.on_order_update(context, event) if the strategy declares on_order_update; otherwise do...
void bars(S &strategy, Ctx &context, const BarSlice &slice)
Calls strategy.on_bars(context, slice) if the strategy declares on_bars; otherwise does nothing.
What a strategy sees and emits: events, intents, the portfolio, reconciliation and risk limits.
MarketPhase
Where the clock stands relative to the trading session.
@ OPEN
Inside the session.
ProbeContextT< NoCaps > MinimalContext
A probe context whose broker implements nothing optional.
ProbeContextT< AllCaps > FullContext
A probe context whose broker implements everything optional.
The library: broker-neutral types (core), and the modules below.
SellableSource
Where the library gets a broker's sellable quantity.
@ 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.
BarInterval
The period one bar covers.
Identifies an instrument: its country, venue and symbol.
A broker's reference to one order, used to amend or cancel it.
The state of one order, as a broker listing reported it.
What a sellable quantity covers, and how RiskGuard must treat resting sells against it.
@ WHOLE_HOLDING
Every share held of the instrument, whatever venue or lot.
@ DEDUCT
Subtract every resting sell from the figure.
The trading days a broker reported for one market, ascending by date.
A capability set in which the broker implements everything optional.
static constexpr bool has_batch_reference_data
Whether one reference-data request can cover several instruments.
static constexpr bool has_calendar_sessions
Whether the broker publishes intraday session hours (only Toss); the library does not expose them yet...
static constexpr bool has_client_order_id
Whether the broker accepts an idempotency key (OrderRequest::client_order_id_).
static constexpr bool has_trigger_price
Whether the adapter sends trigger prices (stop-limit orders).
static constexpr bool supports_interval(BarInterval)
Tests whether the broker serves bars of an interval (the unnamed argument).
static constexpr bool has_trade_prints
Whether the broker publishes recent trades.
static constexpr bool has_history_limit
Whether an order-history inquiry accepts a row limit (OrderHistoryQuery::limit_).
static constexpr bool working_orders_state_terminal
Whether the working-order listing can show a terminal status (filled, cancelled, ....
static constexpr SellableBasis sellable_inquiry_basis
The whole holding, with resting sells deducted (the basis the library assumes for Toss,...
static constexpr bool has_batch_prices
Whether the broker quotes several instruments in one request.
static constexpr bool has_working_orders
Whether the broker lists the orders still open to amendment. A listing shows only what the broker's A...
static constexpr bool has_price_limits
Whether the broker publishes the day's price limits.
static constexpr bool has_amendable_quantity
Whether the broker reports the quantity still amendable on an open order.
static constexpr bool has_partial_cancel
Whether the broker can cancel part of an order.
static constexpr bool has_calendar_range
Whether one calendar request covers a span of dates.
static constexpr bool has_depth
Whether the broker publishes the order book (depth).
static constexpr bool history_requires_range
Whether an order-history inquiry needs a date range (OrderHistoryQuery::from_).
static constexpr bool has_buying_power
Whether the broker reports buying power.
static constexpr bool has_account_discovery
Whether the broker lists the selectable accounts.
static constexpr bool has_calendar
Whether the broker publishes the trading calendar.
static constexpr SellableSource sellable_source
The sellable quantity comes from an inquiry.
static constexpr bool has_order_by_id
Whether the broker can look up one order by its id.
static constexpr bool has_commission_schedule
Whether the broker publishes the account's commission rates.
static constexpr bool has_reference_data
Whether the broker publishes instrument reference data.
static constexpr bool data_only
false: this broker takes orders.
static constexpr bool has_buying_power_quantity
Whether the broker can also say how many shares are affordable.
static constexpr bool has_history_symbol_filter
Whether an order-history inquiry can filter by symbol.
static constexpr bool has_history_cursor
Whether an order-history answer can carry a cursor to the next page.
One bar, delivered to on_bar.
Every instrument's bar of one interval for one period end, delivered to on_bars.
A request to cancel one working order.
One execution of an order the strategy placed, delivered to on_fill.
A capability set in which the broker implements nothing optional.
static constexpr bool history_requires_range
Whether an order-history inquiry needs a date range (OrderHistoryQuery::from_).
static constexpr bool has_reference_data
Whether the broker publishes instrument reference data.
static constexpr bool has_buying_power
Whether the broker reports buying power.
static constexpr bool has_client_order_id
Whether the broker accepts an idempotency key (OrderRequest::client_order_id_).
static constexpr bool has_calendar_sessions
Whether the broker publishes intraday session hours (only Toss); the library does not expose them yet...
static constexpr bool has_order_by_id
Whether the broker can look up one order by its id.
static constexpr bool data_only
Whether the client is a market-data source that takes no orders (built on DataOnlyTraits).
static constexpr bool has_batch_reference_data
Whether one reference-data request can cover several instruments.
static constexpr bool has_account_discovery
Whether the broker lists the selectable accounts.
static constexpr bool has_batch_prices
Whether the broker quotes several instruments in one request.
static constexpr bool has_history_limit
Whether an order-history inquiry accepts a row limit (OrderHistoryQuery::limit_).
static constexpr bool has_buying_power_quantity
Whether the broker can also say how many shares are affordable.
static constexpr bool has_commission_schedule
Whether the broker publishes the account's commission rates.
static constexpr bool supports_interval(BarInterval)
Tests whether the broker serves bars of an interval (the unnamed argument).
static constexpr bool working_orders_state_terminal
Whether the working-order listing can show a terminal status (filled, cancelled, ....
static constexpr SellableBasis sellable_inquiry_basis
Unestablished, since there is no inquiry.
static constexpr bool has_trigger_price
Whether the adapter sends trigger prices (stop-limit orders).
static constexpr bool has_calendar_range
Whether one calendar request covers a span of dates.
static constexpr bool has_history_cursor
Whether an order-history answer can carry a cursor to the next page.
static constexpr bool has_trade_prints
Whether the broker publishes recent trades.
static constexpr bool has_amendable_quantity
Whether the broker reports the quantity still amendable on an open order.
static constexpr bool has_working_orders
Whether the broker lists the orders still open to amendment. A listing shows only what the broker's A...
static constexpr bool has_price_limits
Whether the broker publishes the day's price limits.
static constexpr bool has_calendar
Whether the broker publishes the trading calendar.
static constexpr bool has_partial_cancel
Whether the broker can cancel part of an order.
static constexpr bool has_depth
Whether the broker publishes the order book (depth).
static constexpr bool has_history_symbol_filter
Whether an order-history inquiry can filter by symbol.
static constexpr SellableSource sellable_source
No sellable-quantity figure is available.
What the account holds and which orders are still working, as a strategy sees it.
The broker tag of a probe context; nothing it issues reaches a market.
static constexpr std::string_view name
The broker name an order reference prints.
A quote the runner has just read, delivered to on_quote.
The position a strategy wants to hold in one instrument.