algotradelib 0.0.0.9000
C++ header-only library for algorithmic trading
Loading...
Searching...
No Matches
broker_session.hpp
1#pragma once
2
3#include "./broker_caps.hpp"
4#include "./broker_traits.hpp"
5#include "./calendar.hpp"
6#include "./instrument.hpp"
7
8#include "../network/rate_limit.hpp"
9
10#include <boost/asio/associated_executor.hpp>
11#include <boost/asio/async_result.hpp>
12#include <boost/asio/bind_executor.hpp>
13#include <boost/asio/post.hpp>
14#include <boost/asio/system_executor.hpp>
15
16#include <memory>
17#include <optional>
18#include <stdexcept>
19#include <string>
20#include <utility>
21#include <vector>
22
23namespace algotradelib {
24
25namespace net = boost::asio;
26
27namespace port_detail {
28
29// Converts a broker's account listing into `AccountInfo` rows (shared by both sessions).
30template <typename Client, typename Items>
31Result<std::vector<AccountInfo<Client>>> to_account_info(Result<Items> raw) {
32 using Traits = BrokerTraits<Client>;
33 using Accounts = std::vector<AccountInfo<Client>>;
34 if (!raw) {
35 return Result<Accounts>(std::move(raw.error()));
36 }
37 Accounts listed;
38 listed.reserve(raw.value().size());
39 for (const auto& item : raw.value()) {
40 AccountInfo<Client> info;
41 info.display_number_ = Traits::display_of(item);
42 info.type_ = Traits::type_of(item);
43 info.selector_ = Traits::selector_of(item);
44 listed.push_back(std::move(info));
45 }
46 return Result<Accounts>(std::move(listed));
47}
48
61template <typename Client>
62std::optional<BrokerError> trigger_refusal(const std::optional<Decimal64>& trigger) {
64 return std::nullopt;
65 } else {
66 if (!trigger) {
67 return std::nullopt;
68 }
69 BrokerError error;
70 error.category_ = ErrorCategory::INVALID_ARGUMENT;
71 error.reached_broker_ = false;
72 error.broker_code_ = "unsupported_trigger_price";
73 error.message_ =
74 "this broker does not declare that it transmits a trigger price, so an order carrying one "
75 "would be sent without it; place the order without a trigger or through a broker whose "
76 "BrokerCaps::has_trigger_price is true";
77 return error;
78 }
79}
80
88template <typename Client>
89std::optional<BrokerError> prepare_place(const OrderRequest& order) {
90 if (auto refused = trigger_refusal<Client>(order.trigger_price_)) {
91 return refused;
92 }
93 return BrokerTraits<Client>::validate_place(order);
94}
95
105template <typename Client>
106Result<typename BrokerTraits<Client>::Amendment> prepare_replace(
107 const OrderRef<typename BrokerTraits<Client>::BrokerTag>& ref, const AmendInstruction& instruction
108) {
109 using Amendment = typename BrokerTraits<Client>::Amendment;
110 if (auto refused = trigger_refusal<Client>(amended_trigger_price(instruction))) {
111 return Result<Amendment>(std::move(*refused));
112 }
113 return BrokerTraits<Client>::to_replacement(ref, instruction);
114}
115
116} // namespace port_detail
117
132template <typename Client>
134public:
137
140
142 using Account = typename Traits::Account;
143
145 using Config = typename Traits::Config;
146
148 using BrokerTag = typename Traits::BrokerTag;
149
152
155
158
161
162 static_assert(
163 !Traits::blocking,
164 "BrokerSession requires an asynchronous client; use BlockingSession for a blocking facade"
165 );
166
173 BrokerSession(std::shared_ptr<Client> client, Account account)
174 : client_(std::move(client)), account_(std::move(account)) {
175 if (!client_) {
176 throw std::invalid_argument("broker session requires a client");
177 }
178 }
179
184 static constexpr std::string_view broker_name() noexcept { return Traits::name; }
185
190 const Account& account() const noexcept { return account_; }
191
200 Client& client() noexcept { return *client_; }
201
206 const Client& client() const noexcept { return *client_; }
207
212 const std::shared_ptr<Client>& shared_client() const noexcept { return client_; }
213
228 return Traits::rate_limit_stats(*client_);
229 }
230
232 void close() { client_->close(); }
233
243 template <typename CompletionToken>
244 auto async_quote(InstrumentId instrument, CompletionToken&& token) {
245 return translate<QuoteSnapshot>(
246 std::forward<CompletionToken>(token),
247 [client = client_, instrument = std::move(instrument)](auto&& sink) mutable {
248 Traits::quote(*client, std::move(instrument), std::forward<decltype(sink)>(sink));
249 },
250 [](Result<QuoteSnapshot> raw) { return raw; }
251 );
252 }
253
262 template <typename CompletionToken>
263 auto async_holdings(CompletionToken&& token) {
264 return translate<HoldingsSnapshot>(
265 std::forward<CompletionToken>(token),
266 [client = client_, account = account_](auto&& sink) mutable {
267 Traits::holdings(*client, account, std::forward<decltype(sink)>(sink));
268 },
269 [](Result<HoldingsSnapshot> raw) { return raw; }
270 );
271 }
272
286 template <typename CompletionToken>
287 auto async_buying_power(Currency currency, CompletionToken&& token) {
288 if constexpr (Caps::has_buying_power) {
289 return translate<BuyingPower>(
290 std::forward<CompletionToken>(token),
291 [client = client_, account = account_, currency](auto&& sink) mutable {
292 Traits::buying_power(*client, account, currency, std::forward<decltype(sink)>(sink));
293 },
294 [](Result<BuyingPower> raw) { return raw; }
295 );
296 } else {
297 static_assert(
298 port_detail::dependent_false<Client>,
299 "this broker does not answer what the account may spend; gate the call with "
300 "BrokerCaps<Client>::has_buying_power"
301 );
302 }
303 }
304
317 template <typename CompletionToken>
318 auto async_sellable_quantity(InstrumentId instrument, CompletionToken&& token) {
320 return translate<Decimal64>(
321 std::forward<CompletionToken>(token),
322 [client = client_, account = account_, instrument = std::move(instrument)](
323 auto&& sink
324 ) mutable {
325 Traits::sellable_quantity(
326 *client, account, std::move(instrument), std::forward<decltype(sink)>(sink)
327 );
328 },
329 [](Result<Decimal64> raw) { return raw; }
330 );
331 } else {
332 static_assert(
333 port_detail::dependent_false<Client>,
334 "this broker has no separate sellable-quantity inquiry; read "
335 "HoldingPosition::sellable_quantity_ from the holdings snapshot, and gate on "
336 "BrokerCaps<Client>::sellable_source"
337 );
338 }
339 }
340
352 template <typename CompletionToken>
353 auto async_commissions(CompletionToken&& token) {
354 if constexpr (Caps::has_commission_schedule) {
355 return translate<CommissionSchedule>(
356 std::forward<CompletionToken>(token),
357 [client = client_, account = account_](auto&& sink) mutable {
358 Traits::commissions(*client, account, std::forward<decltype(sink)>(sink));
359 },
360 [](Result<CommissionSchedule> raw) { return raw; }
361 );
362 } else {
363 static_assert(
364 port_detail::dependent_false<Client>,
365 "this broker publishes no commission schedule; gate the call with "
366 "BrokerCaps<Client>::has_commission_schedule"
367 );
368 }
369 }
370
388 template <typename CompletionToken>
389 auto async_place(OrderRequest order, CompletionToken&& token) {
390 if (auto refused = port_detail::prepare_place<Client>(order)) {
391 return complete_error<Placed>(
392 std::forward<CompletionToken>(token), std::move(*refused)
393 );
394 }
395 return translate<Placed>(
396 std::forward<CompletionToken>(token),
397 [client = client_, account = account_, order = std::move(order)](auto&& sink) mutable {
398 Traits::place_order(
399 *client, account, std::move(order), std::forward<decltype(sink)>(sink)
400 );
401 },
402 [](Result<typename Traits::PlaceAck> raw) { return Traits::normalize_place(std::move(raw)); }
403 );
404 }
405
422 template <typename CompletionToken>
423 auto async_replace(Ref ref, AmendInstruction instruction, CompletionToken&& token) {
424 auto amendment = port_detail::prepare_replace<Client>(ref, instruction);
425 if (!amendment) {
426 return complete_error<Placed>(
427 std::forward<CompletionToken>(token), std::move(amendment.error())
428 );
429 }
430 return translate<Placed>(
431 std::forward<CompletionToken>(token),
432 [
433 client = client_,
434 account = account_,
435 value = std::move(amendment.value())
436 ](auto&& sink) mutable {
437 Traits::replace_order(
438 *client, account, std::move(value), std::forward<decltype(sink)>(sink)
439 );
440 },
441 [](Result<typename Traits::PlaceAck> raw) { return Traits::normalize_place(std::move(raw)); }
442 );
443 }
444
465 template <typename CompletionToken>
466 auto async_cancel(Ref ref, AmendInstruction instruction, CompletionToken&& token) {
467 auto amendment = Traits::to_cancellation(ref, instruction);
468 if (!amendment) {
469 return complete_error<Placed>(
470 std::forward<CompletionToken>(token), std::move(amendment.error())
471 );
472 }
473 return translate<Placed>(
474 std::forward<CompletionToken>(token),
475 [
476 client = client_,
477 account = account_,
478 value = std::move(amendment.value())
479 ](auto&& sink) mutable {
480 Traits::cancel_order(
481 *client, account, std::move(value), std::forward<decltype(sink)>(sink)
482 );
483 },
484 [](Result<typename Traits::PlaceAck> raw) { return Traits::normalize_place(std::move(raw)); }
485 );
486 }
487
499 template <typename CompletionToken>
500 auto async_working_orders(CompletionToken&& token) {
501 if constexpr (Caps::has_working_orders) {
502 return translate<WorkingSet>(
503 std::forward<CompletionToken>(token),
504 [client = client_, account = account_](auto&& sink) mutable {
505 Traits::working_orders(*client, account, std::forward<decltype(sink)>(sink));
506 },
508 return Traits::normalize_working_orders(std::move(raw));
509 }
510 );
511 } else {
512 static_assert(
513 port_detail::dependent_false<Client>,
514 "this broker does not publish its amendable orders; gate the call with "
515 "BrokerCaps<Client>::has_working_orders"
516 );
517 }
518 }
519
530 template <typename CompletionToken>
531 auto async_order_history(OrderHistoryQuery query, CompletionToken&& token) {
532 auto converted = Traits::to_history_query(account_, query);
533 if (!converted) {
534 return complete_error<OrderPage>(
535 std::forward<CompletionToken>(token), std::move(converted.error())
536 );
537 }
538 return translate<OrderPage>(
539 std::forward<CompletionToken>(token),
540 [
541 client = client_,
542 account = account_,
543 value = std::move(converted.value())
544 ](auto&& sink) mutable {
545 Traits::order_history(
546 *client, account, std::move(value), std::forward<decltype(sink)>(sink)
547 );
548 },
549 [](Result<OrderPage> raw) { return raw; }
550 );
551 }
552
565 template <typename CompletionToken>
566 auto async_bars(BarQuery query, CompletionToken&& token) {
567 if (auto invalid = Traits::validate_bars(query)) {
568 return complete_error<BarSeries>(
569 std::forward<CompletionToken>(token), std::move(*invalid)
570 );
571 }
572 return translate<BarSeries>(
573 std::forward<CompletionToken>(token),
574 [client = client_, query = std::move(query)](auto&& sink) mutable {
575 Traits::bars(*client, std::move(query), std::forward<decltype(sink)>(sink));
576 },
577 [](Result<BarSeries> raw) { return raw; }
578 );
579 }
580
593 template <typename CompletionToken>
594 auto async_instrument(InstrumentId instrument, CompletionToken&& token) {
595 if constexpr (Caps::has_reference_data) {
596 return translate<Instrument>(
597 std::forward<CompletionToken>(token),
598 [client = client_, instrument = std::move(instrument)](auto&& sink) mutable {
599 Traits::instrument(*client, std::move(instrument), std::forward<decltype(sink)>(sink));
600 },
601 [](Result<Instrument> raw) { return raw; }
602 );
603 } else {
604 static_assert(
605 port_detail::dependent_false<Client>,
606 "this broker does not publish instrument reference data; gate the call with "
607 "BrokerCaps<Client>::has_reference_data"
608 );
609 }
610 }
611
625 template <typename CompletionToken>
626 auto async_calendar(Country country, std::optional<Date> from, CompletionToken&& token) {
627 if constexpr (Caps::has_calendar) {
628 if (auto invalid = Traits::validate_calendar(country, from)) {
629 return complete_error<TradingCalendar>(
630 std::forward<CompletionToken>(token), std::move(*invalid)
631 );
632 }
633 return translate<TradingCalendar>(
634 std::forward<CompletionToken>(token),
635 [client = client_, country, from](auto&& sink) mutable {
636 Traits::calendar(*client, country, from, std::forward<decltype(sink)>(sink));
637 },
638 [](Result<TradingCalendar> raw) { return raw; }
639 );
640 } else {
641 static_assert(
642 port_detail::dependent_false<Client>,
643 "this broker does not publish a trading calendar; gate the call with "
644 "BrokerCaps<Client>::has_calendar"
645 );
646 }
647 }
648
660 template <typename CompletionToken>
661 auto async_price_limits(InstrumentId instrument, CompletionToken&& token) {
662 if constexpr (Caps::has_price_limits) {
663 return translate<PriceLimits>(
664 std::forward<CompletionToken>(token),
665 [client = client_, instrument = std::move(instrument)](auto&& sink) mutable {
666 Traits::price_limits(
667 *client, std::move(instrument), std::forward<decltype(sink)>(sink)
668 );
669 },
670 [](Result<PriceLimits> raw) { return raw; }
671 );
672 } else {
673 static_assert(
674 port_detail::dependent_false<Client>,
675 "this broker does not publish price limits; gate the call with "
676 "BrokerCaps<Client>::has_price_limits"
677 );
678 }
679 }
680
693 template <typename CompletionToken>
694 auto async_trades(InstrumentId instrument, int count, CompletionToken&& token) {
695 if constexpr (Caps::has_trade_prints) {
696 return translate<std::vector<TradePrint>>(
697 std::forward<CompletionToken>(token),
698 [client = client_, instrument = std::move(instrument), count](auto&& sink) mutable {
699 Traits::trades(
700 *client, std::move(instrument), count, std::forward<decltype(sink)>(sink)
701 );
702 },
703 [](Result<std::vector<TradePrint>> raw) { return raw; }
704 );
705 } else {
706 static_assert(
707 port_detail::dependent_false<Client>,
708 "this broker does not publish recent executions; gate the call with "
709 "BrokerCaps<Client>::has_trade_prints"
710 );
711 }
712 }
713
725 template <typename CompletionToken>
726 auto async_prices(std::vector<InstrumentId> instruments, CompletionToken&& token) {
727 if constexpr (Caps::has_batch_prices) {
728 return translate<std::vector<QuoteSnapshot>>(
729 std::forward<CompletionToken>(token),
730 [client = client_, instruments = std::move(instruments)](auto&& sink) mutable {
731 Traits::prices(*client, std::move(instruments), std::forward<decltype(sink)>(sink));
732 },
733 [](Result<std::vector<QuoteSnapshot>> raw) { return raw; }
734 );
735 } else {
736 static_assert(
737 port_detail::dependent_false<Client>,
738 "this broker quotes one instrument per request; gate the call with "
739 "BrokerCaps<Client>::has_batch_prices and call async_quote per instrument"
740 );
741 }
742 }
743
755 template <typename CompletionToken>
756 auto async_depth(InstrumentId instrument, CompletionToken&& token) {
757 if constexpr (Caps::has_depth) {
758 return translate<DepthSnapshot>(
759 std::forward<CompletionToken>(token),
760 [client = client_, instrument = std::move(instrument)](auto&& sink) mutable {
761 Traits::depth(*client, std::move(instrument), std::forward<decltype(sink)>(sink));
762 },
763 [](Result<DepthSnapshot> raw) { return raw; }
764 );
765 } else {
766 static_assert(
767 port_detail::dependent_false<Client>,
768 "this broker does not publish an order book; gate the call with "
769 "BrokerCaps<Client>::has_depth"
770 );
771 }
772 }
773
785 template <typename CompletionToken>
786 auto async_order(Ref ref, CompletionToken&& token) {
787 if constexpr (Caps::has_order_by_id) {
788 return translate<OrderSnapshot>(
789 std::forward<CompletionToken>(token),
790 [client = client_, account = account_, id = ref.primary_](auto&& sink) mutable {
791 Traits::order_by_id(*client, account, std::move(id), std::forward<decltype(sink)>(sink));
792 },
793 [](Result<OrderSnapshot> raw) { return raw; }
794 );
795 } else {
796 static_assert(
797 port_detail::dependent_false<Client>,
798 "this broker does not retrieve one order by identifier; gate the call with "
799 "BrokerCaps<Client>::has_order_by_id and read the order from "
800 "async_working_orders or async_order_history instead"
801 );
802 }
803 }
804
805private:
806 // Initiates one client call; normalization and the handler run once, on the handler's executor.
807 template <typename Out, typename Token, typename Call, typename Normalize>
808 static auto translate(Token&& token, Call call, Normalize normalize) {
809 return net::async_initiate<Token, void(Result<Out>)>(
810 [call = std::move(call), normalize = std::move(normalize)](auto&& handler) mutable {
811 auto executor = net::get_associated_executor(handler);
812 call(
813 net::bind_executor(
814 executor,
815 [
816 handler = std::forward<decltype(handler)>(handler),
817 normalize = std::move(normalize)
818 ](auto raw) mutable {
819 std::move(handler)(normalize(std::move(raw)));
820 }
821 )
822 );
823 },
824 token
825 );
826 }
827
828 // Completes with a pre-flight error, posted (never inline) to the handler's executor, or the
829 // system executor if it has none.
830 template <typename Out, typename Token>
831 static auto complete_error(Token&& token, BrokerError error) {
832 return net::async_initiate<Token, void(Result<Out>)>(
833 [error = std::move(error)](auto&& handler) mutable {
834 auto executor = net::get_associated_executor(handler, net::system_executor());
835 net::post(
836 executor,
837 [
838 handler = std::forward<decltype(handler)>(handler),
839 error = std::move(error)
840 ]() mutable {
841 std::move(handler)(Result<Out>(std::move(error)));
842 }
843 );
844 },
845 token
846 );
847 }
848
849 std::shared_ptr<Client> client_;
850 Account account_;
851};
852
861template <typename Client>
863public:
866
869
871 using Account = typename Traits::Account;
872
874 using Config = typename Traits::Config;
875
877 using BrokerTag = typename Traits::BrokerTag;
878
881
884
887
890
891 static_assert(
892 Traits::blocking,
893 "BlockingSession requires a blocking client facade; use BrokerSession for an "
894 "asynchronous client"
895 );
896
903 BlockingSession(std::shared_ptr<Client> client, Account account)
904 : client_(std::move(client)), account_(std::move(account)) {
905 if (!client_) {
906 throw std::invalid_argument("broker session requires a client");
907 }
908 }
909
914 static constexpr std::string_view broker_name() noexcept { return Traits::name; }
915
920 const Account& account() const noexcept { return account_; }
921
926 Client& client() noexcept { return *client_; }
927
932 const Client& client() const noexcept { return *client_; }
933
944 return Traits::rate_limit_stats(*client_);
945 }
946
953 return Traits::quote(*client_, std::move(instrument));
954 }
955
960 Result<HoldingsSnapshot> holdings() { return Traits::holdings(*client_, account_); }
961
968 if constexpr (Caps::has_buying_power) {
969 return Traits::buying_power(*client_, account_, currency);
970 } else {
971 static_assert(
972 port_detail::dependent_false<Client>,
973 "this broker does not answer what the account may spend; gate the call with "
974 "BrokerCaps<Client>::has_buying_power"
975 );
976 }
977 }
978
986 return Traits::sellable_quantity(*client_, account_, std::move(instrument));
987 } else {
988 static_assert(
989 port_detail::dependent_false<Client>,
990 "this broker has no separate sellable-quantity inquiry; read "
991 "HoldingPosition::sellable_quantity_ from the holdings snapshot, and gate on "
992 "BrokerCaps<Client>::sellable_source"
993 );
994 }
995 }
996
1002 if constexpr (Caps::has_commission_schedule) {
1003 return Traits::commissions(*client_, account_);
1004 } else {
1005 static_assert(
1006 port_detail::dependent_false<Client>,
1007 "this broker publishes no commission schedule; gate the call with "
1008 "BrokerCaps<Client>::has_commission_schedule"
1009 );
1010 }
1011 }
1012
1019 if constexpr (Caps::has_reference_data) {
1020 return Traits::instrument(*client_, std::move(subject));
1021 } else {
1022 static_assert(
1023 port_detail::dependent_false<Client>,
1024 "this broker does not publish instrument reference data; gate the call with "
1025 "BrokerCaps<Client>::has_reference_data"
1026 );
1027 }
1028 }
1029
1036 Result<TradingCalendar> calendar(Country country, std::optional<Date> from = std::nullopt) {
1037 if constexpr (Caps::has_calendar) {
1038 if (auto invalid = Traits::validate_calendar(country, from)) {
1039 return Result<TradingCalendar>(std::move(*invalid));
1040 }
1041 return Traits::calendar(*client_, country, from);
1042 } else {
1043 static_assert(
1044 port_detail::dependent_false<Client>,
1045 "this broker does not publish a trading calendar; gate the call with "
1046 "BrokerCaps<Client>::has_calendar"
1047 );
1048 }
1049 }
1050
1057 if constexpr (Caps::has_price_limits) {
1058 return Traits::price_limits(*client_, std::move(instrument));
1059 } else {
1060 static_assert(
1061 port_detail::dependent_false<Client>,
1062 "this broker does not publish price limits; gate the call with "
1063 "BrokerCaps<Client>::has_price_limits"
1064 );
1065 }
1066 }
1067
1075 if constexpr (Caps::has_trade_prints) {
1076 return Traits::trades(*client_, std::move(instrument), count);
1077 } else {
1078 static_assert(
1079 port_detail::dependent_false<Client>,
1080 "this broker does not publish recent executions; gate the call with "
1081 "BrokerCaps<Client>::has_trade_prints"
1082 );
1083 }
1084 }
1085
1091 Result<std::vector<QuoteSnapshot>> prices(std::vector<InstrumentId> instruments) {
1092 if constexpr (Caps::has_batch_prices) {
1093 return Traits::prices(*client_, std::move(instruments));
1094 } else {
1095 static_assert(
1096 port_detail::dependent_false<Client>,
1097 "this broker quotes one instrument per request; gate the call with "
1098 "BrokerCaps<Client>::has_batch_prices and call quote per instrument"
1099 );
1100 }
1101 }
1102
1109 if constexpr (Caps::has_depth) {
1110 return Traits::depth(*client_, std::move(instrument));
1111 } else {
1112 static_assert(
1113 port_detail::dependent_false<Client>,
1114 "this broker does not publish an order book; gate the call with "
1115 "BrokerCaps<Client>::has_depth"
1116 );
1117 }
1118 }
1119
1126 if (auto invalid = Traits::validate_bars(query)) {
1127 return Result<BarSeries>(std::move(*invalid));
1128 }
1129 return Traits::bars(*client_, std::move(query));
1130 }
1131
1144 if (auto refused = port_detail::prepare_place<Client>(order)) {
1145 return Result<Placed>(std::move(*refused));
1146 }
1147 return Traits::normalize_place(Traits::place_order(*client_, account_, std::move(order)));
1148 }
1149
1162 Result<Placed> replace(const Ref& ref, const AmendInstruction& instruction) {
1163 auto amendment = port_detail::prepare_replace<Client>(ref, instruction);
1164 if (!amendment) {
1165 return Result<Placed>(std::move(amendment.error()));
1166 }
1167 return Traits::normalize_place(
1168 Traits::replace_order(*client_, account_, std::move(amendment.value()))
1169 );
1170 }
1171
1185 Result<Placed> cancel(const Ref& ref, const AmendInstruction& instruction) {
1186 auto amendment = Traits::to_cancellation(ref, instruction);
1187 if (!amendment) {
1188 return Result<Placed>(std::move(amendment.error()));
1189 }
1190 return Traits::normalize_place(
1191 Traits::cancel_order(*client_, account_, std::move(amendment.value()))
1192 );
1193 }
1194
1200 if constexpr (Caps::has_working_orders) {
1201 return Traits::normalize_working_orders(Traits::working_orders(*client_, account_));
1202 } else {
1203 static_assert(
1204 port_detail::dependent_false<Client>,
1205 "this broker does not publish its amendable orders; gate the call with "
1206 "BrokerCaps<Client>::has_working_orders"
1207 );
1208 }
1209 }
1210
1217 auto converted = Traits::to_history_query(account_, query);
1218 if (!converted) {
1219 return Result<OrderPage>(std::move(converted.error()));
1220 }
1221 return Traits::order_history(*client_, account_, std::move(converted.value()));
1222 }
1223
1230 if constexpr (Caps::has_order_by_id) {
1231 return Traits::order_by_id(*client_, account_, ref.primary_);
1232 } else {
1233 static_assert(
1234 port_detail::dependent_false<Client>,
1235 "this broker does not retrieve one order by identifier; gate the call with "
1236 "BrokerCaps<Client>::has_order_by_id and read the order from working_orders or "
1237 "order_history instead"
1238 );
1239 }
1240 }
1241
1247 if constexpr (Caps::has_account_discovery) {
1248 return port_detail::to_account_info<Client>(Traits::accounts(*client_));
1249 } else {
1250 static_assert(
1251 port_detail::dependent_false<Client>,
1252 "this broker does not publish selectable accounts; its account selector comes from "
1253 "configuration, so gate the call with BrokerCaps<Client>::has_account_discovery"
1254 );
1255 }
1256 }
1257
1258private:
1259 std::shared_ptr<Client> client_;
1260 Account account_;
1261};
1262
1273template <typename Client, typename CompletionToken>
1274auto async_accounts(Client& client, CompletionToken&& token) {
1276 using Traits = BrokerTraits<Client>;
1277 using Accounts = std::vector<AccountInfo<Client>>;
1278 return net::async_initiate<CompletionToken, void(Result<Accounts>)>(
1279 [&client](auto&& handler) mutable {
1280 auto executor = net::get_associated_executor(handler);
1281 Traits::accounts(
1282 client,
1283 net::bind_executor(
1284 executor,
1285 [handler = std::forward<decltype(handler)>(handler)](auto raw) mutable {
1286 std::move(handler)(port_detail::to_account_info<Client>(std::move(raw)));
1287 }
1288 )
1289 );
1290 },
1291 token
1292 );
1293 } else {
1294 static_assert(
1295 port_detail::dependent_false<Client>,
1296 "this broker does not publish selectable accounts; its account selector comes from "
1297 "configuration, so gate the call with BrokerCaps<Client>::has_account_discovery"
1298 );
1299 }
1300}
1301
1310template <typename Client>
1313 return port_detail::to_account_info<Client>(BrokerTraits<Client>::accounts(client));
1314 } else {
1315 static_assert(
1316 port_detail::dependent_false<Client>,
1317 "this broker does not publish selectable accounts; its account selector comes from "
1318 "configuration, so gate the call with BrokerCaps<Client>::has_account_discovery"
1319 );
1320 }
1321}
1322
1323} // namespace algotradelib
WorkingOrder< BrokerTag > Working
One working order of this broker.
Result< OrderPage > order_history(const OrderHistoryQuery &query)
Returns order history: placed orders and their executions.
Result< Placed > place(OrderRequest order)
Places a new order.
Result< WorkingSet > working_orders()
Returns the orders still open to modification or cancellation; requires Caps::has_working_orders.
typename Traits::Config Config
The broker's configuration type.
typename Traits::Account Account
The broker's account selector type.
Result< OrderSnapshot > order(const Ref &ref)
Returns one order by its reference; requires Caps::has_order_by_id.
Result< BarSeries > bars(BarQuery query)
Returns bars for an instrument, oldest first, paging until count_ bars are read.
Result< QuoteSnapshot > quote(InstrumentId instrument)
Returns the current quote for an instrument.
Result< Placed > replace(const Ref &ref, const AmendInstruction &instruction)
Modifies a working order.
BrokerCaps< Client > Caps
The client's capabilities.
Result< PriceLimits > price_limits(InstrumentId instrument)
Returns an instrument's price limits for today; requires Caps::has_price_limits.
Result< std::vector< TradePrint > > trades(InstrumentId instrument, int count)
Returns recent trades for an instrument, newest first; requires Caps::has_trade_prints.
Result< BuyingPower > buying_power(Currency currency)
Returns what the account may spend in a currency; requires Caps::has_buying_power.
Result< Placed > cancel(const Ref &ref, const AmendInstruction &instruction)
Cancels a working order, in full or in part.
Result< HoldingsSnapshot > holdings()
Returns the account's holdings.
const Client & client() const noexcept
Returns the broker client.
Result< Decimal64 > sellable_quantity(InstrumentId instrument)
Returns how much of an instrument the account may sell; only where Caps::sellable_source is INQUIRY.
const network::RateLimitStats * rate_limit_stats() const
Returns the client's request-pacing counters.
Client & client() noexcept
Returns the broker client, for broker-specific operations the session does not offer.
OrderRef< BrokerTag > Ref
The order reference type this session issues and accepts.
Result< DepthSnapshot > depth(InstrumentId instrument)
Returns an instrument's order book, best levels first; requires Caps::has_depth.
Result< TradingCalendar > calendar(Country country, std::optional< Date > from=std::nullopt)
Returns the trading calendar; requires Caps::has_calendar.
Result< CommissionSchedule > commissions()
Returns the account's commission rates; requires Caps::has_commission_schedule.
const Account & account() const noexcept
Returns the bound account.
BrokerTraits< Client > Traits
The client's traits.
static constexpr std::string_view broker_name() noexcept
Returns the broker's name.
WorkingOrders< BrokerTag > WorkingSet
A working-order listing of this broker.
BlockingSession(std::shared_ptr< Client > client, Account account)
Binds a blocking client and an account.
PlacedOrder< BrokerTag > Placed
What an order-writing call returns: acknowledgement and reference.
typename Traits::BrokerTag BrokerTag
The broker's tag type.
Result< std::vector< QuoteSnapshot > > prices(std::vector< InstrumentId > instruments)
Returns quotes for several instruments in one request; requires Caps::has_batch_prices.
Result< Instrument > instrument(InstrumentId subject)
Returns an instrument's reference data; requires Caps::has_reference_data.
Result< std::vector< AccountInfo< Client > > > accounts()
Lists the broker's accounts; requires Caps::has_account_discovery.
auto async_quote(InstrumentId instrument, CompletionToken &&token)
Requests the current quote for an instrument.
const network::RateLimitStats * rate_limit_stats() const
Returns the client's request-pacing counters.
auto async_order_history(OrderHistoryQuery query, CompletionToken &&token)
Requests order history: placed orders and their executions.
PlacedOrder< BrokerTag > Placed
What an order-writing call returns: acknowledgement and reference.
BrokerSession(std::shared_ptr< Client > client, Account account)
Binds a client and an account.
OrderRef< BrokerTag > Ref
The order reference type this session issues and accepts.
auto async_place(OrderRequest order, CompletionToken &&token)
Places a new order.
auto async_bars(BarQuery query, CompletionToken &&token)
Requests bars for an instrument, paging until count_ bars are read.
auto async_sellable_quantity(InstrumentId instrument, CompletionToken &&token)
Requests how much of an instrument the account may sell.
typename Traits::BrokerTag BrokerTag
The broker's tag type.
auto async_buying_power(Currency currency, CompletionToken &&token)
Requests what the account may spend in a currency.
BrokerCaps< Client > Caps
The client's capabilities.
auto async_price_limits(InstrumentId instrument, CompletionToken &&token)
Requests an instrument's price limits for today.
static constexpr std::string_view broker_name() noexcept
Returns the broker's name.
Client & client() noexcept
Returns the broker client, for broker-specific operations the session does not offer.
auto async_replace(Ref ref, AmendInstruction instruction, CompletionToken &&token)
Modifies a working order.
void close()
Cancels the client's transport work and queued operations.
typename Traits::Config Config
The broker's configuration type.
auto async_commissions(CompletionToken &&token)
Requests the account's commission rates.
typename Traits::Account Account
The broker's account selector type.
auto async_instrument(InstrumentId instrument, CompletionToken &&token)
Requests an instrument's reference data.
const std::shared_ptr< Client > & shared_client() const noexcept
Returns the shared client pointer, for binding a second session.
auto async_holdings(CompletionToken &&token)
Requests the account's holdings.
auto async_working_orders(CompletionToken &&token)
Requests the orders still open to modification or cancellation.
auto async_trades(InstrumentId instrument, int count, CompletionToken &&token)
Requests recent trades for an instrument, newest first.
WorkingOrder< BrokerTag > Working
One working order of this broker.
const Account & account() const noexcept
Returns the bound account.
const Client & client() const noexcept
Returns the broker client.
auto async_order(Ref ref, CompletionToken &&token)
Requests one order by its reference.
WorkingOrders< BrokerTag > WorkingSet
A working-order listing of this broker.
BrokerTraits< Client > Traits
The client's traits.
auto async_prices(std::vector< InstrumentId > instruments, CompletionToken &&token)
Requests quotes for several instruments in one request.
auto async_cancel(Ref ref, AmendInstruction instruction, CompletionToken &&token)
Cancels a working order, in full or in part.
auto async_calendar(Country country, std::optional< Date > from, CompletionToken &&token)
Requests the trading calendar.
auto async_depth(InstrumentId instrument, CompletionToken &&token)
Requests an instrument's order book, best levels first.
A three-letter currency code, such as KRW or USD (ISO 4217 style).
Definition types.hpp:234
Either a value or a BrokerError.
Definition types.hpp:797
Result(T value)
Constructs a successful result.
Definition types.hpp:803
The library: broker-neutral types (core), and the modules below.
auto async_accounts(Client &client, CompletionToken &&token)
Lists a broker's accounts, before a session exists; requires BrokerCaps<Client>::has_account_discover...
Result< std::vector< AccountInfo< Client > > > list_accounts(Client &client)
Lists a broker's accounts with a blocking client, before a session exists; requires BrokerCaps<Client...
@ INQUIRY
From a per-instrument inquiry, made only where the snapshot has no figure.
Definition types.hpp:1096
@ INVALID_ARGUMENT
The request was invalid, refused by the client before sending or by the broker.
Definition types.hpp:713
Country
A country (market) the library supports.
Definition types.hpp:30
std::optional< Decimal64 > amended_trigger_price(const AmendInstruction &instruction)
Returns the trigger price an amendment leaves the order with.
Definition types.hpp:2271
A modification or cancellation of a working order.
Definition types.hpp:2240
What bars to fetch.
What one broker client implements, as compile-time constants.
static constexpr bool has_commission_schedule
Whether the broker publishes the account's commission rates.
static constexpr bool has_batch_prices
Whether the broker quotes several instruments in one request.
static constexpr bool has_price_limits
Whether the broker publishes the day's price limits.
static constexpr bool has_account_discovery
Whether the broker lists the selectable accounts.
static constexpr SellableSource sellable_source
Where the library gets the sellable quantity: HOLDINGS costs nothing, INQUIRY one request per instrum...
static constexpr bool has_reference_data
Whether the broker publishes instrument reference data.
static constexpr bool has_calendar
Whether the broker publishes the trading calendar.
static constexpr bool has_trade_prints
Whether the broker publishes recent trades.
static constexpr bool has_buying_power
Whether the broker reports buying power.
static constexpr bool has_order_by_id
Whether the broker can look up one order by its id.
static constexpr bool has_trigger_price
Whether the adapter sends trigger prices (stop-limit orders).
static constexpr bool has_depth
Whether the broker publishes the order book (depth).
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...
Describes one broker client to the library: its types, capabilities, call thunks and normalizers.
Identifies an instrument: its country, venue and symbol.
Definition types.hpp:78
The filter for an order-history inquiry.
Definition types.hpp:2325
A broker's reference to one order, used to amend or cancel it.
Definition types.hpp:2111
std::string primary_
The order number or id.
Definition types.hpp:2113
An order to place, in broker-neutral form.
Definition types.hpp:1925
An accepted order: the acknowledgement and the reference to address it with.
Definition types.hpp:2165
An order still open to modification or cancellation.
Definition types.hpp:2290
The orders open to modification or cancellation at one point in time.
Definition types.hpp:2312
A rate limiter's counters per traffic class, its current budget, and the broker's refusals (from Rate...