33#include "fastmm/core/config_macros.hpp"
34#include "fastmm/core/containers/open_hash_map.hpp"
35#include "fastmm/core/containers/pool.hpp"
36#include "fastmm/core/containers/recent_map.hpp"
37#include "fastmm/core/containers/ring_buffer.hpp"
38#include "fastmm/core/containers/static_vector.hpp"
59inline constexpr std::uint64_t
kMaxSeq = 0xFFFF'FFFFULL;
109 std::uint32_t
open = 0;
141 for (
auto& per_inst : best_own_) per_inst[0] = per_inst[1] =
Price{};
142 for (
auto& per_inst : open_qty_) per_inst[0] = per_inst[1] =
Qty{};
147 [[nodiscard]] std::uint16_t
session_epoch() const noexcept {
return epoch_; }
152 if (FASTMM_UNLIKELY(seq_ >= max_seq_))
return ClientOrderId{};
156 [[nodiscard]] std::uint64_t
ids_left() const noexcept {
return max_seq_ - seq_; }
157 [[nodiscard]]
const OmsStats&
stats() const noexcept {
return stats_; }
160 return open_per_inst_[
id.value];
164 return open_qty_[
id.value][
static_cast<std::size_t
>(s)];
168 return best_own_[
id.value][
static_cast<std::size_t
>(s)];
181 if (FASTMM_UNLIKELY(by_id_.insert(
id, h).first ==
nullptr)) {
185 live_pos_[h.
idx] =
static_cast<std::uint32_t
>(live_.size());
186 static_cast<void>(live_.push_back(h.
idx));
187 Order& o = pool_.get(h);
214 Order& o = pool_.get(h);
224 Order& o = pool_.get(h);
235 Order& o = pool_.get(h);
245 times_[h.
idx].replace_sent = now;
267 Order& o = pool_.get(h);
275 times_[h.
idx].t.venue_ack = m.hdr.exch_ts;
276 times_[h.
idx].t.local_ack = m.hdr.recv_ts;
290 apply_replace(o, rekey, new_venue_order);
291 if (o.
cl_ord_id != old_id) u.replaced_cl_ord_id = old_id;
294 TimesSlot& t = times_[h.
idx];
295 t.t =
OrderTimes{t.replace_sent, m.hdr.exch_ts, m.hdr.recv_ts};
303 if (m.cl_ord_id == o.
cl_ord_id && !times_[h.
idx].t.venue_ack.valid())
304 times_[h.
idx].t.venue_ack = m.hdr.exch_ts;
307 finish_update(u, h, o);
318 Order& o = pool_.get(h);
337 finish_update(u, h, o);
341 finish_update(u, h, o);
352 Order& o = pool_.get(h);
358 finish_update(u, h, o);
382 Order& o = pool_.get(h);
386 finish_update(u, h, o);
398 finish_update(u, h, o);
406 if (!m.exec_id.empty() && !remember_exec(m)) {
415 if (FASTMM_UNLIKELY(replayed)) take_synthetic(m.cl_ord_id, m.qty, u);
424 if (FASTMM_UNLIKELY(!replayed && !synthetic_.empty()) && u.known) {
425 if (m.cum_qty.is_positive()) {
426 const Qty booked = h.
valid() ? pool_.get(h).cum_qty : u.order.cum_qty;
427 Qty covered = booked - (m.cum_qty - m.qty);
428 if (covered > m.qty) covered = m.qty;
429 if (covered.
is_positive()) take_synthetic(m.cl_ord_id, covered, u);
430 }
else if (!h.
valid()) {
431 take_synthetic(m.cl_ord_id, m.qty, u);
446 Order& o = pool_.get(h);
451 const Qty fresh = m.qty - u.corrected_qty;
453 if (cum < before) cum = before;
454 if (cum > o.
qty) cum = o.
qty;
466 finish_update(u, h, o);
477 Order& o = pool_.get(h);
496 std::optional<ClientOrderId> sent_watermark = std::nullopt)
noexcept {
497 ReconcileScope& sc = recon_scope_[venue.value];
498 sc.bounded = sent_watermark.has_value();
513 Order& o = pool_.get(h);
525 finish_update(u, h, o);
534 const ReconcileScope& sc = recon_scope_[venue.value];
539 static_cast<void>(unseen.push_back(h));
542 if (!pool_.is_live(h))
continue;
543 Order& o = pool_.get(h);
568 if (timeout.ns <= 0)
return 0;
572 if (now - o.
created < timeout)
return;
573 static_cast<void>(stale.push_back(h));
576 if (!pool_.is_live(h))
continue;
596 if (recently_terminal_.find_if([&](
const TerminalRecord& r) { return r.cl_ord_id == id; }) !=
609 pool_.for_each([&](Handle<Order> h,
const Order& o) {
610 if (o.instrument ==
id) f(h, o);
613 void warm_up() noexcept { pool_.warm_up(); }
616 struct TerminalRecord {
627 struct ReconcileScope {
629 bool bounded =
false;
633 struct SyntheticFill {
640 Timestamp replace_sent;
643 [[nodiscard]]
static bool in_scope(
const Order& o,
VenueId venue)
noexcept {
644 return !venue.valid() || o.venue == venue;
651 void absorb_cum(Order& o,
Qty reported, OmsUpdate& u)
noexcept {
653 if (cum > o.qty) cum = o.qty;
654 if (cum <= o.cum_qty)
return;
655 const Qty diff = cum - o.cum_qty;
656 open_qty_[o.instrument.value][
static_cast<std::size_t
>(o.side)] -= diff;
658 u.missed_qty += diff;
661 note_synthetic(o.cl_ord_id, diff, o.price);
670 for (SyntheticFill& f : synthetic_) {
671 if (f.cl_ord_id ==
id) {
677 if (synthetic_.full()) {
679 synthetic_.erase_front();
681 static_cast<void>(synthetic_.push_back(SyntheticFill{id, qty, px}));
686 if (FASTMM_LIKELY(synthetic_.empty()))
return;
687 for (std::size_t i = 0; i < synthetic_.size(); ++i) {
688 SyntheticFill& f = synthetic_[i];
689 if (f.cl_ord_id !=
id)
continue;
690 u.corrected_qty = qty < f.qty ? qty : f.qty;
691 u.synthetic_px = f.price;
692 f.qty -= u.corrected_qty;
694 if (!f.qty.is_positive()) synthetic_.erase_at(i);
703 void late_fill(
const OrderFillMsg& m, OmsUpdate& u)
noexcept {
704 TerminalRecord* r = recently_terminal_.find_if(
705 [&](
const TerminalRecord& t) {
return t.cl_ord_id == m.cl_ord_id; });
706 if (r ==
nullptr)
return;
708 const Qty fresh = m.qty - u.corrected_qty;
709 Qty cum = m.cum_qty.
is_positive() ? m.cum_qty : r->cum_qty + fresh;
710 if (cum > r->qty) cum = r->qty;
711 if (cum <= r->cum_qty)
return;
717 u.order.cum_qty = r->cum_qty;
718 u.order.state = r->state;
723 Handle<Order> lookup(
ClientOrderId id, OmsUpdate& u)
noexcept {
724 const Handle<Order>* p = by_id_.find(
id);
730 const TerminalRecord* r =
731 recently_terminal_.find_if([&](
const TerminalRecord& t) {
return t.cl_ord_id == id; });
735 u.order.cl_ord_id = r->cl_ord_id;
736 u.order.instrument = r->instrument;
737 u.order.venue = r->venue;
738 u.order.side = r->side;
739 u.order.state = r->state;
740 u.order.price = r->price;
741 u.order.qty = r->qty;
742 u.order.cum_qty = r->cum_qty;
745 return Handle<Order>{};
748 void finish_update(OmsUpdate& u, Handle<Order> h,
const Order& o)
noexcept {
751 u.times = times_[h.idx].t;
758 void apply_replace(Order& o,
bool rekey,
bool new_venue_order)
noexcept {
759 const std::size_t s =
static_cast<std::size_t
>(o.side);
760 Qty& open = open_qty_[o.instrument.value][s];
761 open -= o.leaves_qty();
763 by_id_.erase(o.cl_ord_id);
764 o.cl_ord_id = o.pending_cl_ord_id;
766 if (new_venue_order) o.cum_qty =
Qty{};
767 const Price old_px = o.price;
768 o.price = o.pending_price;
769 o.qty = o.pending_qty;
770 if (o.cum_qty > o.qty) o.cum_qty = o.qty;
771 open += o.leaves_qty();
773 o.pending_price =
Price{};
774 o.pending_qty =
Qty{};
775 o.flags &=
static_cast<std::uint8_t
>(~Order::kReplaceOldCanceled);
777 Price& best = best_own_[o.instrument.value][s];
778 if (best.is_zero() ||
better(o.side, o.price, best)) {
780 }
else if (old_px == best) {
781 recompute_best_own(o.instrument, o.side);
784 void clear_pending(Order& o)
noexcept {
785 if (o.pending_cl_ord_id.valid() && o.pending_cl_ord_id != o.cl_ord_id)
786 by_id_.erase(o.pending_cl_ord_id);
788 o.pending_price =
Price{};
789 o.pending_qty =
Qty{};
790 o.flags &=
static_cast<std::uint8_t
>(~Order::kReplaceOldCanceled);
793 void terminate(OmsUpdate& u,
797 bool by_reconcile =
false) noexcept {
798 o.state = final_state;
801 --open_per_inst_[o.instrument.value];
802 const std::size_t s =
static_cast<std::size_t
>(o.side);
803 open_qty_[o.instrument.value][s] -= o.leaves_qty();
804 by_id_.erase(o.cl_ord_id);
805 if (o.pending_cl_ord_id.valid() && o.pending_cl_ord_id != o.cl_ord_id)
806 by_id_.erase(o.pending_cl_ord_id);
807 recently_terminal_.push(TerminalRecord{o.cl_ord_id,
817 u.handle = Handle<Order>{};
818 u.times = times_[h.idx].t;
823 const Side side = o.side;
824 const Price px = o.price;
826 const std::uint32_t pos = live_pos_[h.idx];
827 const std::uint32_t last = live_.back();
829 live_pos_[last] = pos;
832 if (px == best_own_[inst.value][s]) recompute_best_own(inst, side);
838 for (
const std::uint32_t idx : live_) {
839 const Order& o = pool_.get(Handle<Order>{idx});
840 if (o.instrument != inst || o.side != side)
continue;
841 if (best.is_zero() ||
better(side, o.price, best)) best = o.price;
843 best_own_[inst.value][
static_cast<std::size_t
>(side)] = best;
850 bool remember_exec(
const OrderFillMsg& m)
noexcept {
851 const std::uint64_t key = m.exec_id.hash() ^
852 (
static_cast<std::uint64_t
>(m.hdr.instrument.value) << 1U) ^
853 (
static_cast<std::uint64_t
>(m.side) * 0x9E3779B97F4A7C15ULL);
854 return exec_seen_.assign(key, 1);
857 std::uint16_t epoch_;
858 std::uint64_t max_seq_;
859 std::uint64_t seq_ = 0;
861 Pool<Order, kMaxOpenOrders> pool_;
865 StaticVector<std::uint32_t, kMaxOpenOrders> live_;
867 OpenHashMap<ClientOrderId, Handle<Order>,
kMaxOpenOrders * 4> by_id_;
868 RingBuffer<TerminalRecord, kRecentlyTerminal> recently_terminal_;
869 RecentMap<std::uint64_t, std::uint8_t, kRecentlyTerminal> exec_seen_;
873 ReconcileScope recon_scope_[std::numeric_limits<VenueId::rep_type>::max() + 1U] = {};
874 StaticVector<SyntheticFill, kMaxSyntheticFills> synthetic_;
876 std::unique_ptr<TimesSlot[]> times_ = std::make_unique<TimesSlot[]>(
kMaxOpenOrders);
BookView: the read interface every book implementation (L2Book, L3Book::to_l2 view,...
void reconcile_begin(VenueId venue=VenueId::invalid(), std::optional< ClientOrderId > sent_watermark=std::nullopt) noexcept
Starts reconciling venue's orders (an invalid venue: every venue).
Definition oms.hpp:494
Result< void, RejectReason > request_cancel_unacked(Handle< Order > h) noexcept
Cancel of an order that is still waiting for its ack (the ack-timeout sweep only).
Definition oms.hpp:223
Result< void, RejectReason > request_cancel(Handle< Order > h) noexcept
Definition oms.hpp:213
const OrderTimes & times(Handle< Order > h) const noexcept
Send and ack times of an open order.
Definition oms.hpp:586
std::size_t sweep_pending(Timestamp now, Duration timeout, F &&f) noexcept
Orders still PendingNew timeout after they were submitted: the request or its ack was lost,...
Definition oms.hpp:565
OrderClass classify(ClientOrderId id) const noexcept
Definition oms.hpp:591
std::uint64_t ids_left() const noexcept
Client order ids left in this session.
Definition oms.hpp:158
Result< Handle< Order >, RejectReason > submit(const NewOrderRequest &req, ClientOrderId id, Timestamp now) noexcept
Definition oms.hpp:174
Result< void, RejectReason > request_replace(Handle< Order > h, ClientOrderId new_id, Price px, Qty qty, Timestamp now={}) noexcept
new_id may equal the current id for venues that amend in place.
Definition oms.hpp:233
OmsUpdate on_ack(const OrderAckMsg &m) noexcept
Definition oms.hpp:251
Price best_own_px(InstrumentId id, Side s) const noexcept
Best price among our own open orders on (instrument, side); Price{} if none. For STP.
Definition oms.hpp:169
void reconcile_end(F &&f, VenueId venue=VenueId::invalid()) noexcept
Every open order of venue (invalid: every venue) the snapshot did not report is marked Canceled,...
Definition oms.hpp:532
Qty open_qty(InstrumentId id, Side s) const noexcept
Sum of leaves of open orders on (instrument, side): "in-flight same-side exposure".
Definition oms.hpp:165
OmsUpdate on_expired(const OrderExpiredMsg &m) noexcept
Definition oms.hpp:470
OmsUpdate on_reject(const OrderRejectMsg &m) noexcept
Definition oms.hpp:311
Oms & operator=(const Oms &)=delete
OmsUpdate on_fill(const OrderFillMsg &m) noexcept
Definition oms.hpp:402
bool is_live(Handle< Order > h) const noexcept
Definition oms.hpp:584
std::uint16_t session_epoch() const noexcept
Definition oms.hpp:149
ClientOrderId next_cl_ord_id() noexcept
An invalid id once the session's 32-bit sequence is used up: the wire form carries 48 bits (epoch 16 ...
Definition oms.hpp:153
OmsUpdate on_cancel_ack(const OrderCancelAckMsg &m) noexcept
Definition oms.hpp:345
void warm_up() noexcept
Definition oms.hpp:610
OmsUpdate on_cancel_reject(const OrderCancelRejectMsg &m) noexcept
Definition oms.hpp:375
OmsUpdate reconcile_open_order(const ReconcileMsg &m) noexcept
Venue reports an open order. Unknown -> CancelUnknown (engine cancels by venue id).
Definition oms.hpp:504
Handle< Order > find(ClientOrderId id) const noexcept
Definition oms.hpp:587
const OmsStats & stats() const noexcept
Definition oms.hpp:159
void for_each_open_order(F &&f) const noexcept
Ascending handle order (deterministic). F(Handle<Order>, const Order&).
Definition oms.hpp:601
std::uint32_t open_count() const noexcept
Definition oms.hpp:160
const Order & get(Handle< Order > h) const noexcept
Definition oms.hpp:582
Minimal expected<T, E>: the hot path never throws, it returns Result.
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 std::size_t kMaxSyntheticFills
Fills booked from a cumulative quantity and never named by an execution.
Definition oms.hpp:59
OrderState
Definition enums.hpp:79
constexpr ClientOrderId make_cl_ord_id(std::uint16_t epoch, std::uint32_t seq) noexcept
Definition strong_id.hpp:103
RejectReason
Ordered exactly like RiskEngine::check_new evaluates them (5.7), followed by OMS / transport / venue ...
Definition enums.hpp:123
@ PoolExhausted
OMS / transport.
OrderClass
Definition oms.hpp:135
Side
Definition enums.hpp:12
Fixed< struct QtyTag > Qty
Definition fixed_point.hpp:333
constexpr std::size_t kMaxOpenOrders
Definition oms.hpp:54
constexpr Unexpected< std::decay_t< E > > fail(E &&e) noexcept
Definition result.hpp:25
constexpr std::uint64_t kMaxSeq
Client order ids per session: the wire form is 48 bits, epoch(16) << 32 | seq(32).
Definition oms.hpp:61
StrongId< struct InstrumentTag, std::uint32_t > InstrumentId
Dense index into the engine's instrument table.
Definition strong_id.hpp:38
constexpr std::size_t kRecentlyTerminal
Definition oms.hpp:55
Fixed< struct PriceTag > Price
Definition fixed_point.hpp:332
constexpr std::size_t kMaxInstruments
Definition instrument.hpp:26
constexpr bool better(Side s, Price p, Price q) noexcept
Definition book_view.hpp:48
OmsAction
Definition oms.hpp:63
@ LateFill
fill for a recently terminal order: position, and the terminal record's cum_qty and state (changed = ...
@ CancelUnknown
ack/fill for an id we do not know: engine must cancel it
@ UnknownFill
fill for a completely unknown id: update position, alert
@ Ignored
message did not apply (duplicate ack, late cancel-reject, ...)
@ ReconcileNeeded
too many cancel rejects
@ Duplicate
duplicate exec_id
StrongId< struct VenueTag, std::uint8_t > VenueId
Definition strong_id.hpp:39
Order: the OMS's 128-byte record for one order across its lifetime.
constexpr bool is_zero() const noexcept
Definition fixed_point.hpp:301
constexpr bool is_positive() const noexcept
Definition fixed_point.hpp:302
Definition strong_id.hpp:54
constexpr bool valid() const noexcept
Definition strong_id.hpp:58
std::uint32_t idx
Definition strong_id.hpp:55
std::uint64_t late_fills
Definition oms.hpp:121
std::uint64_t corrected_fills
Missed fills a replayed execution later named, so their price and fee stopped being estimates.
Definition oms.hpp:125
std::uint32_t open
Definition oms.hpp:111
std::uint64_t duplicates
Definition oms.hpp:120
std::uint64_t synthetic_forgotten
Missed fills whose entry was evicted before an execution named them: their price and fee stay estimat...
Definition oms.hpp:128
std::uint64_t reconcile_unresolved
Orders a reconciliation snapshot did not report that still had working quantity: the venue ended them...
Definition oms.hpp:132
std::uint64_t unsolicited_cancels
Definition oms.hpp:122
std::uint64_t unknown_ids
Definition oms.hpp:119
std::uint64_t filled
Definition oms.hpp:116
std::uint64_t missed_fills
cum_qty jumps a fill message never reported
Definition oms.hpp:123
std::uint64_t rejected
Definition oms.hpp:114
std::uint64_t canceled
Definition oms.hpp:115
std::uint64_t fills
Definition oms.hpp:117
std::uint64_t expired
Definition oms.hpp:118
std::uint64_t ack_timeouts
PendingNew orders swept by sweep_pending()
Definition oms.hpp:129
std::uint64_t submitted
Definition oms.hpp:112
std::uint64_t acked
Definition oms.hpp:113
OrderTimes times
The order's times (Oms::times), and the pool slot it had: set on every update of an open order,...
Definition oms.hpp:106
Price fill_px
Definition oms.hpp:85
bool known
the id mapped to an order (open or recently terminal)
Definition oms.hpp:81
Price synthetic_px
Definition oms.hpp:103
Qty corrected_qty
Part of a replayed execution (OrderFillMsg::kReplayed) whose quantity a synthetic fill already put in...
Definition oms.hpp:102
Qty unresolved_qty
Quantity that was still working when a reconciliation snapshot failed to mention the order.
Definition oms.hpp:97
Qty fill_qty
for fills
Definition oms.hpp:84
Handle< Order > slot
Definition oms.hpp:107
Handle< Order > handle
invalid once terminal (slot freed) or unknown
Definition oms.hpp:78
Order order
snapshot after the transition; for a recently terminal id only the terminal record's fields (id,...
Definition oms.hpp:75
bool terminal
the order reached a terminal state in this update
Definition oms.hpp:83
OrderState prev
Definition oms.hpp:79
bool changed
state or quantities changed
Definition oms.hpp:82
ClientOrderId replaced_cl_ord_id
A cancel-replace to a new id completed: the order carries on under order.cl_ord_id and this one is go...
Definition oms.hpp:93
Qty missed_qty
Quantity the venue reports as filled that we never saw a fill message for (a private-stream outage co...
Definition oms.hpp:89
OmsAction action
Definition oms.hpp:80
Definition messages.hpp:188
static constexpr std::uint8_t kAmendedInPlace
The venue amended the resting order in place (Binance Spot order.amend.keepPriority,...
Definition messages.hpp:192
Definition messages.hpp:213
Definition messages.hpp:222
Definition messages.hpp:261
Definition messages.hpp:233
static constexpr std::uint8_t kReplayed
The execution comes from the venue's trade history (Venue::request_executions), not from the private ...
Definition messages.hpp:237
Definition messages.hpp:202
When the order's working leg (the order, or its latest replacement once acknowledged) was sent and ac...
Definition order.hpp:58
OrderState state
1
Definition order.hpp:34
TimeInForce tif
1 -> 64
Definition order.hpp:33
VenueOrderId venue_order_id
41 -> 49
Definition order.hpp:27
ClientOrderId cl_ord_id
8
Definition order.hpp:26
Qty qty
8 -> 88
Definition order.hpp:40
constexpr Qty leaves_qty() const noexcept
Definition order.hpp:47
std::uint8_t flags
1
Definition order.hpp:36
Price pending_price
8 -> 120
Definition order.hpp:44
constexpr bool has(Flags f) const noexcept
Definition order.hpp:52
std::uint8_t cancel_attempts
1
Definition order.hpp:37
ClientOrderId pending_cl_ord_id
8 -> 112 replace: id of the replacement order
Definition order.hpp:43
Timestamp created
8 -> 104
Definition order.hpp:42
VenueId venue
1
Definition order.hpp:30
constexpr bool is_working() const noexcept
Definition order.hpp:49
Qty pending_qty
8 -> 128
Definition order.hpp:45
InstrumentId instrument
4 -> 60
Definition order.hpp:29
Side side
1
Definition order.hpp:31
std::uint32_t user_tag
4 -> 72 (QuoteManager slot id etc.)
Definition order.hpp:38
RejectReason reject_reason
1
Definition order.hpp:35
Qty cum_qty
8 -> 96
Definition order.hpp:41
OrderType type
1
Definition order.hpp:32
Price price
8 -> 80
Definition order.hpp:39
@ kReplaceOldCanceled
cancel-then-new replace: old leg already cancelled
Definition order.hpp:23
@ kReconciled
state fixed up by reconciliation
Definition order.hpp:20
@ kReduceOnly
Definition order.hpp:18
@ kPostOnly
Definition order.hpp:17
@ kSeenInReconcile
scratch flag during a reconcile pass
Definition order.hpp:21
@ kUnsolicitedCancel
venue cancelled it (not requested by us)
Definition order.hpp:19
Control thread injects the venue's view after a reconnect (5.8).
Definition messages.hpp:469
static constexpr StrongId invalid() noexcept
Definition strong_id.hpp:30
Rep value
Definition strong_id.hpp:26
constexpr bool valid() const noexcept
Definition time.hpp:55