algotradelib 0.0.0.9000
C++ header-only library for algorithmic trading
Loading...
Searching...
No Matches
report.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "./engine.hpp"
4
5#include <algotradelib/analytics>
6#include <algotradelib/core>
7
8#include <cstddef>
9#include <chrono>
10#include <optional>
11#include <string>
12#include <utility>
13#include <vector>
14
15namespace algotradelib {
16namespace backtest {
17
25
36 std::vector<AppliedRate> applied_rates_;
50 std::optional<VenueKey> asserted_venue_;
52 std::string commission_tariff_;
64
74
76 std::size_t gaps_{0};
77
79 std::size_t unfilled_{0};
80
90 std::size_t quotes_{0};
91
107
112 static constexpr bool volume_ignored = true;
113
121 static constexpr bool bar_tape_unverified = true;
122
129 static constexpr bool sellable_is_whole_holding = true;
130
138 static constexpr bool buying_power_is_ledger_cash = true;
139
147 std::size_t unvalued_bars_{0};
149 std::optional<std::chrono::system_clock::time_point> first_unvalued_;
155 std::size_t stop_releases_{0};
163 std::size_t stop_fills_intrabar_{0};
168 std::size_t limit_fills_bounded_{0};
176 std::size_t overdrawn_bars_{0};
178 std::optional<std::chrono::system_clock::time_point> first_overdrawn_;
180 std::optional<Money> lowest_cash_;
191 std::optional<std::chrono::system_clock::time_point> lowest_cash_at_;
193 std::optional<SellTax> sell_tax_;
195 std::size_t expired_day_orders_{0};
196};
197
203struct Report {
205 std::size_t bars_{0};
207 std::size_t executions_{0};
208
219
248
256 std::optional<Decimal64> commission_paid_;
258 std::vector<Money> commission_by_currency_;
265 std::optional<Decimal64> institution_fees_paid_;
272 std::vector<Money> institution_by_currency_;
279 std::optional<Decimal64> slippage_paid_;
281 std::vector<Money> slippage_by_currency_;
282
284 std::size_t rejected_{0};
286 std::size_t refused_{0};
287
296 std::optional<BrokerError> halted_;
298 std::size_t aborted_{0};
299
309 std::optional<Decimal64> tax_paid_;
311 std::vector<Money> tax_by_currency_;
312};
313
327 const ReplayResult& result,
328 const NextBarFillModel& fills,
329 std::optional<math::Scalar> periods_per_year = std::nullopt,
330 math::Scalar risk_free = math::Scalar{0}
331) {
332 Report report;
333 report.bars_ = result.bars_;
334 report.executions_ = result.executions_.size();
335 report.rejected_ = result.rejected_.size();
336 report.refused_ = result.refused_.size();
337
338 report.caveats_.limit_fill_ = fills.limit_price();
339 report.halted_ = result.halted_;
340 report.aborted_ = result.aborted_;
341 if (fills.commission().is_scheduled()) {
342 report.caveats_.asserted_venue_ = fills.commission().asserted_venue();
343 }
344 report.caveats_.commission_tariff_ = std::string(fills.commission().tariff());
345 report.caveats_.slippage_basis_points_ = fills.slippage().basis_points_;
346 report.caveats_.slippage_ticks_ = fills.slippage().ticks_;
347 report.caveats_.gaps_ = result.gaps_;
348 report.caveats_.unfilled_ = result.unfilled_;
349 report.caveats_.stop_releases_ = result.stop_releases_;
350 report.caveats_.stop_fills_intrabar_ = result.stop_fills_intrabar_;
351 report.caveats_.limit_fills_bounded_ = result.limit_fills_bounded_;
352 report.caveats_.quotes_ = result.quotes_;
353 report.caveats_.unchecked_fill_causality_ = result.unchecked_fill_causality_;
354 report.caveats_.unvalued_bars_ = result.unvalued_bars_;
355 report.caveats_.first_unvalued_ = result.first_unvalued_;
356 report.caveats_.overdrawn_bars_ = result.overdrawn_bars_;
357 report.caveats_.first_overdrawn_ = result.first_overdrawn_;
358 report.caveats_.lowest_cash_ = result.lowest_cash_;
359 report.caveats_.lowest_cash_at_ = result.lowest_cash_at_;
360 report.caveats_.expired_day_orders_ = result.expired_day_orders_;
361
362 // Costs summed exactly, because they are money -- and **per currency**, because a replay whose
363 // instruments span markets would otherwise have won added to dollars to make one scalar.
364 // Checked, and a refusal is recorded rather than swallowed: leaving the previous total in place
365 // and carrying on produced a plausible figure that was short by an unknown amount, which is the
366 // one outcome every exact-decimal path here exists to prevent. Two individually representable
367 // commissions whose sum does not fit is enough to reach it, and a probe did.
368 bool costs_fit = true;
369 const auto accumulate = [&costs_fit](std::vector<Money>& totals, const Money& amount) {
370 for (auto& held : totals) {
371 if (held.currency_ == amount.currency_) {
372 const auto sum = decimal_add(held.amount_, amount.amount_);
373 if (!sum) {
374 costs_fit = false;
375 return;
376 }
377 held.amount_ = *sum;
378 return;
379 }
380 }
381 totals.push_back(amount);
382 };
383
384 std::vector<InstrumentId> seen;
385 for (const auto& execution : result.executions_) {
386 accumulate(report.commission_by_currency_, Money{execution.commission_, execution.currency_});
387 if (execution.institution_fee_) {
388 accumulate(
389 report.institution_by_currency_, Money{*execution.institution_fee_, execution.currency_}
390 );
391 } else {
392 // The rate stated one figure, so this execution's commission is inclusive and the split total
393 // below cannot be completed. Counted rather than approximated.
394 ++report.caveats_.inclusive_commission_executions_;
395 }
396 // The rate that charged it, collapsed so an unchanged rate is one row with a count rather than
397 // one row per fill. Accumulated here, from the executions the ledger accepted, rather than from
398 // the schedule: a row the replay never used is not a rate this run applied.
399 bool counted = false;
400 for (auto& applied : report.caveats_.applied_rates_) {
401 if (applied == execution.applied_) {
402 ++applied.executions_;
403 counted = true;
404 break;
405 }
406 }
407 if (!counted) {
408 auto entry = execution.applied_;
409 entry.executions_ = 1;
410 report.caveats_.applied_rates_.push_back(std::move(entry));
411 }
412 // The tax, totalled per currency, over the executions in the market it was stated for: another
413 // market's fills pay none by construction, and counting them would add a zero row in their
414 // currency and cost a single-currency total its scalar.
415 if (fills.sell_tax() && execution.fill_.instrument_.country_ == fills.sell_tax()->country_) {
416 accumulate(report.tax_by_currency_, Money{execution.tax_, execution.currency_});
417 }
418 // Slippage is a per-share price difference, so its cost is that times the quantity -- and it
419 // is totalled per currency like every other cost, because a replay can hold fills in more
420 // than one and adding them would produce a figure denominated in nothing.
421 if (const auto cost = decimal_multiply(execution.slippage_, execution.fill_.quantity_)) {
422 accumulate(report.slippage_by_currency_, Money{*cost, execution.currency_});
423 } else {
424 costs_fit = false;
425 }
426 bool known = false;
427 for (const auto& id : seen) {
428 if (id == execution.fill_.instrument_) {
429 known = true;
430 break;
431 }
432 }
433 if (!known) {
434 seen.push_back(execution.fill_.instrument_);
435 }
436 }
437 report.caveats_.cost_totals_incomplete_ = !costs_fit;
438 // A scalar is stated only where one can be true: one currency, something executed, and every
439 // addition representable. On a mixed-currency replay the per-currency vectors are the whole
440 // answer, and on an arithmetic refusal nothing is stated at all rather than a figure short by an
441 // amount nobody can name.
442 if (costs_fit && report.commission_by_currency_.size() == 1) {
443 report.commission_paid_ = report.commission_by_currency_.front().amount_;
444 }
445 // Present only when every charged rate split the two components, so the total is complete rather
446 // than a sum over the subset that happened to state one.
447 if (costs_fit && report.caveats_.inclusive_commission_executions_ == 0 &&
448 report.institution_by_currency_.size() == 1) {
449 report.institution_fees_paid_ = report.institution_by_currency_.front().amount_;
450 }
451 if (costs_fit && report.slippage_by_currency_.size() == 1) {
452 report.slippage_paid_ = report.slippage_by_currency_.front().amount_;
453 }
454 report.caveats_.sell_tax_ = fills.sell_tax();
455 if (fills.sell_tax() && costs_fit && report.tax_by_currency_.size() == 1) {
456 report.tax_paid_ = report.tax_by_currency_.front().amount_;
457 }
458 report.caveats_.multiple_instruments_ = seen.size() > 1;
459 for (const auto& execution : result.executions_) {
460 if (!(execution.currency_ == result.currency_)) {
461 ++report.caveats_.other_currency_executions_;
462 }
463 }
464
465 if (result.equity_.empty()) {
466 // Nothing to measure. Every figure stays unobserved rather than becoming zero, which would read as
467 // a flat result rather than as an absent one.
468 report.total_return_ = math::missing();
469 report.sharpe_ = math::missing();
470 report.annualized_sharpe_ = math::missing();
471 report.sortino_ = math::missing();
472 report.max_drawdown_ = math::missing();
473 report.total_turnover_ = math::missing();
474 return report;
475 }
476
477 report.opening_equity_ = result.equity_.front().equity_;
478 report.closing_equity_ = result.equity_.back().equity_;
479
480 // The returns and the equity curve, as the columns the metrics take.
481 math::Vector returns(static_cast<math::Index>(result.equity_.size()));
482 math::Vector levels(static_cast<math::Index>(result.equity_.size()));
483 for (std::size_t at = 0; at < result.equity_.size(); ++at) {
484 returns[static_cast<math::Index>(at)] = result.equity_[at].period_return_;
485 levels[static_cast<math::Index>(at)] = analytics::to_scalar(result.equity_[at].equity_);
486 }
487
488 // From a negative opening the ratio's sign reverses (a replay may start with negative cash), so a
489 // non-positive opening has no return and no turnover, as a zero one has none.
490 const auto opening = analytics::to_scalar(report.opening_equity_);
491 report.total_return_ = !(opening > math::Scalar{0}) ?
493 analytics::to_scalar(report.closing_equity_) / opening - math::Scalar{1};
494
495 report.sharpe_ = analytics::sharpe(returns, risk_free);
496 report.sortino_ = analytics::sortino(returns, risk_free);
497 // Equity at or below zero before the end leaves the periods after it without a return
498 // (`EquityPoint::period_return_`), so no ratio describes the run.
499 for (std::size_t at = 0; at + 1 < result.equity_.size(); ++at) {
500 if (result.equity_[at].equity_.coefficient() <= 0) {
501 report.sharpe_ = math::undefined();
502 report.sortino_ = math::undefined();
503 break;
504 }
505 }
506 report.max_drawdown_ = analytics::max_drawdown(levels).magnitude_;
507
508 report.annualized_sharpe_ = periods_per_year ?
510 math::missing();
511
512 // Every trade outside the curve: it holds only the opening cash, and its figures describe no trading.
513 const auto outside = report.caveats_.other_currency_executions_;
514 if (outside > 0 && outside == result.executions_.size()) {
515 report.total_return_ = math::missing();
516 report.sharpe_ = math::missing();
517 report.annualized_sharpe_ = math::missing();
518 report.sortino_ = math::missing();
519 report.max_drawdown_ = math::missing();
520 }
521
522 // Turnover from the executions rather than from a weight matrix, because a replay knows what it
523 // actually traded. One-way, matching `analytics::one_way_turnover`: the fraction of equity that
524 // changed hands.
525 // Same money on both sides, or no figure at all.
526 report.total_turnover_ = !(opening > math::Scalar{0}) ? math::undefined() : math::Scalar{0};
527 if (outside > 0) {
528 report.total_turnover_ = math::missing();
529 }
530 if (math::observed(report.total_turnover_)) {
531 for (const auto& execution : result.executions_) {
532 const auto traded = decimal_multiply(execution.fill_.quantity_, execution.fill_.price_);
533 if (traded) {
534 report.total_turnover_ += analytics::to_scalar(*traded) / opening;
535 }
536 }
537 }
538
539 return report;
540}
541
542} // namespace backtest
543} // namespace algotradelib
An exact signed decimal: an int64 coefficient times 10^-scale.
Definition types.hpp:297
const VenueKey & asserted_venue() const noexcept
Returns the asserted venue.
Definition cost.hpp:247
std::string_view tariff() const noexcept
Returns the schedule's tariff name.
Definition cost.hpp:253
bool is_scheduled() const noexcept
Tests whether the policy resolves a schedule.
Definition cost.hpp:241
The fill model: market orders at the next bar's open, limit orders when the next bar's range reaches ...
Definition fill.hpp:68
const CommissionPolicy & commission() const noexcept
Returns the commission policy.
Definition fill.hpp:104
const std::optional< SellTax > & sell_tax() const noexcept
Returns the tax on sales.
Definition fill.hpp:143
LimitFillPrice limit_price() const noexcept
Returns which price a filled limit order is credited.
Definition fill.hpp:116
const SlippageModel & slippage() const noexcept
Returns the slippage model.
Definition fill.hpp:110
The backtest engine: Backtester replays bars (and quotes) through the same Runner a live session uses...
Scalar sortino(math::SeriesView period_returns, Scalar target=Scalar{0})
Computes the per-period Sortino ratio, (mean(returns) - target) / downside_deviation.
Definition metrics.hpp:153
Scalar annualize_ratio(Scalar ratio, Scalar periods_per_year)
Annualizes a per-period ratio by multiplying it by sqrt(periods_per_year).
Definition metrics.hpp:200
MaxDrawdown max_drawdown(math::SeriesView levels)
Finds the worst peak-to-trough decline of a level series.
Definition metrics.hpp:83
Scalar to_scalar(const Decimal64 &value)
Converts an exact decimal to the nearest Scalar.
math::Vector returns(math::SeriesView prices)
Computes simple period returns, p[i] / p[i - 1] - 1.
Definition returns.hpp:27
Scalar sharpe(math::SeriesView period_returns, Scalar risk_free=Scalar{0})
Computes the per-period Sharpe ratio, (mean(returns) - risk_free) / stdev(returns).
Definition metrics.hpp:127
Replays a strategy over stored bars: fill and cost models, the replay ledger, and the report.
Definition clock.hpp:14
Report summarize(const ReplayResult &result, const NextBarFillModel &fills, std::optional< math::Scalar > periods_per_year=std::nullopt, math::Scalar risk_free=math::Scalar{0})
Summarizes a replay into a Report.
Definition report.hpp:326
LimitFillPrice
Which price a backtest credits a limit order that fills.
Definition cost.hpp:562
@ AT_LIMIT
The default: fill at the limit, or at the bar's worst traded price when the whole bar was better than...
Definition cost.hpp:571
std::optional< math::Scalar > periods_per_year(BarInterval interval, const strategy::SessionWindow &session, math::Scalar trading_days)
Returns how many bars of an interval a year contains, for analytics::annualize_ratio.
Definition clock.hpp:47
double Scalar
The floating-point type the analytics and math layers compute in (double).
Definition scalar.hpp:18
bool observed(Scalar value)
Tests whether a value is an observation.
Definition scalar.hpp:36
Eigen::Index Index
The signed index type of rows and columns.
Definition eigen.hpp:65
Scalar undefined()
Returns the value that stands for "the data was there, but the statistic does not exist".
Definition scalar.hpp:51
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > Vector
A dense column vector of Scalar.
Definition eigen.hpp:48
Scalar missing()
Returns the value that stands for "no observation" (a quiet NaN).
Definition scalar.hpp:28
The library: broker-neutral types (core), and the modules below.
std::optional< Decimal64 > decimal_add(const Decimal64 &lhs, const Decimal64 &rhs)
Adds two decimals exactly.
Definition types.hpp:509
std::optional< Decimal64 > decimal_multiply(const Decimal64 &lhs, const Decimal64 &rhs)
Multiplies two decimals exactly; the scales add.
Definition types.hpp:571
Scalar magnitude_
The decline as a non-negative fraction of the peak (0.2 is 20% below it).
Definition metrics.hpp:64
Everything a replay produced; summarize turns it into a Report.
Definition engine.hpp:308
std::size_t unvalued_bars_
Bars whose equity total did not fit an exact decimal, so they have no curve point.
Definition engine.hpp:358
std::size_t stop_releases_
Stop-limit orders a bar released; see ReportCaveats::stop_releases_.
Definition engine.hpp:362
std::size_t bars_
The bars fed.
Definition engine.hpp:323
std::size_t limit_fills_bounded_
Limit-type fills moved to stay in range; see ReportCaveats::limit_fills_bounded_.
Definition engine.hpp:366
std::size_t unchecked_fill_causality_
How many fill decisions could not be checked for causality; see ReportCaveats::unchecked_fill_causali...
Definition engine.hpp:350
std::optional< std::chrono::system_clock::time_point > first_unvalued_
The period end of the first unvalued bar, if any.
Definition engine.hpp:360
std::vector< strategy::GuardRejection > refused_
Instructions a risk guard refused.
Definition engine.hpp:319
std::vector< strategy::Rejection > rejected_
Instructions not placed: reconciliation could not build them, or the replay cannot model them (BINDIN...
Definition engine.hpp:317
std::vector< Execution > executions_
Every execution, in order.
Definition engine.hpp:312
std::size_t gaps_
Bars that did not follow their predecessor; gaps are not interpolated.
Definition engine.hpp:321
std::size_t expired_day_orders_
DAY orders withdrawn unfilled when their session ended, as a broker does at the close.
Definition engine.hpp:385
std::size_t unfilled_
Orders still queued when finish ran.
Definition engine.hpp:325
std::optional< BrokerError > halted_
Why the replay stopped early, if it did.
Definition engine.hpp:336
std::size_t stop_fills_intrabar_
Fills of stops released inside a bar; see ReportCaveats::stop_fills_intrabar_.
Definition engine.hpp:364
std::size_t quotes_
The quotes fed; see ReportCaveats::quotes_ for what quote-driven orders mean in a replay.
Definition engine.hpp:340
Currency currency_
The currency of the equity curve (the replay's accounting currency).
Definition engine.hpp:327
std::optional< std::chrono::system_clock::time_point > first_overdrawn_
The first bar end at which cash was below zero, if any.
Definition engine.hpp:379
std::size_t aborted_
Orders still working when the replay halted, cleared from the queue (unlike unfilled_).
Definition engine.hpp:338
std::optional< Money > lowest_cash_
The lowest cash any execution left in currency_, if below zero (checked at every fill).
Definition engine.hpp:381
std::optional< std::chrono::system_clock::time_point > lowest_cash_at_
When lowest_cash_ was reached (that execution's Fill::at_).
Definition engine.hpp:383
std::vector< EquityPoint > equity_
One point per bar that could be valued, in order.
Definition engine.hpp:310
std::size_t overdrawn_bars_
Bars that closed with cash below zero in currency_ (counted per bar fed, so two instruments count bot...
Definition engine.hpp:377
What a replay's figures assumed and did not account for.
Definition report.hpp:27
std::size_t quotes_
How many quotes the replay was fed (zero for a bars-only replay).
Definition report.hpp:90
static constexpr bool volume_ignored
Always true: fills are never capped by traded volume, and queue position, market impact and borrow av...
Definition report.hpp:112
std::size_t gaps_
Bars that did not follow their predecessor (see ReplayClock::contiguous); gaps are not interpolated.
Definition report.hpp:76
std::size_t stop_releases_
How many stop-limit orders a bar released, each using the assumed direction (a sell stop releases whe...
Definition report.hpp:155
std::size_t inclusive_commission_executions_
How many executions were charged a single combined rate (no institution fee split).
Definition report.hpp:43
std::optional< std::chrono::system_clock::time_point > first_unvalued_
The period end of the first unvalued bar, if any.
Definition report.hpp:149
std::size_t limit_fills_bounded_
Limit-type fills whose price was moved to stay within the bar's range and the limit (see LimitFillPri...
Definition report.hpp:168
bool multiple_instruments_
Whether more than one instrument traded: a caution about survivorship bias.
Definition report.hpp:73
int slippage_ticks_
The ticks of slippage configured; fewer apply where the grid has no further step.
Definition report.hpp:63
std::size_t unfilled_
Orders still queued, unfilled, when the replay ended.
Definition report.hpp:79
static constexpr bool bar_tape_unverified
Always true: the replay cannot know which venue's tape its bars came from.
Definition report.hpp:121
std::optional< Money > lowest_cash_
The lowest cash an execution left, if it went below zero.
Definition report.hpp:180
std::size_t stop_fills_intrabar_
Fills of stops released inside a bar, which assume the price moved continuously through the trigger.
Definition report.hpp:163
std::optional< std::chrono::system_clock::time_point > first_overdrawn_
The first bar end at which cash was below zero.
Definition report.hpp:178
std::optional< std::chrono::system_clock::time_point > lowest_cash_at_
When the lowest cash was reached.
Definition report.hpp:191
std::size_t overdrawn_bars_
Bars that closed with cash below zero in the replay's currency: the replay borrowed,...
Definition report.hpp:176
std::string commission_tariff_
The tariff the commission schedule named; empty for a flat model.
Definition report.hpp:52
std::size_t unchecked_fill_causality_
How many times a fill was allowed without the engine being able to prove its price came after the dec...
Definition report.hpp:106
bool cost_totals_incomplete_
Whether some cost could not be summed exactly, so the cost totals are incomplete.
Definition report.hpp:59
std::size_t unvalued_bars_
Bars whose equity total could not be computed exactly (ReplayResult::unvalued_bars_).
Definition report.hpp:147
std::optional< SellTax > sell_tax_
The sale tax given to the fill model; absent if none was modelled.
Definition report.hpp:193
std::vector< AppliedRate > applied_rates_
Every commission rate that charged an execution, with how many it charged.
Definition report.hpp:36
Decimal64 slippage_basis_points_
The basis points of slippage applied.
Definition report.hpp:61
std::size_t expired_day_orders_
DAY orders withdrawn unfilled when their session ended, as they would expire live.
Definition report.hpp:195
LimitFillPrice limit_fill_
Which price a filled limit order was credited.
Definition report.hpp:29
std::size_t other_currency_executions_
Executions in a currency other than the equity curve's (ReplayConfig::currency_).
Definition report.hpp:189
std::optional< VenueKey > asserted_venue_
The venue costs were computed at, as the caller asserted it (a replay cannot observe where a fill exe...
Definition report.hpp:50
static constexpr bool buying_power_is_ledger_cash
Always true: a replay's buying power is the ledger's own cash, not a broker's figure.
Definition report.hpp:138
static constexpr bool sellable_is_whole_holding
Always true: a replay reports every held share as sellable.
Definition report.hpp:129
The summary of a replay; read halted_ and caveats_ before the figures.
Definition report.hpp:203
std::optional< BrokerError > halted_
Why the replay stopped early, if it did; read this before any figure.
Definition report.hpp:296
math::Scalar total_turnover_
One-way turnover summed over the replay, as a fraction of equity.
Definition report.hpp:247
std::optional< Decimal64 > slippage_paid_
Slippage given up, in money.
Definition report.hpp:279
Decimal64 opening_equity_
Equity at the first recorded point.
Definition report.hpp:210
math::Scalar total_return_
The total return over the replay, as a fraction; math::undefined() when the opening equity is zero or...
Definition report.hpp:218
Decimal64 closing_equity_
Equity at the last recorded point.
Definition report.hpp:212
math::Scalar max_drawdown_
The worst peak-to-trough decline of equity, as a non-negative fraction.
Definition report.hpp:240
std::size_t refused_
Instructions a risk guard refused.
Definition report.hpp:286
std::vector< Money > commission_by_currency_
Commission paid per currency.
Definition report.hpp:258
std::size_t rejected_
Instructions not placed: reconciliation could not build them, or the replay cannot model them (BINDIN...
Definition report.hpp:284
std::optional< Decimal64 > tax_paid_
Sale tax paid.
Definition report.hpp:309
std::size_t aborted_
Orders cleared from the queue by a halt (unlike ReportCaveats::unfilled_).
Definition report.hpp:298
math::Scalar annualized_sharpe_
The annualized Sharpe ratio; missing() unless a period count was given to summarize.
Definition report.hpp:232
math::Scalar sortino_
The per-period Sortino ratio; see analytics::sortino.
Definition report.hpp:238
std::size_t bars_
The bars replayed.
Definition report.hpp:205
std::vector< Money > institution_by_currency_
Institution fees per currency.
Definition report.hpp:272
std::vector< Money > slippage_by_currency_
Slippage given up per currency.
Definition report.hpp:281
std::optional< Decimal64 > institution_fees_paid_
Institution fees (유관기관제비용) paid.
Definition report.hpp:265
std::optional< Decimal64 > commission_paid_
Commission paid.
Definition report.hpp:256
std::vector< Money > tax_by_currency_
Sale tax paid per currency, over executions in the taxed market.
Definition report.hpp:311
math::Scalar sharpe_
The per-period Sharpe ratio; see analytics::sharpe.
Definition report.hpp:226
ReportCaveats caveats_
What this run assumed and did not model.
Definition report.hpp:301
std::size_t executions_
The executions.
Definition report.hpp:207
Decimal64 basis_points_
The price given up per execution, in ten-thousandths of the price.
Definition cost.hpp:427
int ticks_
Grid steps given up per execution, each a step to the next grid price against the order.
Definition cost.hpp:429