12#include <algotradelib/runner>
13#include <algotradelib/vanilla>
44InstrumentId samsung() {
return InstrumentId{Country::KR, Venue::COMBINED,
"005930"}; }
47Instrument samsung_reference() {
49 reference.id_ = samsung();
50 reference.name_ =
"Samsung Electronics";
51 reference.type_ = SecurityType::STOCK;
52 reference.currency_ = Currency{
"KRW"};
53 reference.lot_size_ = Decimal64::parse(
"1");
54 reference.price_rule_ = PriceRule::krx_equity();
64const std::vector<const char*>& closes() {
65 static const std::vector<const char*> series = {
66 "60000",
"60500",
"61200",
"61000",
"62000",
"63000",
"64500",
"65000",
"66200",
"67000",
67 "68500",
"69000",
"70200",
"71000",
"72500",
"73000",
"72800",
"72000",
"71000",
"69500",
68 "68000",
"66500",
"65000",
"63500",
"62000",
"61000",
"60000",
"59000",
"58500",
"58000",
76 value.timestamp_ = std::chrono::system_clock::time_point{} +
77 std::chrono::hours(24 * (20000 +
static_cast<int>(index)));
78 value.open_ = Decimal64::parse(closes()[index]);
79 value.high_ = Decimal64::parse(closes()[index]);
80 value.low_ = Decimal64::parse(closes()[index]);
81 value.close_ = Decimal64::parse(closes()[index]);
82 value.volume_ = Decimal64::parse(
"1200000");
87template <
typename Report>
88void print(std::size_t day,
const Report& report) {
89 for (
const auto& order : report.would_place_) {
90 std::cout <<
" day " << std::setw(2) << day <<
" "
91 << (order.side_ == OrderSide::BUY ?
"BUY " :
"SELL") <<
' ' << std::setw(4)
92 << order.quantity_.str() <<
" shares";
94 std::cout <<
" at " << order.price_->str();
96 std::cout <<
" at market";
98 std::cout <<
" [" << order.tag_ <<
"]\n";
100 for (
const auto& rejection : report.rejected_) {
101 std::cout <<
" day " << std::setw(2) << day <<
" rejected: "
113using RefusalTally = std::map<std::string, std::pair<std::size_t, std::size_t>>;
115template <
typename Report>
116void tally(std::size_t day,
const Report& report, RefusalTally& counts) {
117 for (
const auto& refusal : report.refused_) {
119 auto found = counts.find(reason);
120 if (found == counts.end()) {
121 counts.emplace(reason, std::pair<std::size_t, std::size_t>{day, 1});
123 ++found->second.second;
135template <
typename Strategy>
137 std::cout << title <<
'\n';
140 dry.context().set_instrument(samsung_reference());
142 dry.set_portfolio_value(Decimal64::parse(
"100000000"));
144 Decimal64 held = Decimal64::parse(
"0");
145 RefusalTally refusals;
146 for (std::size_t day = 0; day < closes().size(); ++day) {
147 dry.context().set_last_price(samsung(), Decimal64::parse(closes()[day]));
150 HoldingPosition position;
151 position.instrument_ = samsung();
152 position.quantity_ = held;
155 position.sellable_quantity_ = held;
162 dry.context().set_portfolio(portfolio);
164 const auto report = dry.feed(
bar(day));
166 tally(day, report, refusals);
168 for (
const auto& order : report.would_place_) {
169 const auto signed_quantity = order.side_ == OrderSide::BUY ?
172 if (signed_quantity) {
173 held = *signed_quantity;
177 for (
const auto& entry : refusals) {
178 std::cout <<
" day " << std::setw(2) << entry.second.first <<
" refused: " << entry.first
179 <<
" (and on " << entry.second.second - 1 <<
" later bars)\n";
181 std::cout <<
" ending position: " << held.str() <<
" shares\n\n";
187 std::cout <<
"005930, " << closes().size()
188 <<
" daily bars built in code -- no credentials, no network\n\n";
190 vanilla::BuyAndHold holding;
191 holding.params_.instrument_ = samsung();
192 holding.params_.quantity_ = Decimal64::parse(
"100");
193 run(
"BuyAndHold: one target, then nothing, and it remembers nothing to do it", holding);
195#if defined(ATL_HAS_EIGEN)
196 vanilla::MovingAverageCrossover::Params crossing;
197 crossing.instrument_ = samsung();
200 crossing.quantity_ = Decimal64::parse(
"100");
202 "SmaCrossover(3, 8): long while the fast average leads, flat while it lags",
203 vanilla::SmaCrossover{crossing}
206 vanilla::DonchianBreakout breaking;
207 breaking.params_.instrument_ = samsung();
208 breaking.params_.lookback_ = 5;
209 breaking.params_.quantity_ = Decimal64::parse(
"100");
210 run(
"DonchianBreakout(5): the channel excludes today, so a break is measurable", breaking);
212 vanilla::RsiThreshold indexed;
213 indexed.params_.instrument_ = samsung();
214 indexed.params_.period_ = 5;
215 indexed.params_.oversold_ = 35.0;
216 indexed.params_.overbought_ = 65.0;
217 indexed.params_.quantity_ = Decimal64::parse(
"100");
218 run(
"RsiThreshold(5, 35/65): Wilder's smoothing and Wilder's seed", indexed);
221 <<
"The indicator strategies need a linear algebra backend and this build has none.\n"
222 <<
"Configure with -DATL_USE_EIGEN=ON to see SmaCrossover, DonchianBreakout and RsiThreshold.\n"
223 <<
"BuyAndHold and TimedLiquidation read no series and run either way.\n\n";
229 vanilla::BuyAndHold oversized;
230 oversized.params_.instrument_ = samsung();
231 oversized.params_.quantity_ = Decimal64::parse(
"100");
233 "BuyAndHold with a 1,000,000 KRW per-order ceiling: refused, never resized",
A three-letter currency code, such as KRW or USD (ISO 4217 style).
An exact signed decimal: an int64 coefficient times 10^-scale.
BrokerError refusal()
Returns the error every account-side member of DataOnlyTraits answers with.
Runs a strategy: the runner, the polling loop, the order journal, and stopping.
Runner< Strategy, CapsType, DryTag, RecordingExecutor< DryTag > > DryRunner
A runner that sends nothing: handlers fire and instructions are recorded by a RecordingExecutor.
void bar(S &strategy, Ctx &context, const BarEvent &event)
Calls strategy.on_bar(context, event) if the strategy declares on_bar; otherwise does nothing.
The strategies this library ships (buy and hold, crossovers, mean reversion, sizing,...
What a strategy sees and emits: events, intents, the portfolio, reconciliation and risk limits.
BarEvent make_bar_event(InstrumentId instrument, BarInterval interval, BarStamp stamp, Bar bar)
Makes a bar event, computing its period_end_ with bar_period_end.
@ OPEN
Inside the session.
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 guard_reason_name(GuardReason reason) noexcept
Returns a lowercase name for a refusal reason, such as "order_rate_exceeded", for logs.
The library: broker-neutral types (core), and the modules below.
BarStamp
Which edge of its period a bar's timestamp marks.
SecurityType
What kind of security an instrument is.
std::optional< Decimal64 > decimal_add(const Decimal64 &lhs, const Decimal64 &rhs)
Adds two decimals exactly.
Country
A country (market) the library supports.
std::optional< Decimal64 > decimal_subtract(const Decimal64 &lhs, const Decimal64 &rhs)
Subtracts two decimals exactly.
Venue
A trading venue: an exchange, or the combined view across exchanges.
BarInterval
The period one bar covers.
OrderSide
The side of an order: its economic direction, buy or sell.
One bar: the prices and volume of one period, as exact decimals.
One position row in a holdings snapshot.
Identifies an instrument: its country, venue and symbol.
Reference data for one instrument.
The price grid (tick sizes) an instrument's order prices must sit on.
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.
One bar, delivered to on_bar.
What the account holds and which orders are still working, as a strategy sees it.
SellableBasis sellable_basis_
What the sellable_quantity_ figures on positions_ cover.
std::vector< HoldingPosition > positions_
Every position row the broker reported.
The limits the operator sets for RiskGuard.
std::optional< Decimal64 > max_order_notional_
The largest value one order may have, in its own currency.