FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
hooks.hpp
Go to the documentation of this file.
1#pragma once
2// Strategy hooks (ADR-0012): the hook table, the compile-time checker and the Fill view.
3//
4// A strategy is a plain class. Its hooks are ordinary public member functions, all optional:
5//
6// on_start(ctx) on_stop(ctx) on_book(ctx, id, book) on_book_ticker(ctx, id, m)
7// on_trade(ctx, id, m) on_option_ticker(ctx, id, m) on_fill(ctx, fill)
8// on_order_update(ctx, u) on_timer(ctx, timer_id, tag) on_connection(ctx, m)
9// on_quoting(ctx, enabled) on_params(ctx) on_balance(ctx, m) on_perp_state(ctx, id, m)
10// on_risk_reject(ctx, r) on_batch_end(ctx)
11//
12// The engine checks every hook name when it is instantiated. If the strategy has a member with a
13// hook's name (function, template, data member, static, inherited or private) and the engine's
14// call does not compile, the build stops with one error per hook:
15//
16// fastmm: on_trade has the wrong signature or is not public; expected
17// void on_trade(auto& ctx, InstrumentId id, const TradeMsg& m)
18//
19// A few likely misspellings (on_fills, on_tick, onBook, ...) produce a deprecation warning instead,
20// because a private helper may use such a name; `static constexpr bool
21// fastmm_allow_near_miss_names = true;` in the strategy silences it. Strategy headers can run the
22// same check early with stand-in context and book types:
23//
24// static_assert(fastmm::verify_strategy<MyMM>());
25//
26// Limits:
27// * a `final` class loses the name probe; only the call check remains, so a wrong signature is
28// silently not called;
29// * a hook that is ambiguous across two bases is reported as a wrong signature;
30// * a hook with a deduced (`auto`) return type must not be checked with verify_strategy: the
31// stand-in types instantiate its body;
32// * private hooks are rejected, also when the class befriends the engine;
33// * implicit argument conversions are accepted (on_timer(auto&, TimerId, int) compiles);
34// * near-miss warnings are emitted inside this header, so a build that includes FastMM as a
35// system header does not show them.
36#include "fastmm/core/enums.hpp"
39#include "fastmm/core/oms.hpp"
41#include "fastmm/core/timer_wheel.hpp"
42
43#include <array>
44#include <concepts>
45#include <cstddef>
46#include <cstdint>
47#include <string_view>
48#include <type_traits>
49
50namespace fastmm {
51
52// One execution as a strategy sees it (on_fill). The engine builds it on its stack after the
53// position and fees are updated. The pointers are valid only during the call; do not store them.
54struct Fill {
55 InstrumentId instrument; // the order's instrument, else the fill message's
56 Side side{}; // the order's side, else the fill message's
57 Price price; // this execution
59 Qty position_delta; // signed change of the position; differs from qty when the commission is
60 // charged in the base asset
61 Notional fee; // quote currency, as booked
62 bool fee_converted = true; // false: commission in a third asset, not booked (counted in stats)
64 bool known = false; // the id matched an order, open or recently terminal (`update->order`)
65 bool late = false; // the order was already terminal when the fill arrived
66 bool order_done = false; // the order reached a terminal state with this fill
67 const OmsUpdate* update = nullptr; // non-null when known
68 const OrderFillMsg* msg = nullptr; // always set by the engine
69};
71// A new order or replace the engine's own risk check refused (RiskEngine, [risk]): nothing was
72// sent and no order exists for it. Delivered to on_risk_reject once the hook that asked (or
73// set_quotes from it) has returned, never from inside the call that was refused.
74struct RiskReject {
79 bool replace; // a replace of `order`; else a new order and `order` is invalid
80 std::uint32_t user_tag; // NewOrderRequest::user_tag; a quote's is QuoteManager::make_tag
85};
87// The hook table. X(Enum, name, word, (parameters), (arguments), "expected signature"). The
88// parameters name the engine's call; `Ctx` and `Book` are the engine's context and book types.
89#define FASTMM_STRATEGY_HOOKS(X) \
90 X(Start, on_start, "start", (Ctx & ctx), (ctx), "void on_start(auto& ctx)") \
91 X(Stop, on_stop, "stop", (Ctx & ctx), (ctx), "void on_stop(auto& ctx)") \
92 X(Book, \
93 on_book, \
94 "book", \
95 (Ctx & ctx, InstrumentId id, const Book& book), \
96 (ctx, id, book), \
97 "void on_book(auto& ctx, InstrumentId id, const auto& book)") \
98 X(BookTicker, \
99 on_book_ticker, \
100 "book_ticker", \
101 (Ctx & ctx, InstrumentId id, const BookTickerMsg& m), \
102 (ctx, id, m), \
103 "void on_book_ticker(auto& ctx, InstrumentId id, const BookTickerMsg& m)") \
104 X(Trade, \
105 on_trade, \
106 "trade", \
107 (Ctx & ctx, InstrumentId id, const TradeMsg& m), \
108 (ctx, id, m), \
109 "void on_trade(auto& ctx, InstrumentId id, const TradeMsg& m)") \
110 X(OptionTicker, \
111 on_option_ticker, \
112 "option_ticker", \
113 (Ctx & ctx, InstrumentId id, const OptionTickerMsg& m), \
114 (ctx, id, m), \
115 "void on_option_ticker(auto& ctx, InstrumentId id, const OptionTickerMsg& m)") \
116 X(Fill, \
117 on_fill, \
118 "fill", \
119 (Ctx & ctx, const Fill& fill), \
120 (ctx, fill), \
121 "void on_fill(auto& ctx, const Fill& fill)") \
122 X(OrderUpdate, \
123 on_order_update, \
124 "order_update", \
125 (Ctx & ctx, const OmsUpdate& u), \
126 (ctx, u), \
127 "void on_order_update(auto& ctx, const OmsUpdate& u)") \
128 X(Timer, \
129 on_timer, \
130 "timer", \
131 (Ctx & ctx, TimerId id, std::uint64_t tag), \
132 (ctx, id, tag), \
133 "void on_timer(auto& ctx, TimerId id, std::uint64_t tag)") \
134 X(Connection, \
135 on_connection, \
136 "connection", \
137 (Ctx & ctx, const ConnectionStateMsg& m), \
138 (ctx, m), \
139 "void on_connection(auto& ctx, const ConnectionStateMsg& m)") \
140 X(Quoting, \
141 on_quoting, \
142 "quoting", \
143 (Ctx & ctx, bool enabled), \
144 (ctx, enabled), \
145 "void on_quoting(auto& ctx, bool enabled)") \
146 X(Params, on_params, "params", (Ctx & ctx), (ctx), "void on_params(auto& ctx)") \
147 X(Balance, \
148 on_balance, \
149 "balance", \
150 (Ctx & ctx, const BalanceMsg& m), \
151 (ctx, m), \
152 "void on_balance(auto& ctx, const BalanceMsg& m)") \
153 X(PerpState, \
154 on_perp_state, \
155 "perp_state", \
156 (Ctx & ctx, InstrumentId id, const PerpStateMsg& m), \
157 (ctx, id, m), \
158 "void on_perp_state(auto& ctx, InstrumentId id, const PerpStateMsg& m)") \
159 X(RiskReject, \
160 on_risk_reject, \
161 "risk_reject", \
162 (Ctx & ctx, const RiskReject& r), \
163 (ctx, r), \
164 "void on_risk_reject(auto& ctx, const RiskReject& r)") \
165 X(BatchEnd, on_batch_end, "batch_end", (Ctx & ctx), (ctx), "void on_batch_end(auto& ctx)")
166
167// Optional state the engine keeps across sessions ([strategy] state_file): `std::string_view
168// state()` returns the bytes to save (valid until the strategy's next hook; the engine copies them
169// at once) and `bool restore(std::string_view)` takes the bytes saved before, false when they are
170// not this strategy's (it then starts fresh). The engine calls restore once, after on_start and
171// before the first event, and state() every state_interval_s, at the end, every
172// state_snapshot_interval_s for the journal, and never from inside another hook. The bytes are the
173// strategy's own format. With a journal, the bytes handed to restore() are journaled
174// (StrategyStateMsg), so a replay restores them too and a backtest can start from them.
175template <class S>
176concept KeepsState = requires(S& s, std::string_view bytes) {
177 { s.state() } -> std::convertible_to<std::string_view>;
178 { s.restore(bytes) } -> std::same_as<bool>;
179};
180
181// Y(misspelling, hook): names that produce a "did you mean" warning.
182#define FASTMM_STRATEGY_HOOK_NEAR_MISSES(Y) \
183 Y(on_fills, on_fill) \
184 Y(on_reject, on_risk_reject) \
185 Y(on_risk_rejected, on_risk_reject) \
186 Y(on_execution, on_fill) \
187 Y(on_trades, on_trade) \
188 Y(on_order_book, on_book) \
189 Y(on_orderbook, on_book) \
190 Y(on_book_update, on_book) \
191 Y(on_depth, on_book) \
192 Y(on_tick, on_book) \
193 Y(on_bbo, on_book_ticker) \
194 Y(on_ticker, on_book_ticker) \
195 Y(on_options_ticker, on_option_ticker) \
196 Y(on_order, on_order_update) \
197 Y(on_order_event, on_order_update) \
198 Y(on_timer_fired, on_timer) \
199 Y(on_connection_state, on_connection) \
200 Y(on_disconnect, on_connection) \
201 Y(on_init, on_start) \
202 Y(on_shutdown, on_stop) \
203 Y(on_quote, on_quoting) \
204 Y(on_param, on_params) \
205 Y(on_parameters, on_params) \
206 Y(on_balances, on_balance) \
207 Y(on_perp, on_perp_state) \
208 Y(on_funding, on_perp_state) \
209 Y(on_mark, on_perp_state) \
210 Y(onBook, on_book) \
211 Y(OnBook, on_book) \
212 Y(onTrade, on_trade) \
213 Y(onFill, on_fill)
214
215enum class Hook : std::uint8_t {
216#define FASTMM_HOOK_ENUM(E, name, word, params, args, sig) E,
218#undef FASTMM_HOOK_ENUM
219 Count
220};
221
222// Absent: no member with the hook's name. Ok: the engine calls it. Mismatch: a member with the
223// name exists but the call does not compile (a build error through verify_strategy).
224enum class HookStatus : std::uint8_t { Absent, Ok, Mismatch };
225
226struct HookInfo {
227 Hook hook;
228 std::string_view name; // "on_trade"
229 std::string_view word; // "trade" (start-up log)
230 std::string_view signature; // "void on_trade(auto& ctx, InstrumentId id, const TradeMsg& m)"
233inline constexpr std::array<HookInfo, static_cast<std::size_t>(Hook::Count)> kHooks{{
234#define FASTMM_HOOK_INFO(E, name, word, params, args, sig) {Hook::E, #name, word, sig},
236#undef FASTMM_HOOK_INFO
237}};
238
239namespace detail::hooks {
240
241#define FASTMM_HOOK_UNPAREN(...) __VA_ARGS__
242
243// Name probe: lookup of `name` in name_probe<S> is ambiguous exactly when S has any member called
244// `name` (function, template, data, static, inherited, private); an ambiguous lookup is a
245// substitution failure, so the requires-expression is false. The conjunction keeps final and
246// non-class types away from the derivation.
247template <class S>
248concept probeable = std::is_class_v<S> && !std::is_final_v<S>;
249
250#define FASTMM_HOOK_PROBE(name) \
251 struct probe_##name { \
252 void name(); \
253 }; \
254 template <class S> \
255 struct name_probe_##name : S, probe_##name {}; \
256 template <class S> \
257 concept declares_##name = probeable<S> && (!requires { &name_probe_##name<S>::name; });
258
259#define FASTMM_HOOK_CALL(E, name, word, params, args, sig) \
260 FASTMM_HOOK_PROBE(name) \
261 template <class S, class Ctx, class Book> \
262 concept calls_##name = requires(S& s, FASTMM_HOOK_UNPAREN params) { \
263 { s.name args } -> std::same_as<void>; \
264 };
266#undef FASTMM_HOOK_CALL
267
268#define FASTMM_HOOK_NEAR_MISS(name, hook) \
269 FASTMM_HOOK_PROBE(name) \
270 template <bool Found> \
271 struct near_miss_##name { \
272 static constexpr bool value = false; \
273 }; \
274 template <> \
275 struct near_miss_##name<true> { \
276 [[deprecated("fastmm: " #name " is not a hook; did you mean " #hook "?")]] \
277 static constexpr bool value = true; \
278 };
280#undef FASTMM_HOOK_NEAR_MISS
281#undef FASTMM_HOOK_PROBE
282
283template <class S>
284concept allows_near_miss_names = requires { requires S::fastmm_allow_near_miss_names; };
285
286// Stand-ins for verify_strategy<S>() in a strategy header, where the engine types are unknown.
287struct StandInContext {};
288struct StandInBook {};
289
290} // namespace detail::hooks
291
292template <class S, class Ctx, class Book>
293[[nodiscard]] constexpr HookStatus hook_status(Hook h) noexcept {
294 switch (h) {
295#define FASTMM_HOOK_STATUS(E, name, word, params, args, sig) \
296 case Hook::E: \
297 if (detail::hooks::calls_##name<S, Ctx, Book>) return HookStatus::Ok; \
298 return detail::hooks::declares_##name<S> ? HookStatus::Mismatch : HookStatus::Absent;
300#undef FASTMM_HOOK_STATUS
301 case Hook::Count:
302 break;
303 }
304 return HookStatus::Absent;
305}
306
307// True when the engine calls S's hook `h` (the engine dispatches on this at compile time).
308template <class S, class Ctx, class Book>
309[[nodiscard]] constexpr bool implements_hook(Hook h) noexcept {
310 return hook_status<S, Ctx, Book>(h) == HookStatus::Ok;
312
313// Compile-time check of every hook of S; always returns true (failures are static_asserts), so it
314// can be written as `static_assert(fastmm::verify_strategy<MyMM>());`.
315template <class S,
316 class Ctx = detail::hooks::StandInContext,
317 class Book = detail::hooks::StandInBook>
318[[nodiscard]] constexpr bool verify_strategy() noexcept {
319 // The local names make clang's "due to requirement '...'" line readable.
320#define FASTMM_HOOK_ASSERT(E, name, word, params, args, sig) \
321 const bool name##_declared = detail::hooks::declares_##name<S>; \
322 const bool name##_signature_matches = detail::hooks::calls_##name<S, Ctx, Book>; \
323 static_assert(!name##_declared || name##_signature_matches, \
324 "fastmm: " #name " has the wrong signature or is not public; expected " sig);
326#undef FASTMM_HOOK_ASSERT
327 const bool near_miss_names_allowed = detail::hooks::allows_near_miss_names<S>;
328#define FASTMM_HOOK_WARN(name, hook) \
329 static_cast<void>(detail::hooks::near_miss_##name < !near_miss_names_allowed && \
330 detail::hooks::declares_##name < S >> ::value);
332#undef FASTMM_HOOK_WARN
333 constexpr bool has_state = requires(S& s) { s.state(); };
334 constexpr bool has_restore = requires(S& s, std::string_view b) { s.restore(b); };
335 static_assert(has_state == has_restore,
336 "fastmm: state() and restore() go together; expected std::string_view state() and "
337 "bool restore(std::string_view)");
338 static_assert(!has_state || KeepsState<S>,
339 "fastmm: expected std::string_view state() and bool restore(std::string_view)");
340 return true;
341}
342
343namespace detail::hooks {
344struct HookSetText {
345 std::array<char, 160> chars{};
346 std::size_t size = 0;
347};
348template <class S, class Ctx, class Book>
349inline constexpr HookSetText hook_set_text = [] {
350 HookSetText t;
351 for (const HookInfo& h : kHooks) {
352 if (!implements_hook<S, Ctx, Book>(h.hook)) continue;
353 if (t.size != 0) t.chars[t.size++] = ' ';
354 for (const char c : h.word) t.chars[t.size++] = c;
355 }
356 return t;
357}();
358} // namespace detail::hooks
359
360// The implemented hooks as words in table order ("start book fill timer connection quoting").
361template <class S, class Ctx, class Book>
362[[nodiscard]] constexpr std::string_view implemented_hooks() noexcept {
363 const detail::hooks::HookSetText& t = detail::hooks::hook_set_text<S, Ctx, Book>;
364 return std::string_view(t.chars.data(), t.size);
365}
366
367} // namespace fastmm
All engine enumerations.
Fixed<Tag>: int64 fixed point with a global 1e-8 scale (ADR-0001).
#define FASTMM_HOOK_STATUS(E, name, word, params, args, sig)
#define FASTMM_STRATEGY_HOOK_NEAR_MISSES(Y)
Y(misspelling, hook): names that produce a "did you mean" warning.
Definition hooks.hpp:184
#define FASTMM_HOOK_ASSERT(E, name, word, params, args, sig)
#define FASTMM_HOOK_NEAR_MISS(name, hook)
#define FASTMM_HOOK_WARN(name, hook)
#define FASTMM_HOOK_CALL(E, name, word, params, args, sig)
Definition hooks.hpp:261
#define FASTMM_HOOK_INFO(E, name, word, params, args, sig)
Event bus message structs (5.5).
Definition strategy.hpp:16
OrderType
Definition enums.hpp:23
constexpr bool implements_hook(Hook h) noexcept
True when the engine calls S's hook h (the engine dispatches on this at compile time).
Definition hooks.hpp:311
RejectReason
Ordered exactly like RiskEngine::check_new evaluates them (5.7), followed by OMS / transport / venue ...
Definition enums.hpp:123
Side
Definition enums.hpp:12
HookStatus
Absent: no member with the hook's name.
Definition hooks.hpp:226
StrongId< struct InstrumentTag, std::uint32_t > InstrumentId
Dense index into the engine's instrument table.
Definition strong_id.hpp:38
constexpr HookStatus hook_status(Hook h) noexcept
Definition hooks.hpp:295
Liquidity
Definition enums.hpp:434
constexpr std::string_view implemented_hooks() noexcept
The implemented hooks as words in table order ("start book fill timer connection quoting").
Definition hooks.hpp:364
constexpr std::array< HookInfo, static_cast< std::size_t >(Hook::Count)> kHooks
Definition hooks.hpp:235
Hook
Definition hooks.hpp:217
FASTMM_HOOK_ENUM(E, name, word, params, args, sig)
constexpr bool verify_strategy() noexcept
Compile-time check of every hook of S; always returns true (failures are static_asserts),...
Definition hooks.hpp:320
Order management (5.8): a Pool<Order> + OpenHashMap<ClientOrderId, Handle> + a ring of recently termi...
Strongly typed identifiers.
InstrumentId instrument
the order's instrument, else the fill message's
Definition hooks.hpp:57
Side side
the order's side, else the fill message's
Definition hooks.hpp:58
Liquidity liquidity
Definition hooks.hpp:65
Qty position_delta
signed change of the position; differs from qty when the commission is charged in the base asset
Definition hooks.hpp:61
Qty qty
Definition hooks.hpp:60
bool known
the id matched an order, open or recently terminal (update->order)
Definition hooks.hpp:66
bool late
the order was already terminal when the fill arrived
Definition hooks.hpp:67
Notional fee
quote currency, as booked
Definition hooks.hpp:63
const OmsUpdate * update
non-null when known
Definition hooks.hpp:69
const OrderFillMsg * msg
always set by the engine
Definition hooks.hpp:70
Price price
this execution
Definition hooks.hpp:59
bool fee_converted
false: commission in a third asset, not booked (counted in stats)
Definition hooks.hpp:64
bool order_done
the order reached a terminal state with this fill
Definition hooks.hpp:68
Definition hooks.hpp:228
Hook hook
Definition hooks.hpp:229
std::string_view name
"on_trade"
Definition hooks.hpp:230
std::string_view signature
"void on_trade(auto\& ctx, InstrumentId id, const TradeMsg\& m)"
Definition hooks.hpp:232
std::string_view word
"trade" (start-up log)
Definition hooks.hpp:231
Definition oms.hpp:74
Definition messages.hpp:233
A new order or replace the engine's own risk check refused (RiskEngine, [risk]): nothing was sent and...
Definition hooks.hpp:76
Side side
Definition hooks.hpp:78
Timestamp time
Definition hooks.hpp:86
ClientOrderId order
Definition hooks.hpp:85
RejectReason reason
Definition hooks.hpp:80
Qty qty
Definition hooks.hpp:84
std::uint32_t user_tag
NewOrderRequest::user_tag; a quote's is QuoteManager::make_tag.
Definition hooks.hpp:82
OrderType type
Definition hooks.hpp:79
bool replace
a replace of order; else a new order and order is invalid
Definition hooks.hpp:81
Price price
Definition hooks.hpp:83
InstrumentId instrument
Definition hooks.hpp:77
Definition time.hpp:45