FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
metrics.hpp
Go to the documentation of this file.
1#pragma once
2// Metrics (8.4) computed from the result rows: equity bars (1 s by default), fills and
3// orders. Every input is an exact integer (raw 1e-8 fixed point); the doubles here are
4// reporting-only and never feed back into the simulation.
7
8#include <cstdint>
9#include <vector>
10
11namespace fastmm::bt {
12
13struct FillRows;
14struct EquityRows;
15struct OrderRows;
16
17// How the net PnL of the run splits into the parts a market maker can act on:
18// net == spread_capture + mid_drift - fees_paid + rebates_received + residual
19// `mid_drift` is the move of the mid from each fill to the end of the run, valued at the signed
20// quantity of the fill: adverse selection plus the mark-to-market of whatever is still open.
21// `residual` is whatever the average-cost ledger books differently (fixed-point rounding, a
22// contract multiplier, an instrument whose book never had two sides). A large residual means the
23// decomposition does not describe this run; it is printed, not hidden.
24//
25// A fill whose venue mid is unknown at the moment of the fill (the sweep that filled it emptied
26// that side of the book) has no spread capture to attribute, so its whole
27// signed_qty * (final mid - price) goes to mid_drift instead. `capture_fills` and
28// `capture_notional` cover the rest, and are what spread_capture_bps divides by.
29struct PnlDecomposition {
30 double spread_capture = 0.0; // sum of signed_qty * (mid at fill - fill price)
31 double spread_capture_bps = 0.0;
32 double mid_drift = 0.0; // sum of signed_qty * (final mid - mid at fill)
33 double fees_paid = 0.0; // sum of the positive fees, >= 0
34 double rebates_received = 0.0; // sum of the negative fees with the sign flipped, >= 0
35 double net = 0.0; // spread_capture + mid_drift - fees_paid + rebates_received
36 double residual = 0.0; // reported net PnL - net
37 double notional = 0.0; // traded notional of every fill
38 double capture_notional = 0.0; // traded notional of the fills that had a mid at the fill
39 std::uint64_t capture_fills = 0;
40 std::uint64_t fills = 0;
41};
43// Diagnostics of how the fills were obtained, next to the markouts.
44struct FillQuality {
45 // Notional-weighted (mid at fill - price) for buys, (price - mid at fill) for sells, over the
46 // fills that had a venue mid. This is the realised spread: what markouts start from.
47 double realized_spread_bps = 0.0;
48 double realized_spread_quote = 0.0;
49 double at_touch_share = 0.0; // fills at the venue's best price on our side
50 double inside_touch_share = 0.0; // fills at a better price than the touch (it improved it)
51 double behind_touch_share = 0.0; // fills at a worse price than the touch
52 double through_touch_share = 0.0; // fills at or beyond the opposite touch (aggressive)
53 // Venue arrival of a new order to its first fill. Only orders that filled are counted.
54 std::uint64_t time_to_fill_p50_ns = 0;
55 std::uint64_t time_to_fill_p90_ns = 0;
56 std::uint64_t time_to_fill_p99_ns = 0;
57 // Share of new orders that got at least one fill (fill_ratio counts fills per order instead).
58 double fill_rate_per_quote = 0.0;
59 std::uint64_t quotes_placed = 0;
60 std::uint64_t quotes_filled = 0;
61 // Displayed quantity still ahead of the order when it filled; the L2Queue fill model only.
63 double queue_ahead_mean = 0.0; // base units
64 double queue_ahead_p50 = 0.0;
65 double queue_ahead_p90 = 0.0;
66};
68struct Metrics {
69 // PnL in quote currency at the last bar: net == realized + unrealized - fees.
70 double net_pnl = 0.0;
71 double realized_pnl = 0.0;
72 double unrealized_pnl = 0.0;
73 double fees = 0.0;
74 double final_position = 0.0; // base units, summed over instruments
75 double sharpe_bar = 0.0; // mean / sample stdev of per-bar equity changes
76 // sharpe_bar * sqrt(bars per 365-day year); NaN for runs shorter than
77 // kMinAnnualizedDurationS, where the scaling turns noise into four-digit values.
78 double sharpe_annualized = 0.0;
79 double max_drawdown = 0.0; // largest fall of equity from its peak, quote currency, >= 0
80 // max_drawdown / initial_capital; NaN when initial_capital <= 0.
81 double max_drawdown_pct = 0.0;
82 std::uint64_t fills = 0;
83 std::uint64_t maker_fills = 0;
84 std::uint64_t taker_fills = 0;
85 std::uint64_t orders = 0; // new orders sent
86 std::uint64_t cancels = 0;
87 std::uint64_t replaces = 0;
88 std::uint64_t rejects = 0;
89 double fill_ratio = 0.0; // fills / new orders
90 double spread_captured_bps = 0.0; // mean of (mid - px) / mid for buys, (px - mid) / mid sells
91 double volume_base = 0.0;
92 double volume_quote = 0.0;
93 double inventory_mean = 0.0; // over bars
94 double inventory_abs_mean = 0.0; // over bars
95 double inventory_max = 0.0; // max |position| over bars
96 double quote_uptime = 0.0; // fraction of bars with both a bid and an ask resting
97 std::uint64_t bars = 0;
98 double duration_s = 0.0;
99 // Virtual tick-to-order: engine receipt of the triggering event (T0, virtual clock) to the
100 // order's arrival at the simulated venue. Deterministic; includes the order latency model.
101 std::uint64_t virtual_tick_to_order_p50_ns = 0;
102 std::uint64_t virtual_tick_to_order_p99_ns = 0;
103 // Measured wall clock per engine step that consumed market data (0 when not measured).
104 std::uint64_t wall_tick_to_order_p50_ns = 0;
105 std::uint64_t wall_tick_to_order_p99_ns = 0;
106 // Where the net PnL came from, and how good the fills were. See markout.hpp.
109 std::vector<MarkoutHorizon> markouts; // one entry per configured horizon, in order
111 // The markout of one horizon, or nullptr when it was not measured.
112 [[nodiscard]] const MarkoutHorizon* markout(std::int64_t horizon_ns) const noexcept;
113};
115// Shortest run whose Sharpe ratio is annualised: one day.
116inline constexpr double kMinAnnualizedDurationS = 86400.0;
117
119 Duration bar = seconds(1);
120 double initial_capital = 0.0; // quote currency; only used for max_drawdown_pct
121 std::uint64_t rejects = 0;
122 std::uint64_t wall_p50_ns = 0;
123 std::uint64_t wall_p99_ns = 0;
124 // Venue mid of each instrument at the end of the run (raw price, indexed by instrument id).
125 // The decomposition marks the leftover inventory and the mid drift against it; without it
126 // only instrument 0's last equity-bar mid is available.
127 std::vector<std::int64_t> final_mid;
128 // Set when the venue could report a queue position (FillModel::L2Queue).
130 // Time of the last event of the run: a fill whose horizon falls after it could not be marked
131 // because the data ended, which is a different thing from a one-sided book.
132 std::int64_t end_ts = 0;
133};
135[[nodiscard]] Metrics compute_metrics(const EquityRows& equity,
136 const FillRows& fills,
137 const OrderRows& orders,
138 const MetricsInputs& in);
139
140} // namespace fastmm::bt
Post-fill markouts: the metric that separates spread captured from adverse selection.
Definition backtest_config.hpp:39
constexpr double kMinAnnualizedDurationS
Shortest run whose Sharpe ratio is annualised: one day.
Definition metrics.hpp:118
Metrics compute_metrics(const EquityRows &equity, const FillRows &fills, const OrderRows &orders, const MetricsInputs &in)
constexpr Duration seconds(std::int64_t v) noexcept
Definition time.hpp:41
Definition time.hpp:22
Definition result.hpp:54
Diagnostics of how the fills were obtained, next to the markouts.
Definition metrics.hpp:46
double queue_ahead_mean
base units
Definition metrics.hpp:65
std::uint64_t quotes_placed
Definition metrics.hpp:61
std::uint64_t time_to_fill_p50_ns
Venue arrival of a new order to its first fill. Only orders that filled are counted.
Definition metrics.hpp:56
double queue_ahead_p90
Definition metrics.hpp:67
double inside_touch_share
fills at a better price than the touch (it improved it)
Definition metrics.hpp:52
std::uint64_t quotes_filled
Definition metrics.hpp:62
double realized_spread_bps
Notional-weighted (mid at fill - price) for buys, (price - mid at fill) for sells,...
Definition metrics.hpp:49
std::uint64_t time_to_fill_p90_ns
Definition metrics.hpp:57
std::uint64_t time_to_fill_p99_ns
Definition metrics.hpp:58
double realized_spread_quote
Definition metrics.hpp:50
double at_touch_share
fills at the venue's best price on our side
Definition metrics.hpp:51
bool queue_position_known
Displayed quantity still ahead of the order when it filled; the L2Queue fill model only.
Definition metrics.hpp:64
double behind_touch_share
fills at a worse price than the touch
Definition metrics.hpp:53
double through_touch_share
fills at or beyond the opposite touch (aggressive)
Definition metrics.hpp:54
double fill_rate_per_quote
Share of new orders that got at least one fill (fill_ratio counts fills per order instead).
Definition metrics.hpp:60
double queue_ahead_p50
Definition metrics.hpp:66
Definition result.hpp:20
Buckets of one horizon, all over the same measured fills.
Definition markout.hpp:58
Definition metrics.hpp:120
Duration bar
Definition metrics.hpp:121
std::int64_t end_ts
Time of the last event of the run: a fill whose horizon falls after it could not be marked because th...
Definition metrics.hpp:134
double initial_capital
quote currency; only used for max_drawdown_pct
Definition metrics.hpp:122
bool queue_position_known
Set when the venue could report a queue position (FillModel::L2Queue).
Definition metrics.hpp:131
std::uint64_t wall_p50_ns
Definition metrics.hpp:124
std::uint64_t wall_p99_ns
Definition metrics.hpp:125
std::vector< std::int64_t > final_mid
Venue mid of each instrument at the end of the run (raw price, indexed by instrument id).
Definition metrics.hpp:129
std::uint64_t rejects
Definition metrics.hpp:123
Definition metrics.hpp:70
double fill_ratio
fills / new orders
Definition metrics.hpp:91
double final_position
base units, summed over instruments
Definition metrics.hpp:76
std::uint64_t replaces
Definition metrics.hpp:89
std::uint64_t rejects
Definition metrics.hpp:90
double fees
Definition metrics.hpp:75
double net_pnl
PnL in quote currency at the last bar: net == realized + unrealized - fees.
Definition metrics.hpp:72
std::vector< MarkoutHorizon > markouts
one entry per configured horizon, in order
Definition metrics.hpp:111
double inventory_max
max |position| over bars
Definition metrics.hpp:97
std::uint64_t maker_fills
Definition metrics.hpp:85
std::uint64_t virtual_tick_to_order_p99_ns
Definition metrics.hpp:104
double spread_captured_bps
mean of (mid - px) / mid for buys, (px - mid) / mid sells
Definition metrics.hpp:92
PnlDecomposition decomposition
Where the net PnL came from, and how good the fills were. See markout.hpp.
Definition metrics.hpp:109
double sharpe_annualized
sharpe_bar * sqrt(bars per 365-day year); NaN for runs shorter than kMinAnnualizedDurationS,...
Definition metrics.hpp:80
FillQuality fill_quality
Definition metrics.hpp:110
std::uint64_t taker_fills
Definition metrics.hpp:86
std::uint64_t bars
Definition metrics.hpp:99
double volume_quote
Definition metrics.hpp:94
double duration_s
Definition metrics.hpp:100
std::uint64_t cancels
Definition metrics.hpp:88
double volume_base
Definition metrics.hpp:93
std::uint64_t wall_tick_to_order_p99_ns
Definition metrics.hpp:107
double unrealized_pnl
Definition metrics.hpp:74
const MarkoutHorizon * markout(std::int64_t horizon_ns) const noexcept
The markout of one horizon, or nullptr when it was not measured.
double realized_pnl
Definition metrics.hpp:73
double quote_uptime
fraction of bars with both a bid and an ask resting
Definition metrics.hpp:98
std::uint64_t virtual_tick_to_order_p50_ns
Virtual tick-to-order: engine receipt of the triggering event (T0, virtual clock) to the order's arri...
Definition metrics.hpp:103
double inventory_mean
over bars
Definition metrics.hpp:95
std::uint64_t orders
new orders sent
Definition metrics.hpp:87
std::uint64_t fills
Definition metrics.hpp:84
double sharpe_bar
mean / sample stdev of per-bar equity changes
Definition metrics.hpp:77
double max_drawdown
largest fall of equity from its peak, quote currency, >= 0
Definition metrics.hpp:81
double max_drawdown_pct
max_drawdown / initial_capital; NaN when initial_capital <= 0.
Definition metrics.hpp:83
std::uint64_t wall_tick_to_order_p50_ns
Measured wall clock per engine step that consumed market data (0 when not measured).
Definition metrics.hpp:106
double inventory_abs_mean
over bars
Definition metrics.hpp:96
Definition result.hpp:74
How the net PnL of the run splits into the parts a market maker can act on: net == spread_capture + m...
Definition metrics.hpp:31
double notional
traded notional of every fill
Definition metrics.hpp:39
double spread_capture
sum of signed_qty * (mid at fill - fill price)
Definition metrics.hpp:32
std::uint64_t fills
Definition metrics.hpp:42
double capture_notional
traded notional of the fills that had a mid at the fill
Definition metrics.hpp:40
double residual
reported net PnL - net
Definition metrics.hpp:38
double spread_capture_bps
Definition metrics.hpp:33
double fees_paid
sum of the positive fees, >= 0
Definition metrics.hpp:35
std::uint64_t capture_fills
Definition metrics.hpp:41
double rebates_received
sum of the negative fees with the sign flipped, >= 0
Definition metrics.hpp:36
double net
spread_capture + mid_drift - fees_paid + rebates_received
Definition metrics.hpp:37
double mid_drift
sum of signed_qty * (final mid - mid at fill)
Definition metrics.hpp:34
Time primitives.