9#include <algotradelib/core>
132 throw std::invalid_argument(
"a sell tax rate must be at least 0 and below 1");
135 model.sell_tax_ = std::move(tax);
143 const std::optional<SellTax>&
sell_tax() const noexcept {
return sell_tax_; }
156 case OrderType::UNKNOWN:
179 switch (order.time_in_force_) {
185 case TimeInForce::UNKNOWN:
225 const std::optional<Date>& open_date = std::nullopt,
226 bool released =
false
229 bool at_open =
false;
230 std::optional<Decimal64> reference;
236 switch (release_of(order, next)) {
241 case Release::AT_OPEN:
244 reference = reference_price(order, next, at_open);
246 if (reference && limit_marketable_at(order, *next.
bar_.
open_)) {
250 case Release::INTRABAR:
252 reference = intrabar_release_price(order, next);
263 reference = reference_price(order, next, at_open);
273 std::optional<Decimal64> bound;
275 if (limit_basis && order.
price_) {
276 const auto& limit = *order.
price_;
277 if (order.
side_ == OrderSide::SELL) {
291 auto slipped = slippage_.apply(*reference, order.
side_, rule);
294 const bool favourable = order.
side_ == OrderSide::SELL ?
298 slipped->first = *reference;
302 if (slipped && bound) {
305 const bool past = order.
side_ == OrderSide::SELL ?
309 const auto moved = order.
side_ == OrderSide::SELL ?
315 slipped->first = ceiling;
316 slipped->second = *moved;
321 if (slipped && bound) {
322 const bool beyond = order.
side_ == OrderSide::SELL ?
326 const auto moved = order.
side_ == OrderSide::SELL ?
332 slipped->first = *bound;
333 slipped->second = *moved;
342 const auto& charged_on = at_open && open_date ? *open_date : date;
352 std::optional<Decimal64> tax;
355 const auto charged = value ?
decimal_multiply(*value, sell_tax_->rate_) : std::nullopt;
363 error.
message_ =
"the tax on that sale does not fit an exact decimal";
378 execution.
applied_ = std::move(charged.value().applied_);
381 execution.
tax_ = *tax;
408 const bool sell = order.
side_ == OrderSide::SELL;
409 const bool reached = sell ?
413 return Release::NONE;
416 const auto& open = *next.bar_.open_;
417 const bool through = sell ? !decimal_less(trigger, open) : !decimal_less(open, trigger);
419 return Release::AT_OPEN;
422 return Release::INTRABAR;
426 static bool limit_marketable_at(
const strategy::PlannedOrder& order,
const Decimal64& price) {
427 const auto& limit = *order.
price_;
445 static std::optional<Decimal64> intrabar_release_price(
446 const strategy::PlannedOrder& order,
447 const strategy::BarEvent& next
450 const auto& limit = *order.
price_;
451 const bool sell = order.
side_ == OrderSide::SELL;
452 if (limit_marketable_at(order, trigger)) {
458 if (limit_marketable_at(order, next.
bar_.
close_)) {
473 std::optional<Decimal64> reference_price(
474 const strategy::PlannedOrder& order,
475 const strategy::BarEvent& next,
480 const bool has_open = next.
bar_.
open_.has_value();
484 const bool limit_basis = limit_priced(order.
type_);
485 if (!limit_basis || !order.
price_) {
489 const auto& limit = *order.
price_;
490 bool open_if_better =
false;
491 switch (limit_price_) {
493 open_if_better =
true;
499 if (order.
side_ == OrderSide::BUY) {
523 std::optional<SellTax> sell_tax_;
An exact signed decimal: an int64 coefficient times 10^-scale.
static Decimal64 parse(std::string_view input)
Parses a decimal string exactly (no binary floating point).
How a backtest charges commission: one stated model, or a schedule resolved per execution.
const CommissionPolicy & commission() const noexcept
Returns the commission policy.
NextBarFillModel(CommissionPolicy commission, SlippageModel slippage=SlippageModel::none(), LimitFillPrice limit_price=LimitFillPrice::AT_LIMIT)
Constructs a model with a commission policy, which may resolve a schedule per execution.
NextBarFillModel with_sell_tax(SellTax tax) const
Returns a copy of this model that charges a tax on sales.
FillDecision decide(const strategy::PlannedOrder &order, const strategy::BarEvent &next, const PriceRule &rule, const Date &date, const std::optional< Date > &open_date=std::nullopt, bool released=false) const
Decides whether an order trades against a bar, and at what price and cost.
const std::optional< SellTax > & sell_tax() const noexcept
Returns the tax on sales.
NextBarFillModel(CommissionModel commission=CommissionModel::none(), SlippageModel slippage=SlippageModel::none(), LimitFillPrice limit_price=LimitFillPrice::AT_LIMIT)
Constructs a model with a flat commission.
static bool rests_unfilled(const strategy::PlannedOrder &order) noexcept
Tests whether an order that did not fill stays queued for a later bar of its session.
LimitFillPrice limit_price() const noexcept
Returns which price a filled limit order is credited.
static constexpr bool limit_priced(OrderType type) noexcept
Tests whether the model prices an order type against its limit (a limit order, and a stop-limit once ...
const SlippageModel & slippage() const noexcept
Returns the slippage model.
Trading costs for a backtest: commission (CommissionModel, CommissionPolicy), slippage (SlippageModel...
The events a runner delivers to a strategy's handlers, and the market phase.
The replay ledger: positions, cash and realized profit a backtest keeps, in exact decimals.
Replays a strategy over stored bars: fill and cost models, the replay ledger, and the report.
LimitFillPrice
Which price a backtest credits a limit order that fills.
@ AT_OPEN_IF_BETTER
Fill at the bar's open when it opened through the limit (assumes the order was resting before the ope...
@ AT_LIMIT
The default: fill at the limit, or at the bar's worst traded price when the whole bar was better than...
The library: broker-neutral types (core), and the modules below.
std::optional< Decimal64 > decimal_floor_to_multiple(const Decimal64 &value, const Decimal64 &step)
Rounds down to a multiple of a step (toward negative infinity).
@ INVALID_ARGUMENT
The request was invalid, refused by the client before sending or by the broker.
OrderType
How an order's price is decided (its price basis).
@ LIMIT
지정가: fills at the stated price or better.
@ BEST_LIMIT
최유리지정가: priced at the best quote on the opposite side.
@ PRIMARY_LIMIT
최우선지정가: priced at the best quote on the order's own side.
@ CONDITIONAL_LIMIT
조건부지정가: a limit that becomes a market order at the close.
@ MID_PRICE
중간가: priced at the midpoint of the spread.
@ STOP_LIMIT
스톱지정가: a limit released when a trigger price is reached.
@ CLOSING_PRICE
시간외종가: priced at a session's closing price.
@ MARKET
시장가: fills at whatever the book offers.
constexpr bool carries_trigger_price(OrderType type) noexcept
Tests whether an order type takes a trigger price.
Country
A country (market) the library supports.
std::optional< Decimal64 > decimal_subtract(const Decimal64 &lhs, const Decimal64 &rhs)
Subtracts two decimals exactly.
bool decimal_less(const Decimal64 &lhs, const Decimal64 &rhs)
Compares two decimals numerically, whatever their scales.
std::optional< Decimal64 > decimal_multiply(const Decimal64 &lhs, const Decimal64 &rhs)
Multiplies two decimals exactly; the scales add.
@ FILL_OR_KILL
Fills entirely and immediately, or not at all.
@ DAY
Rests until the session ends; an unfilled remainder is cancelled then.
@ IMMEDIATE_OR_CANCEL
Fills what it can immediately and cancels the remainder.
Reconciliation: turning a strategy's intents and targets into the orders and cancellations to send.
Decimal64 high_
The highest trade price.
Decimal64 close_
The last trade price.
std::optional< Decimal64 > open_
The first trade's price; empty if nothing traded in the period (never zero or the previous close).
Decimal64 low_
The lowest trade price.
The error a broker or transport operation returns.
std::string message_
A human-readable description, for logs; do not parse it.
ErrorCategory category_
The kind of failure; the field to branch on.
std::string broker_code_
The broker's own error code, verbatim (for example KIS's EGW00201).
bool reached_broker_
Whether a request was sent before this error.
A calendar date with no time of day or time zone.
Country country_
The listing country, which selects the symbol convention and the price grid.
The price grid (tick sizes) an instrument's order prices must sit on.
A flat commission: a rate on traded value, with an optional per-execution minimum.
static CommissionModel none()
Returns a model that charges nothing (the report says so).
One execution a backtest produced, and what it cost.
std::optional< Decimal64 > institution_fee_
The institution fee (유관기관제비용), if the rate was split; empty is not zero.
Decimal64 commission_
The broker's commission; includes the institution fee when institution_fee_ is empty.
AppliedRate applied_
The commission rate that charged this execution.
Decimal64 slippage_
The price given up to slippage (already included in the fill price).
strategy::Fill fill_
The fill, as on_fill receives it.
Decimal64 tax_
The sale tax charged; zero unless a sell tax applies (NextBarFillModel::with_sell_tax).
What NextBarFillModel::decide concluded about one order and one bar.
std::optional< BrokerError > costing_error_
Why a fill could not be costed (no commission rate, or a tax that does not fit).
bool still_working_
Whether the order stays queued for a later bar (it did not trade but may still).
bool bounded_
Whether a limit-type fill price was moved to stay within the bar's range and the limit; counted by th...
bool released_
Whether this bar released a dormant stop-limit order (whether or not it also filled).
std::optional< Execution > execution_
The execution, if the order traded against this bar.
bool intrabar_release_fill_
Whether the fill depended on a stop released inside the bar, where the bar cannot show the order of p...
A tax on sales, stated by the caller; see NextBarFillModel::with_sell_tax.
Country country_
The market whose sales pay it.
Decimal64 rate_
The fraction of a sale's value charged, such as 0.0020 for 0.20%.
Slippage: how far a fill price moves against the order (a buy pays more, a sell gets less).
static SlippageModel none()
Returns a model with no slippage (the report says so).
One bar, delivered to on_bar.
std::chrono::system_clock::time_point period_end_
When the bar's period ended: the earliest moment the bar could be known.
std::chrono::system_clock::time_point at_
When the execution happened.
InstrumentId instrument_
The instrument traded.
std::string tag_
The tag of the intent that produced the order.
OrderSide side_
The side of the order.
Decimal64 price_
The execution price.
Decimal64 quantity_
The quantity of this execution, not the order's running total.
One order the runner is to place.
std::string tag_
The tag of the intent or target the order came from.
std::optional< Decimal64 > trigger_price_
The trigger price of a STOP_LIMIT order, exactly as stated; empty otherwise.
OrderType type_
The order type.
InstrumentId instrument_
The instrument to trade.
std::optional< Decimal64 > price_
The limit price, after clamping and rounding; empty for a market order.
Decimal64 quantity_
The quantity, after rounding onto the lot.