FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
quote_manager.hpp
Go to the documentation of this file.
1#pragma once
2// QuoteManager (5.6): diffs the strategy's DesiredQuotes against the orders resting at the
3// venue and emits the minimal set of New / Cancel / Replace actions.
4//
5// Rules:
6// * level i of a side maps to slot i (one order per slot);
7// * hysteresis: keep an order whose price is within min_requote_ticks of the desired
8// price and whose leaves cover min_qty_bps of the desired quantity;
9// * never re-quote a slot within min_requote_interval of its last change;
10// * orders in a Pending* state are never touched: the slot records the target instead and applies
11// it when the order's ack or terminal update arrives via on_order_update() (dropped if the
12// instrument is pulled first);
13// * a slot only counts an order as its own if it is live and carries the slot's tag (order
14// slots are reused, and a replace changes the order's id);
15// * while an instrument is pulled, an order that becomes working late (its ack arrived after
16// the pull) is cancelled at once;
17// * a New rejected by the venue (other than a post-only cross) blocks new orders on that side
18// for reject_backoff, doubling on each further reject up to reject_backoff_max and reset by
19// an ack on that side, so an unfundable side is not resent on every requote;
20// * an instrument whose venue supports replace (set_replace(), with supports_replace) gets a
21// single Replace; otherwise Cancel now and New only once the cancel's terminal update arrives
22// via on_order_update() (cancel-then-new keeps momentary exposure down and never double-quotes
23// a level, and the New is checked against the balance once the old order's hold is released);
24// * such a deferred New (QuoteAction::deferred) that the balance no longer covers -- the order it
25// replaced filled instead of cancelling -- is withheld (QuoteAction::withheld, kept_balance)
26// rather than refused by risk: the target predates the fill, the strategy's next quotes decide;
27// * pull_quotes(keep_desired) pauses an instrument (the engine during reconciliation): resume()
28// re-applies the last desired quotes unless the instrument was pulled for good meanwhile;
29// * a New or Replace the placer holds back for want of an order token (QuoteAction::rate_limited)
30// is neither a reject nor a backoff: the instrument joins a FIFO of starved instruments, and
31// the engine re-applies their last desired quotes (retry()) in that order as tokens come back,
32// so a burst of requotes on many instruments is served in turn instead of refused event by
33// event.
34//
35// Actions are executed by the caller through a Placer callable so the QuoteManager stays
36// independent of risk/OMS/transport wiring:
37// bool place(QuoteAction& a); // New: sets a.handle on success; returns false on reject
38#include "fastmm/core/config_macros.hpp"
39#include "fastmm/core/containers/ring_buffer.hpp"
40#include "fastmm/core/containers/static_vector.hpp"
43#include "fastmm/core/oms.hpp"
44#include "fastmm/core/time.hpp"
45
46#include <array>
47#include <cstddef>
48#include <cstdint>
49
50namespace fastmm {
51
52inline constexpr std::size_t kMaxQuoteLevels = 8;
53
55 StaticVector<Level, kMaxQuoteLevels> bids; // index 0 = closest to mid
56 StaticVector<Level, kMaxQuoteLevels> asks;
57 // The pool account a level's order goes from (NewOrderRequest::account); invalid = the engine's
58 // automatic routing. A resting order on another account is cancelled and placed again.
59 std::array<VenueId, kMaxQuoteLevels> bid_account{};
60 std::array<VenueId, kMaxQuoteLevels> ask_account{};
61 void clear() noexcept {
63 asks.clear();
64 bid_account = {};
65 ask_account = {};
66 }
67 [[nodiscard]] VenueId account(Side s, std::uint32_t lvl) const noexcept {
68 return lvl < kMaxQuoteLevels ? (s == Side::Buy ? bid_account : ask_account)[lvl] : VenueId{};
69 }
70 [[nodiscard]] const StaticVector<Level, kMaxQuoteLevels>& side(Side s) const noexcept {
71 return s == Side::Buy ? bids : asks;
72 }
73 [[nodiscard]] bool empty() const noexcept { return bids.empty() && asks.empty(); }
74 // Append the next level of a side (index 0 first). A non-positive price or quantity is dropped,
75 // as is a level beyond kMaxQuoteLevels; returns whether the level was added.
76 bool bid(Price px, Qty qty) noexcept {
77 return px.is_positive() && qty.is_positive() && bids.push_back(Level{px, qty});
78 }
79 bool ask(Price px, Qty qty) noexcept {
80 return px.is_positive() && qty.is_positive() && asks.push_back(Level{px, qty});
81 }
82 // Never cross ourselves at level 0: an ask at or below the best bid moves one tick above it.
83 void uncross(Price tick) noexcept {
84 if (!bids.empty() && !asks.empty() && bids[0].price >= asks[0].price)
85 asks[0].price = bids[0].price + tick;
86 }
87};
88
89struct QuoteParams {
90 std::int64_t min_requote_ticks = 1; // |desired - resting| < this -> keep
92 std::int64_t min_qty_bps = 8000; // leaves >= 80 % of desired -> qty ok
93 bool supports_replace = false; // off: never replace; on: where the instrument's venue can
94 // Replace only if every instrument's venue can (the engine before replace was per venue; set to
95 // replay journals recorded then).
96 bool replace_all_venues = false;
97 bool post_only = true;
98 Duration reject_backoff = milliseconds(1000); // 0 = no backoff after venue rejects
100 // Order tokens ([risk] orders_per_sec) a quote that does not reduce the position leaves for the
101 // orders a strategy sends itself (hedges, unwinds) and for quotes that do. Read by the engine's
102 // placer; 0 = quotes may take the last token.
103 std::int64_t token_reserve = 0;
104};
106enum class QuoteActionKind : std::uint8_t { New, Cancel, Replace };
107
112 std::uint32_t level;
113 Handle<Order> handle; // Cancel/Replace input; New output
114 ClientOrderId cl_ord_id; // New output (the placer fills it)
118 // New: the target was recorded while an earlier order held the slot and is placed now that it
119 // ended (on_order_update), so it was decided against a balance that order may have changed.
120 bool deferred;
121 bool withheld; // New output: not sent, the balance does not cover it (deferred only)
122 // New / Replace output: not sent, no order token for it now (QuoteParams::token_reserve); the
123 // instrument is retried when tokens come back.
124 bool rate_limited;
125 VenueId account{}; // New: the pool account to send it from (invalid = automatic)
128struct QuoteStats {
129 std::uint64_t news = 0;
130 std::uint64_t cancels = 0;
131 std::uint64_t replaces = 0;
132 std::uint64_t kept_hysteresis = 0;
133 std::uint64_t kept_interval = 0;
134 std::uint64_t skipped_pending = 0;
135 std::uint64_t rejected = 0;
136 std::uint64_t reject_backoffs = 0; // venue rejects that started or extended a side backoff
137 std::uint64_t kept_backoff = 0; // News withheld because their side was backing off
138 std::uint64_t kept_balance = 0; // deferred News withheld: the balance no longer covers them
139 std::uint64_t kept_rate_limit = 0; // News / Replaces held back for want of an order token
140 std::uint64_t retries = 0; // retry() calls that re-applied a starved instrument's quotes
143class QuoteManager {
144 public:
145 explicit QuoteManager(const QuoteParams& p = {}) noexcept : params_(p) { resolve_replace(); }
146
147 [[nodiscard]] const QuoteParams& params() const noexcept { return params_; }
148 void set_params(const QuoteParams& p) noexcept {
149 params_ = p;
150 resolve_replace();
151 }
152 [[nodiscard]] const QuoteStats& stats() const noexcept { return stats_; }
153 // Whether the instrument's venue amends in place (default true; supports_replace still applies).
154 void set_replace(InstrumentId id, bool on) noexcept {
155 InstState& st = state_[id.value];
156 st.venue_no_replace = !on;
157 st.replace = params_.supports_replace && on;
158 }
159 [[nodiscard]] bool replaces(InstrumentId id) const noexcept { return state_[id.value].replace; }
160
161 // user_tag encoding so a terminal OmsUpdate can be mapped back to its slot.
162 [[nodiscard]] static constexpr std::uint32_t make_tag(Side s, std::uint32_t level) noexcept {
163 return 0x5100'0000U | (static_cast<std::uint32_t>(s) << 8) | level;
165 [[nodiscard]] static constexpr bool is_quote_tag(std::uint32_t tag) noexcept {
166 return (tag & 0xFFFF'0000U) == 0x5100'0000U;
168
169 // Reconciles desired vs resting for one instrument. Returns the number of actions placed.
170 template <class Placer>
171 std::uint32_t reconcile(const Instrument& inst,
172 const DesiredQuotes& desired,
173 const Oms& oms,
174 Timestamp now,
175 Placer&& place) noexcept {
176 InstState& st = state_[inst.id.value];
177 st.pulled = false;
178 st.resumable = false;
179 st.desired = desired;
180 std::uint32_t actions = 0;
181 for (Side side : {Side::Buy, Side::Sell}) {
182 const auto& want = st.desired.side(side);
183 for (std::uint32_t lvl = 0; lvl < kMaxQuoteLevels; ++lvl) {
184 Slot& slot = st.slots[static_cast<std::size_t>(side)][lvl];
185 const bool has_want = lvl < want.size() && want[lvl].qty.is_positive();
186 actions += reconcile_slot(inst,
187 st,
188 oms,
189 side,
190 lvl,
191 slot,
192 has_want ? want[lvl] : Level{},
193 st.desired.account(side, lvl),
194 now,
195 place);
196 }
197 }
198 return actions;
199 }
200
201 // Cancels every working quote on the instrument and suppresses re-quoting until the
202 // next reconcile(). With keep_desired (a pause, not a decision to stop quoting) the last desired
203 // quotes are kept for resume(), unless the instrument was already pulled.
204 template <class Placer>
205 std::uint32_t pull_quotes(const Instrument& inst,
206 const Oms& oms,
207 Placer&& place,
208 bool keep_desired = false) noexcept {
209 InstState& st = state_[inst.id.value];
210 if (keep_desired) {
211 st.resumable = st.resumable || (!st.pulled && !st.desired.empty());
212 } else {
213 st.resumable = false;
214 st.desired.clear();
215 }
216 st.pulled = true;
217 std::uint32_t actions = 0;
218 for (Side side : {Side::Buy, Side::Sell}) {
219 for (std::uint32_t lvl = 0; lvl < kMaxQuoteLevels; ++lvl) {
220 Slot& slot = st.slots[static_cast<std::size_t>(side)][lvl];
221 slot.apply_want = false;
222 if (!owns(oms, inst, side, lvl, slot)) continue;
223 const Order& o = oms.get(slot.handle);
224 if (!o.is_working()) continue; // pending: cannot touch; a later pull will catch it
225 actions += cancel(inst, side, lvl, slot, place);
226 }
227 }
228 return actions;
229 }
230
231 // Re-applies the desired quotes kept by pull_quotes(keep_desired). Returns the actions placed.
232 template <class Placer>
233 std::uint32_t resume(const Instrument& inst,
234 const Oms& oms,
236 Placer&& place) noexcept {
237 InstState& st = state_[inst.id.value];
238 if (!st.resumable) return 0;
239 const DesiredQuotes desired = st.desired;
240 return reconcile(inst, desired, oms, now, place);
241 }
242 [[nodiscard]] bool resumable(InstrumentId id) const noexcept {
243 return state_[id.value].resumable;
245
246 // Instruments whose quotes were held back for want of an order token, oldest first.
247 [[nodiscard]] bool starved() const noexcept { return !starved_.empty(); }
248 [[nodiscard]] std::size_t starved_count() const noexcept { return starved_.size(); }
249 // Takes the oldest starved instrument off the queue (the caller checks starved() first).
250 [[nodiscard]] InstrumentId pop_starved() noexcept {
251 const InstrumentId id{starved_.front()};
252 starved_.pop();
253 state_[id.value].starved = false;
254 return id;
255 }
256 // Re-applies the last desired quotes of an instrument popped from the starved queue; one that was
257 // pulled meanwhile stays pulled. An action held back again queues it at the back.
258 template <class Placer>
259 std::uint32_t retry(const Instrument& inst,
260 const Oms& oms,
262 Placer&& place) noexcept {
263 InstState& st = state_[inst.id.value];
264 if (st.pulled) return 0;
265 ++stats_.retries;
266 const DesiredQuotes desired = st.desired;
267 return reconcile(inst, desired, oms, now, place);
268 }
269
270 // Feed every OmsUpdate here. A terminal update frees the slot it belonged to and, if a target
271 // was recorded for it (cancel-then-new, or a requote that met the order pending), places the New
272 // now. The ack of an order that was pending at a requote applies the recorded target. While the
273 // instrument is pulled, an order that has just become working is cancelled instead.
274 template <class Placer>
275 void on_order_update(const OmsUpdate& u,
276 const Instrument& inst,
277 const Oms& oms,
278 Timestamp now,
279 Placer&& place) noexcept {
280 if (!is_quote_tag(u.order.user_tag)) return;
281 const Side side = static_cast<Side>((u.order.user_tag >> 8) & 1U);
282 const std::uint32_t lvl = u.order.user_tag & 0xFFU;
283 if (lvl >= kMaxQuoteLevels || u.order.instrument != inst.id) return;
284 InstState& st = state_[inst.id.value];
285 Slot& slot = st.slots[static_cast<std::size_t>(side)][lvl];
286 if (!u.terminal) {
287 if (u.prev == OrderState::PendingNew && u.order.is_working())
288 st.backoff[static_cast<std::size_t>(side)] = Duration{}; // the venue takes this side
289 if (!u.handle.valid() || slot.handle.idx != u.handle.idx ||
290 !owns(oms, inst, side, lvl, slot) || !u.order.is_working())
291 return;
292 if (st.pulled) {
293 static_cast<void>(cancel(inst, side, lvl, slot, place));
294 return;
295 }
296 if (slot.apply_want &&
297 (u.prev == OrderState::PendingNew || u.prev == OrderState::PendingReplace)) {
298 slot.apply_want = false;
299 static_cast<void>(reconcile_slot(
300 inst, st, oms, side, lvl, slot, slot.want, slot.want_account, now, place));
301 }
302 return;
303 }
304 if (u.order.state == OrderState::Rejected && u.prev == OrderState::PendingNew)
305 on_venue_reject(st, side, u.order.reject_reason, now);
306 // The terminal order was this slot's unless the slot already holds a newer live order. Ids
307 // are not compared: a replace changes the order's id, and a stale id used to drop the update
308 // and leave the slot waiting for a terminal state forever.
309 if (owns(oms, inst, side, lvl, slot)) return;
310 slot.handle = Handle<Order>{};
311 slot.cl_ord_id = ClientOrderId{};
312 slot.awaiting_terminal = false;
313 if (st.pulled || !slot.apply_want) return;
314 slot.apply_want = false;
315 if (!slot.want.qty.is_positive()) return;
316 if (backing_off(st, side, now)) {
317 ++stats_.kept_backoff;
318 return;
319 }
320 submit_new(inst, side, lvl, slot, slot.want, slot.want_account, now, place, /*deferred=*/true);
321 }
322
323 [[nodiscard]] Handle<Order> slot_handle(InstrumentId id,
324 Side s,
325 std::uint32_t level) const noexcept {
326 return state_[id.value].slots[static_cast<std::size_t>(s)][level].handle;
327 }
328 [[nodiscard]] bool pulled(InstrumentId id) const noexcept { return state_[id.value].pulled; }
329 // The last ladder reconcile() applied to the instrument (empty after a pull).
330 [[nodiscard]] const DesiredQuotes& desired(InstrumentId id) const noexcept {
331 return state_[id.value].desired;
333 // The instrument waits for an order token (a New or Replace was rate-limited).
334 [[nodiscard]] bool starved(InstrumentId id) const noexcept { return state_[id.value].starved; }
335 // The side is in its venue-reject backoff at `now`.
336 [[nodiscard]] bool backing_off(InstrumentId id, Side s, Timestamp now) const noexcept {
337 return backing_off(state_[id.value], s, now);
339
340 private:
341 struct Slot {
342 Handle<Order> handle{};
343 ClientOrderId cl_ord_id{};
344 Timestamp last_requote{};
345 Level want{}; // target to apply once the slot's pending order resolves
346 VenueId want_account{}; // ... and its account (DesiredQuotes::account)
347 bool apply_want = false; // `want` is pending (qty 0: no quote on this level)
348 bool awaiting_terminal = false;
349 };
350 struct InstState {
351 Slot slots[2][kMaxQuoteLevels];
352 DesiredQuotes desired;
353 bool pulled = false;
354 bool resumable = false; // paused by pull_quotes(keep_desired): resume() re-applies `desired`
355 bool venue_no_replace = false; // the venue cannot amend in place (set_replace)
356 bool replace = false; // supports_replace && !venue_no_replace
357 Duration backoff[2] = {}; // current backoff per side (0 = none)
358 Timestamp blocked_until[2] = {}; // no News on the side before this
359 bool starved = false; // queued in starved_
360 };
361
362 [[nodiscard]] static bool backing_off(const InstState& st, Side side, Timestamp now) noexcept {
363 const auto i = static_cast<std::size_t>(side);
364 return st.backoff[i].ns > 0 && now < st.blocked_until[i];
365 }
366 void on_venue_reject(InstState& st, Side side, RejectReason reason, Timestamp now) noexcept {
367 // A post-only cross is a stale price, fixed by the next requote; other venue rejects (no
368 // balance, filters, permissions) repeat until something changes.
369 if (params_.reject_backoff.ns <= 0 || reason == RejectReason::PostOnlyWouldCross) return;
370 const auto i = static_cast<std::size_t>(side);
371 Duration next = st.backoff[i].ns <= 0 ? params_.reject_backoff : st.backoff[i] + st.backoff[i];
372 if (params_.reject_backoff_max.ns > 0 && next.ns > params_.reject_backoff_max.ns)
373 next = params_.reject_backoff_max;
374 st.backoff[i] = next;
375 st.blocked_until[i] = now + next;
376 ++stats_.reject_backoffs;
377 }
378
379 // One slot of reconcile(): keep, cancel, replace or place so the slot converges on `target`
380 // (qty 0: no quote).
381 template <class Placer>
382 std::uint32_t reconcile_slot(const Instrument& inst,
383 InstState& st,
384 const Oms& oms,
385 Side side,
386 std::uint32_t lvl,
387 Slot& slot,
388 Level target,
389 VenueId account,
390 Timestamp now,
391 Placer& place) noexcept {
392 const bool has_want = target.qty.is_positive();
393 if (owns(oms, inst, side, lvl, slot)) {
394 const Order& o = oms.get(slot.handle);
395 if (is_pending(o.state)) {
396 // Cannot touch it now; apply the target when its ack or terminal update arrives.
397 ++stats_.skipped_pending;
398 slot.want = target;
399 slot.want_account = account;
400 slot.apply_want = true;
401 return 0;
402 }
403 slot.apply_want = false;
404 if (!has_want) return cancel(inst, side, lvl, slot, place);
405 // Asked for on another account: an order cannot move between accounts.
406 if (account.valid() && o.venue != account) {
407 slot.want = target;
408 slot.want_account = account;
409 slot.apply_want = true;
410 return cancel(inst, side, lvl, slot, place);
411 }
412 const std::int64_t dpx = (target.price - o.price).abs().raw / inst.tick.raw;
413 const bool qty_ok = static_cast<Int128>(o.leaves_qty().raw) * 10'000 >=
414 static_cast<Int128>(target.qty.raw) * params_.min_qty_bps;
415 if (dpx < params_.min_requote_ticks && qty_ok) {
416 ++stats_.kept_hysteresis;
417 return 0;
418 }
419 if (now - slot.last_requote < params_.min_requote_interval) {
420 ++stats_.kept_interval;
421 return 0;
422 }
423 if (st.replace) {
424 QuoteAction a{QuoteActionKind::Replace,
425 inst.id,
426 side,
427 lvl,
428 slot.handle,
430 target.price,
431 target.qty,
432 params_.post_only,
433 false,
434 false,
435 false};
436 if (!place(a)) {
437 held_back(st, inst.id, a);
438 return 0;
439 }
440 ++stats_.replaces;
441 slot.last_requote = now;
442 return 1;
443 }
444 slot.want = target;
445 slot.want_account = account;
446 slot.apply_want = true;
447 return cancel(inst, side, lvl, slot, place);
448 }
449 // No resting order in this slot. A cancel still in flight keeps the order live (Pending*,
450 // handled above), so any earlier order is final in the OMS and there is nothing left to
451 // wait for, even if its terminal update never reached on_order_update().
452 slot.handle = Handle<Order>{};
453 slot.awaiting_terminal = false;
454 slot.apply_want = false;
455 if (!has_want) return 0;
456 if (backing_off(st, side, now)) {
457 ++stats_.kept_backoff;
458 return 0;
459 }
460 return submit_new(inst, side, lvl, slot, target, account, now, place);
461 }
462
463 // The slot's order is live and really the slot's: order slots are reused by later orders.
464 [[nodiscard]] static bool owns(const Oms& oms,
465 const Instrument& inst,
466 Side side,
467 std::uint32_t lvl,
468 const Slot& slot) noexcept {
469 if (!slot.handle.valid() || !oms.is_live(slot.handle)) return false;
470 const Order& o = oms.get(slot.handle);
471 return o.instrument == inst.id && o.user_tag == make_tag(side, lvl);
472 }
473
474 template <class Placer>
475 std::uint32_t cancel(
476 const Instrument& inst, Side side, std::uint32_t lvl, Slot& slot, Placer& place) noexcept {
477 QuoteAction a{QuoteActionKind::Cancel,
478 inst.id,
479 side,
480 lvl,
481 slot.handle,
483 Price{},
484 Qty{},
485 false,
486 false,
487 false,
488 false};
489 if (!place(a)) {
490 ++stats_.rejected;
491 return 0;
492 }
493 ++stats_.cancels;
494 slot.awaiting_terminal = true;
495 return 1;
496 }
497 template <class Placer>
498 std::uint32_t submit_new(const Instrument& inst,
499 Side side,
500 std::uint32_t lvl,
501 Slot& slot,
502 Level target,
503 VenueId account,
504 Timestamp now,
505 Placer& place,
506 bool deferred = false) noexcept {
507 QuoteAction a{QuoteActionKind::New,
508 inst.id,
509 side,
510 lvl,
511 Handle<Order>{},
513 target.price,
514 target.qty,
515 params_.post_only,
516 deferred,
517 false,
518 false,
519 account};
520 if (!place(a) || !a.handle.valid()) {
521 if (a.withheld) {
522 ++stats_.kept_balance;
523 } else {
524 held_back(state_[inst.id.value], inst.id, a);
525 }
526 return 0;
527 }
528 ++stats_.news;
529 slot.handle = a.handle;
530 slot.cl_ord_id = a.cl_ord_id;
531 slot.last_requote = now;
532 slot.awaiting_terminal = false;
533 return 1;
534 }
535
536 // A failed New or Replace: a rate-limited one waits in the starved queue, anything else was
537 // refused.
538 void held_back(InstState& st, InstrumentId id, const QuoteAction& a) noexcept {
539 if (!a.rate_limited) {
540 ++stats_.rejected;
541 return;
542 }
543 ++stats_.kept_rate_limit;
544 if (st.starved) return;
545 st.starved = true;
546 static_cast<void>(starved_.try_push(static_cast<std::uint16_t>(id.value)));
547 }
548
549 void resolve_replace() noexcept {
550 for (InstState& st : state_) st.replace = params_.supports_replace && !st.venue_no_replace;
551 }
552
553 QuoteParams params_;
554 QuoteStats stats_{};
555 InstState state_[kMaxInstruments] = {};
556 RingBuffer<std::uint16_t, kMaxInstruments> starved_; // each instrument at most once
557};
558
559} // namespace fastmm
Definition oms.hpp:137
const Order & get(Handle< Order > h) const noexcept
Definition oms.hpp:582
Definition quote_manager.hpp:145
const QuoteParams & params() const noexcept
Definition quote_manager.hpp:149
bool starved() const noexcept
Instruments whose quotes were held back for want of an order token, oldest first.
Definition quote_manager.hpp:249
std::uint32_t reconcile(const Instrument &inst, const DesiredQuotes &desired, const Oms &oms, Timestamp now, Placer &&place) noexcept
Reconciles desired vs resting for one instrument. Returns the number of actions placed.
Definition quote_manager.hpp:173
std::size_t starved_count() const noexcept
Definition quote_manager.hpp:250
static constexpr std::uint32_t make_tag(Side s, std::uint32_t level) noexcept
user_tag encoding so a terminal OmsUpdate can be mapped back to its slot.
Definition quote_manager.hpp:164
const QuoteStats & stats() const noexcept
Definition quote_manager.hpp:154
bool resumable(InstrumentId id) const noexcept
Definition quote_manager.hpp:244
bool backing_off(InstrumentId id, Side s, Timestamp now) const noexcept
The side is in its venue-reject backoff at now.
Definition quote_manager.hpp:338
std::uint32_t retry(const Instrument &inst, const Oms &oms, Timestamp now, Placer &&place) noexcept
Re-applies the last desired quotes of an instrument popped from the starved queue; one that was pulle...
Definition quote_manager.hpp:261
static constexpr bool is_quote_tag(std::uint32_t tag) noexcept
Definition quote_manager.hpp:167
const DesiredQuotes & desired(InstrumentId id) const noexcept
The last ladder reconcile() applied to the instrument (empty after a pull).
Definition quote_manager.hpp:332
std::uint32_t resume(const Instrument &inst, const Oms &oms, Timestamp now, Placer &&place) noexcept
Re-applies the desired quotes kept by pull_quotes(keep_desired). Returns the actions placed.
Definition quote_manager.hpp:235
void set_replace(InstrumentId id, bool on) noexcept
Whether the instrument's venue amends in place (default true; supports_replace still applies).
Definition quote_manager.hpp:156
bool replaces(InstrumentId id) const noexcept
Definition quote_manager.hpp:161
void set_params(const QuoteParams &p) noexcept
Definition quote_manager.hpp:150
Handle< Order > slot_handle(InstrumentId id, Side s, std::uint32_t level) const noexcept
Definition quote_manager.hpp:325
bool pulled(InstrumentId id) const noexcept
Definition quote_manager.hpp:330
void on_order_update(const OmsUpdate &u, const Instrument &inst, const Oms &oms, Timestamp now, Placer &&place) noexcept
Feed every OmsUpdate here.
Definition quote_manager.hpp:277
InstrumentId pop_starved() noexcept
Takes the oldest starved instrument off the queue (the caller checks starved() first).
Definition quote_manager.hpp:252
std::uint32_t pull_quotes(const Instrument &inst, const Oms &oms, Placer &&place, bool keep_desired=false) noexcept
Cancels every working quote on the instrument and suppresses re-quoting until the next reconcile().
Definition quote_manager.hpp:207
Instrument: the single definition of a tradable contract (5.3 / 5.15).
Event bus message structs (5.5).
Definition strategy.hpp:16
StrongId< struct ClientOrderTag, std::uint64_t, 0 > ClientOrderId
48-bit: session_epoch(16) << 32 | seq(32). 0 is never issued.
Definition strong_id.hpp:41
constexpr Duration milliseconds(std::int64_t v) noexcept
Definition time.hpp:38
constexpr bool is_pending(OrderState s) noexcept
Definition enums.hpp:93
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
Fixed< struct QtyTag > Qty
Definition fixed_point.hpp:333
StrongId< struct InstrumentTag, std::uint32_t > InstrumentId
Dense index into the engine's instrument table.
Definition strong_id.hpp:38
Fixed< struct PriceTag > Price
Definition fixed_point.hpp:332
constexpr std::size_t kMaxInstruments
Definition instrument.hpp:26
QuoteActionKind
Definition quote_manager.hpp:108
constexpr std::size_t kMaxQuoteLevels
Definition quote_manager.hpp:54
StrongId< struct VenueTag, std::uint8_t > VenueId
Definition strong_id.hpp:39
Order management (5.8): a Pool<Order> + OpenHashMap<ClientOrderId, Handle> + a ring of recently termi...
Definition quote_manager.hpp:56
StaticVector< Level, kMaxQuoteLevels > asks
Definition quote_manager.hpp:58
std::array< VenueId, kMaxQuoteLevels > ask_account
Definition quote_manager.hpp:62
void clear() noexcept
Definition quote_manager.hpp:63
const StaticVector< Level, kMaxQuoteLevels > & side(Side s) const noexcept
Definition quote_manager.hpp:72
VenueId account(Side s, std::uint32_t lvl) const noexcept
Definition quote_manager.hpp:69
bool bid(Price px, Qty qty) noexcept
Append the next level of a side (index 0 first).
Definition quote_manager.hpp:78
StaticVector< Level, kMaxQuoteLevels > bids
index 0 = closest to mid
Definition quote_manager.hpp:57
bool empty() const noexcept
Definition quote_manager.hpp:75
std::array< VenueId, kMaxQuoteLevels > bid_account
The pool account a level's order goes from (NewOrderRequest::account); invalid = the engine's automat...
Definition quote_manager.hpp:61
bool ask(Price px, Qty qty) noexcept
Definition quote_manager.hpp:81
void uncross(Price tick) noexcept
Never cross ourselves at level 0: an ask at or below the best bid moves one tick above it.
Definition quote_manager.hpp:85
Definition time.hpp:22
std::int64_t ns
Definition time.hpp:23
Definition strong_id.hpp:54
Definition instrument.hpp:29
InstrumentId id
4
Definition instrument.hpp:42
One price level. Shared by book messages and the L2 book.
Definition messages.hpp:30
Definition oms.hpp:74
Definition order.hpp:15
constexpr bool is_working() const noexcept
Definition order.hpp:49
Definition quote_manager.hpp:110
bool post_only
Definition quote_manager.hpp:119
bool withheld
New output: not sent, the balance does not cover it (deferred only)
Definition quote_manager.hpp:123
VenueId account
New: the pool account to send it from (invalid = automatic)
Definition quote_manager.hpp:127
bool rate_limited
New / Replace output: not sent, no order token for it now (QuoteParams::token_reserve); the instrumen...
Definition quote_manager.hpp:126
bool deferred
New: the target was recorded while an earlier order held the slot and is placed now that it ended (on...
Definition quote_manager.hpp:122
InstrumentId instrument
Definition quote_manager.hpp:112
Price price
Definition quote_manager.hpp:117
ClientOrderId cl_ord_id
New output (the placer fills it)
Definition quote_manager.hpp:116
Side side
Definition quote_manager.hpp:113
std::uint32_t level
Definition quote_manager.hpp:114
Qty qty
Definition quote_manager.hpp:118
QuoteActionKind kind
Definition quote_manager.hpp:111
Handle< Order > handle
Cancel/Replace input; New output.
Definition quote_manager.hpp:115
Definition quote_manager.hpp:91
Duration min_requote_interval
Definition quote_manager.hpp:93
Duration reject_backoff
0 = no backoff after venue rejects
Definition quote_manager.hpp:100
std::int64_t min_qty_bps
leaves >= 80 % of desired -> qty ok
Definition quote_manager.hpp:94
Duration reject_backoff_max
Definition quote_manager.hpp:101
std::int64_t min_requote_ticks
|desired - resting| < this -> keep
Definition quote_manager.hpp:92
std::int64_t token_reserve
Order tokens ([risk] orders_per_sec) a quote that does not reduce the position leaves for the orders ...
Definition quote_manager.hpp:105
bool replace_all_venues
Replace only if every instrument's venue can (the engine before replace was per venue; set to replay ...
Definition quote_manager.hpp:98
bool post_only
Definition quote_manager.hpp:99
bool supports_replace
off: never replace; on: where the instrument's venue can
Definition quote_manager.hpp:95
Definition quote_manager.hpp:130
std::uint64_t rejected
Definition quote_manager.hpp:137
std::uint64_t kept_hysteresis
Definition quote_manager.hpp:134
std::uint64_t cancels
Definition quote_manager.hpp:132
std::uint64_t kept_interval
Definition quote_manager.hpp:135
std::uint64_t reject_backoffs
venue rejects that started or extended a side backoff
Definition quote_manager.hpp:138
std::uint64_t retries
retry() calls that re-applied a starved instrument's quotes
Definition quote_manager.hpp:142
std::uint64_t kept_backoff
News withheld because their side was backing off.
Definition quote_manager.hpp:139
std::uint64_t news
Definition quote_manager.hpp:131
std::uint64_t skipped_pending
Definition quote_manager.hpp:136
std::uint64_t kept_balance
deferred News withheld: the balance no longer covers them
Definition quote_manager.hpp:140
std::uint64_t replaces
Definition quote_manager.hpp:133
std::uint64_t kept_rate_limit
News / Replaces held back for want of an order token.
Definition quote_manager.hpp:141
Rep value
Definition strong_id.hpp:26
Definition time.hpp:45
Time primitives.