FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
quoting.hpp
Go to the documentation of this file.
1#pragma once
2// Quoting helpers for strategies (ADR-0012): integer-only, noexcept, no allocation.
3//
4// mid(bid, ask) (bid + ask) / 2, truncated like the books' mid()
5// microprice(bid_level, ask_level) size-weighted mid of the touch
6// spread_ratio(bid, ask) (ask - bid) / mid as a Ratio
7// away_from(ref, side, dist) a bid dist below ref, an ask dist above
8// inventory_allows(side, pos, qty, lim) may this side add qty without passing the limit
9// keep_passive(q, best_bid, best_ask, tick)
10// shift a ladder so level 0 is one tick inside the touch
11// fit_side(levels, room, inst) cut a side to a total quantity
12// fit_to_balance(ctx, id, inst, q) cut both sides to what the account's balance covers
13//
14// Together with Instrument::ticks(n), DesiredQuotes::bid/ask/uncross and the Ratio operators and
15// literals in core/fixed_point.hpp.
16#include "fastmm/core/config_macros.hpp"
17#include "fastmm/core/enums.hpp"
22
23#include <cstdint>
24
25namespace fastmm {
26
27[[nodiscard]] constexpr Price mid(Price bid, Price ask) noexcept {
28 return Price::from_raw((bid.raw + ask.raw) / 2);
30
31// Size-weighted microprice of the touch, truncated toward zero. With no size on either side it is
32// the plain mid.
33[[nodiscard]] constexpr Price microprice(Level bid, Level ask) noexcept {
34 const Int128 w = static_cast<Int128>(bid.qty.raw) + ask.qty.raw;
35 if (w <= 0) return mid(bid.price, ask.price);
36 const Int128 num = static_cast<Int128>(bid.price.raw) * ask.qty.raw +
37 static_cast<Int128>(ask.price.raw) * bid.qty.raw;
38 return Price::from_raw(static_cast<std::int64_t>(num / w));
39}
40
41// (ask - bid) / mid; zero when the mid is not positive. 1 bp == Ratio raw 10'000.
42[[nodiscard]] constexpr Ratio spread_ratio(Price bid, Price ask) noexcept {
43 const Price m = mid(bid, ask);
44 if (!m.is_positive()) return Ratio{};
45 return ratio(ask - bid, m);
46}
47
48// A price `dist` away from `ref` on the passive side: Buy -> ref - dist, Sell -> ref + dist.
49[[nodiscard]] constexpr Price away_from(Price ref, Side side, Price dist) noexcept {
50 return side == Side::Buy ? ref - dist : ref + dist;
52
53// Whether a fill of `qty` on `side` keeps the position within +-limit: Buy needs
54// position + qty <= limit, Sell needs position - qty >= -limit. A zero limit means no cap. A side
55// that reduces the position is allowed as long as the result stays within the limit.
56[[nodiscard]] constexpr bool inventory_allows(Side side,
57 Qty position,
58 Qty qty,
59 Qty limit) noexcept {
60 if (limit.is_zero()) return true;
61 return side == Side::Buy ? position + qty <= limit : position - qty >= -limit;
62}
63
64// Post-only quotes must not cross the market. Inventory skew can push the unwinding side through
65// the touch (a skewed bid above the best ask); the venue would reject it and that side would stop
66// quoting. Shift the whole ladder back so level 0 sits one tick inside the touch, keeping the
67// spacing between levels; bid levels pushed to a non-positive price are removed. An empty opposite
68// side (price 0) imposes no limit.
69inline void keep_passive(DesiredQuotes& q, Price best_bid, Price best_ask, Price tick) noexcept {
70 if (!q.bids.empty() && best_ask.is_positive()) {
71 const Price limit = best_ask - tick;
72 if (q.bids[0].price > limit) {
73 const Price shift = q.bids[0].price - limit;
74 for (auto& l : q.bids) l.price = l.price - shift;
75 while (!q.bids.empty() && !q.bids[q.bids.size() - 1].price.is_positive()) q.bids.pop_back();
76 }
77 }
78 if (!q.asks.empty() && best_bid.is_positive()) {
79 const Price limit = best_bid + tick;
80 if (q.asks[0].price < limit) {
81 const Price shift = limit - q.asks[0].price;
82 for (auto& l : q.asks) l.price = l.price + shift;
83 }
84 }
85}
86
87// Cuts one side of a ladder to `room` in total, keeping the levels closest to the mid: each level
88// is rounded down to the lot, and one left under the lot, the instrument's min_qty or its
89// min_notional ends the side (the venue would refuse it).
90inline void fit_side(StaticVector<Level, kMaxQuoteLevels>& levels,
91 Qty room,
92 const Instrument& inst) noexcept {
93 Qty left = room;
94 std::size_t keep = 0;
95 for (; keep < levels.size(); ++keep) {
96 Level& l = levels[keep];
97 if (l.qty > left) l.qty = inst.round_qty(left);
98 if (!l.qty.is_positive() || l.qty < inst.min_qty) break;
99 if (inst.min_notional.is_positive() && inst.notional(l.price, l.qty) < inst.min_notional) break;
100 left = left - l.qty;
101 }
102 while (levels.size() > keep) levels.pop_back();
103}
104
105// Cuts both sides of a ladder to what the account's balance on the instrument's venue covers
106// (StrategyContext::balance_room, priced at the side's dearest level). What the side's resting
107// orders hold counts as room: the ladder replaces them. A side whose balance the venue has not
108// reported is left as it is. Before any venue reports balances it is one test.
109template <class Ctx>
110FASTMM_NOINLINE void fit_to_reported(Ctx& ctx,
111 InstrumentId id,
112 const Instrument& inst,
113 DesiredQuotes& q) noexcept {
114 for (const Side side : {Side::Buy, Side::Sell}) {
115 auto& levels = side == Side::Buy ? q.bids : q.asks;
116 if (levels.empty()) continue;
117 const Price dear = side == Side::Buy ? levels[0].price : levels[levels.size() - 1].price;
118 const Qty room = ctx.balance_room(id, side, dear);
119 if (room == Qty::max()) continue;
120 fit_side(levels, room + ctx.open_qty(id, side), inst);
121 }
122}
123template <class Ctx>
124FASTMM_FORCE_INLINE void fit_to_balance(Ctx& ctx,
125 InstrumentId id,
126 const Instrument& inst,
127 DesiredQuotes& q) noexcept {
128 if (FASTMM_UNLIKELY(ctx.balances_live())) fit_to_reported(ctx, id, inst, q);
129}
130
131} // namespace fastmm
All engine enumerations.
Fixed<Tag>: int64 fixed point with a global 1e-8 scale (ADR-0001).
Instrument: the single definition of a tradable contract (5.3 / 5.15).
Event bus message structs (5.5).
Definition strategy.hpp:16
void fit_side(StaticVector< Level, kMaxQuoteLevels > &levels, Qty room, const Instrument &inst) noexcept
Cuts one side of a ladder to room in total, keeping the levels closest to the mid: each level is roun...
Definition quoting.hpp:92
void fit_to_reported(Ctx &ctx, InstrumentId id, const Instrument &inst, DesiredQuotes &q) noexcept
Cuts both sides of a ladder to what the account's balance on the instrument's venue covers (StrategyC...
Definition quoting.hpp:112
constexpr Price away_from(Price ref, Side side, Price dist) noexcept
A price dist away from ref on the passive side: Buy -> ref - dist, Sell -> ref + dist.
Definition quoting.hpp:51
Side
Definition enums.hpp:12
constexpr Ratio spread_ratio(Price bid, Price ask) noexcept
(ask - bid) / mid; zero when the mid is not positive. 1 bp == Ratio raw 10'000.
Definition quoting.hpp:44
FASTMM_FORCE_INLINE void fit_to_balance(Ctx &ctx, InstrumentId id, const Instrument &inst, DesiredQuotes &q) noexcept
Definition quoting.hpp:126
Fixed< struct QtyTag > Qty
Definition fixed_point.hpp:333
constexpr Price microprice(Level bid, Level ask) noexcept
Size-weighted microprice of the touch, truncated toward zero.
Definition quoting.hpp:35
void keep_passive(DesiredQuotes &q, Price best_bid, Price best_ask, Price tick) noexcept
Post-only quotes must not cross the market.
Definition quoting.hpp:71
constexpr bool inventory_allows(Side side, Qty position, Qty qty, Qty limit) noexcept
Whether a fill of qty on side keeps the position within +-limit: Buy needs position + qty <= limit,...
Definition quoting.hpp:58
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
Fixed< RatioTag > Ratio
A dimensionless factor: 1.0 == 1e8 raw, 1 bp == 10'000 raw, so 0.0001 bp is the smallest step.
Definition fixed_point.hpp:336
constexpr Ratio ratio(Fixed< T > num, Fixed< T > den) noexcept
num / den as a Ratio, truncated toward zero; zero when den is zero.
Definition fixed_point.hpp:356
constexpr Price mid(Price bid, Price ask) noexcept
Definition quoting.hpp:29
QuoteManager (5.6): diffs the strategy's DesiredQuotes against the orders resting at the venue and em...
Definition quote_manager.hpp:56
static constexpr Fixed max() noexcept
Definition fixed_point.hpp:165
static constexpr Fixed from_raw(std::int64_t r) noexcept
Definition fixed_point.hpp:155
std::int64_t raw
Definition fixed_point.hpp:152
constexpr bool is_positive() const noexcept
Definition fixed_point.hpp:302
Definition instrument.hpp:29
One price level. Shared by book messages and the L2 book.
Definition messages.hpp:30
Price price
Definition messages.hpp:31
Qty qty
Definition messages.hpp:32