8#include "fastmm/core/config_macros.hpp"
21inline constexpr std::int64_t
kFixedScale = 100'000'000;
36[[nodiscard]] FASTMM_FORCE_INLINE
constexpr std::int64_t mul_div(std::int64_t a,
38 std::int64_t d)
noexcept {
40 if (FASTMM_LIKELY(!__builtin_mul_overflow(a, b, &p) && d != -1))
return p / d;
41 return static_cast<std::int64_t
>(
static_cast<Int128
>(a) * b / d);
43template <std::
int64_t D>
44[[nodiscard]] FASTMM_FORCE_INLINE
constexpr std::int64_t mul_div(std::int64_t a,
45 std::int64_t b)
noexcept {
47 if (FASTMM_LIKELY(!__builtin_mul_overflow(a, b, &p)))
return p / D;
48 return static_cast<std::int64_t
>(
static_cast<Int128
>(a) * b / D);
55[[nodiscard]]
constexpr std::optional<std::int64_t> parse_scaled_decimal(
56 std::string_view s,
int decimals,
bool literal =
false) noexcept {
57 constexpr std::int64_t kMax = std::numeric_limits<std::int64_t>::max();
60 if (!literal && !s.empty() && (s[0] ==
'-' || s[0] ==
'+')) {
64 const std::size_t mant_begin = i;
65 std::int64_t n_digits = 0;
66 std::int64_t frac_digits = 0;
68 for (; i < s.size(); ++i) {
70 if (c >=
'0' && c <=
'9') {
72 if (dot) ++frac_digits;
73 }
else if (c ==
'.' && !dot) {
75 }
else if (!literal || c !=
'\'') {
79 const std::size_t mant_end = i;
80 if (n_digits == 0)
return std::nullopt;
84 if (s[i] !=
'e' && s[i] !=
'E')
return std::nullopt;
88 if (i < s.size() && (s[i] ==
'-' || s[i] ==
'+')) {
89 exp_neg = s[i] ==
'-';
92 std::size_t exp_digits = 0;
93 for (; i < s.size(); ++i) {
95 if (literal && c ==
'\'')
continue;
96 if (c <
'0' || c >
'9')
return std::nullopt;
97 if (exp < 100'000) exp = exp * 10 + (c -
'0');
100 if (exp_digits == 0)
return std::nullopt;
101 if (exp_neg) exp = -exp;
103 if (literal && !dot && !has_exp && n_digits > 1 && s[mant_begin] ==
'0')
return std::nullopt;
105 const std::int64_t shift = exp - frac_digits + decimals;
106 const std::int64_t keep = n_digits + (shift < 0 ? shift : 0);
108 std::int64_t idx = 0;
109 for (std::size_t k = mant_begin; k < mant_end; ++k) {
111 if (c <
'0' || c >
'9')
continue;
112 const int d = c -
'0';
114 if (r > (kMax - d) / 10)
return std::nullopt;
121 for (std::int64_t k = 0; k < shift && r != 0; ++k) {
122 if (r > kMax / 10)
return std::nullopt;
130[[nodiscard]]
inline std::optional<std::int64_t> scaled_round(
double d,
double scale)
noexcept {
131 constexpr double kTwoPow63 = 9223372036854775808.0;
132 const double scaled = d * scale;
133 const double rounded = scaled < 0 ? scaled - 0.5 : scaled + 0.5;
134 if (!(rounded > -kTwoPow63 && rounded < kTwoPow63))
return std::nullopt;
135 return static_cast<std::int64_t
>(rounded);
140[[nodiscard]]
inline F saturate(
double d)
noexcept {
141 if (d != d)
return F{};
142 return d < 0 ? F::min() : F::
max();
152 constexpr Fixed() noexcept = default;
153 [[nodiscard]] static constexpr
Fixed from_raw(std::int64_t r) noexcept {
162 [[nodiscard]]
static constexpr Fixed zero() noexcept {
return Fixed{}; }
163 [[nodiscard]]
static constexpr Fixed max() noexcept {
164 return from_raw(std::numeric_limits<std::int64_t>::max());
166 [[nodiscard]]
static constexpr Fixed min() noexcept {
167 return from_raw(std::numeric_limits<std::int64_t>::min());
172 [[nodiscard]]
static constexpr std::optional<Fixed>
from_decimal(std::string_view s)
noexcept {
173 if (s.empty())
return std::nullopt;
176 if (s[0] ==
'-' || s[0] ==
'+') {
180 constexpr std::int64_t kMaxInt =
181 std::numeric_limits<std::int64_t>::max() /
kFixedScale;
183 std::size_t int_digits = 0;
184 for (; i < s.size() && s[i] !=
'.'; ++i) {
186 if (c <
'0' || c >
'9')
return std::nullopt;
187 if (ip > (kMaxInt - (c -
'0')) / 10)
return std::nullopt;
188 ip = ip * 10 + (c -
'0');
192 std::size_t frac_digits = 0;
195 for (; i < s.size(); ++i) {
197 if (c <
'0' || c >
'9')
return std::nullopt;
199 fp = fp * 10 + (c -
'0');
201 }
else if (c !=
'0') {
206 if (int_digits == 0 && frac_digits == 0)
return std::nullopt;
207 for (std::size_t k = frac_digits; k < static_cast<std::size_t>(
kFixedDecimals); ++k) fp *= 10;
209 if (mag > std::numeric_limits<std::int64_t>::max() - fp)
return std::nullopt;
210 const std::int64_t r = mag + fp;
218 [[nodiscard]]
static constexpr std::optional<Fixed>
parse(std::string_view s)
noexcept {
219 const std::optional<std::int64_t> r = detail::parse_scaled_decimal(s,
kFixedDecimals);
220 if (!r)
return std::nullopt;
227 requires std::is_same_v<Tag, RatioTag>
231 [[nodiscard]]
static std::optional<Fixed>
from_bps_checked(
double bps)
noexcept
232 requires std::is_same_v<Tag, RatioTag>
234 const std::optional<std::int64_t> r =
235 detail::scaled_round(bps,
static_cast<double>(
kRatioPerBp));
236 if (!r)
return std::nullopt;
240 [[nodiscard]]
double to_bps() const noexcept
241 requires std::is_same_v<Tag, RatioTag>
243 return static_cast<double>(
raw) /
static_cast<double>(
kRatioPerBp);
248 std::size_t
to_decimal(
char* buf)
const noexcept {
250 std::uint64_t mag = 0;
253 mag =
raw == std::numeric_limits<std::int64_t>::min()
254 ?
static_cast<std::uint64_t
>(std::numeric_limits<std::int64_t>::max()) + 1U
255 :
static_cast<std::uint64_t
>(-
raw);
257 mag =
static_cast<std::uint64_t
>(
raw);
259 const std::uint64_t ip = mag /
static_cast<std::uint64_t
>(
kFixedScale);
260 std::uint64_t fp = mag %
static_cast<std::uint64_t
>(
kFixedScale);
264 std::uint64_t v = ip;
266 tmp[n++] =
static_cast<char>(
'0' + (v % 10));
269 while (n > 0) *p++ = tmp[--n];
274 frac[k] =
static_cast<char>(
'0' + (fp % 10));
278 while (len > 0 && frac[len - 1] ==
'0') --len;
279 for (
int k = 0; k < len; ++k) *p++ = frac[k];
281 return static_cast<std::size_t
>(p - buf);
291 const std::optional<std::int64_t> r = detail::scaled_round(d,
static_cast<double>(
kFixedScale));
292 if (!r)
return std::nullopt;
295 [[nodiscard]]
double to_double() const noexcept {
296 return static_cast<double>(
raw) /
static_cast<double>(
kFixedScale);
299 [[nodiscard]]
constexpr bool is_zero() const noexcept {
return raw == 0; }
300 [[nodiscard]]
constexpr bool is_positive() const noexcept {
return raw > 0; }
330using Price = Fixed<struct PriceTag>;
331using Qty = Fixed<struct QtyTag>;
336static_assert(std::is_trivially_copyable_v<Price> &&
sizeof(
Price) == 8);
345[[nodiscard]]
constexpr Fixed<T>
operator*(
Ratio r, Fixed<T> v)
noexcept {
354[[nodiscard]]
constexpr Ratio ratio(Fixed<T> num, Fixed<T> den)
noexcept {
355 if (den.raw == 0)
return Ratio{};
364template <
class A,
class B>
365[[nodiscard]]
constexpr std::int64_t
mul_raw(Fixed<A> a, Fixed<B> b)
noexcept {
366 return detail::mul_div<kFixedScale>(a.raw, b.raw);
370 if (q.raw == 0)
return Price{};
375[[nodiscard]]
constexpr Fixed<T>
apply_bps(Fixed<T> v, std::int64_t bps)
noexcept {
380[[nodiscard]]
constexpr std::int64_t
floor_div(std::int64_t a, std::int64_t b)
noexcept {
381 const std::int64_t q = a / b;
382 return (a % b != 0 && ((a < 0) != (b < 0))) ? q - 1 : q;
387 FASTMM_ASSERT(tick.raw > 0);
389 const std::int64_t floored = q * tick.raw;
394 FASTMM_ASSERT(tick.raw > 0);
395 const std::int64_t half = tick.raw / 2;
396 const std::int64_t shifted = p.raw >= 0 ? p.raw + half : p.raw - half;
400 return tick.raw > 0 && p.raw % tick.raw == 0;
404 FASTMM_ASSERT(lot.raw > 0);
407[[nodiscard]]
constexpr bool on_lot(
Qty q,
Qty lot)
noexcept {
408 return lot.raw > 0 && q.raw % lot.raw == 0;
412[[nodiscard]]
constexpr Fixed<T>
min(Fixed<T> a, Fixed<T> b)
noexcept {
413 return a.raw < b.raw ? a : b;
416[[nodiscard]]
constexpr Fixed<T>
max(Fixed<T> a, Fixed<T> b)
noexcept {
417 return a.raw < b.raw ? b : a;
423[[nodiscard]]
constexpr std::optional<Fixed<T>>
checked_add(Fixed<T> a, Fixed<T> b)
noexcept {
425 if (__builtin_add_overflow(a.raw, b.raw, &r))
return std::nullopt;
429[[nodiscard]]
constexpr std::optional<Fixed<T>>
checked_sub(Fixed<T> a, Fixed<T> b)
noexcept {
431 if (__builtin_sub_overflow(a.raw, b.raw, &r))
return std::nullopt;
435[[nodiscard]]
constexpr std::optional<Fixed<T>>
checked_mul(Fixed<T> a, std::int64_t k)
noexcept {
437 if (__builtin_mul_overflow(a.raw, k, &r))
return std::nullopt;
443inline constexpr char kLiteralChars[
sizeof...(Cs)] = {Cs...};
445[[nodiscard]]
constexpr std::optional<std::int64_t> literal_raw(
int decimals)
noexcept {
446 return parse_scaled_decimal(
447 std::string_view(kLiteralChars<Cs...>,
sizeof...(Cs)), decimals,
true);
454inline namespace literals {
456constexpr Price operator""_px(
unsigned long long v)
noexcept {
459constexpr Qty operator""_qty(
unsigned long long v)
noexcept {
463[[nodiscard]]
constexpr Price operator""_px() noexcept {
464 static_assert(detail::literal_raw<Cs...>(
kFixedDecimals).has_value(),
465 "fastmm: _px literal needs more than 8 decimals or is out of range");
470[[nodiscard]]
constexpr Qty operator""_qty() noexcept {
471 static_assert(detail::literal_raw<Cs...>(
kFixedDecimals).has_value(),
472 "fastmm: _qty literal needs more than 8 decimals or is out of range");
477[[nodiscard]]
constexpr Ratio operator""_bps() noexcept {
478 static_assert(detail::literal_raw<Cs...>(
kBpsDecimals).has_value(),
479 "fastmm: _bps literal needs more than 4 decimals or is out of range");
Definition strategy.hpp:16
constexpr Fixed< T > apply_bps(Fixed< T > v, std::int64_t bps) noexcept
value * bps / 10000 in one domain.
Definition fixed_point.hpp:377
constexpr Qty round_to_lot(Qty q, Qty lot) noexcept
Quantities always round down (never send more than intended).
Definition fixed_point.hpp:405
constexpr Fixed< T > operator*(Fixed< T > v, Ratio r) noexcept
value * ratio in the value's domain, through Int128 with one truncation toward zero: mid * 5_bps,...
Definition fixed_point.hpp:343
constexpr int kFixedDecimals
Definition fixed_point.hpp:24
constexpr Price round_to_tick_nearest(Price p, Price tick) noexcept
Definition fixed_point.hpp:395
constexpr std::int64_t kFixedScale
1e-8
Definition fixed_point.hpp:23
constexpr Fixed< T > min(Fixed< T > a, Fixed< T > b) noexcept
Definition fixed_point.hpp:414
constexpr std::int64_t kRatioPerBp
Ratio (below): 1.0 == kFixedScale raw, one basis point == kRatioPerBp raw.
Definition fixed_point.hpp:28
constexpr Fixed< T > max(Fixed< T > a, Fixed< T > b) noexcept
Definition fixed_point.hpp:418
Side
Definition enums.hpp:12
constexpr Price div(Notional n, Qty q) noexcept
notional / qty -> price (used for average cost). Truncates toward zero.
Definition fixed_point.hpp:371
constexpr std::optional< Fixed< T > > checked_mul(Fixed< T > a, std::int64_t k) noexcept
Definition fixed_point.hpp:437
Fixed< struct QtyTag > Qty
Definition fixed_point.hpp:333
Fixed< struct NotionalTag > Notional
Definition fixed_point.hpp:334
constexpr std::optional< Fixed< T > > checked_sub(Fixed< T > a, Fixed< T > b) noexcept
Definition fixed_point.hpp:431
constexpr std::int64_t floor_div(std::int64_t a, std::int64_t b) noexcept
Floor division for possibly-negative numerators (prices are >= 0, but be safe).
Definition fixed_point.hpp:382
Fixed< struct PriceTag > Price
Definition fixed_point.hpp:332
constexpr std::optional< Fixed< T > > checked_add(Fixed< T > a, Fixed< T > b) noexcept
Checked arithmetic: nullopt instead of a wrapped int64. The operators stay unchecked; these are for t...
Definition fixed_point.hpp:425
constexpr std::size_t kMaxDecimalChars
sign + 11 integer digits + '.' + 8 fraction digits + NUL
Definition fixed_point.hpp:26
constexpr std::int64_t mul_raw(Fixed< A > a, Fixed< B > b) noexcept
Generic scaled multiply for fixed*fixed in one domain (e.g. qty * multiplier).
Definition fixed_point.hpp:367
constexpr bool on_lot(Qty q, Qty lot) noexcept
Definition fixed_point.hpp:409
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 Notional mul(Price p, Qty q) noexcept
price * qty with a 128-bit intermediate, truncated toward zero to 1e-8.
Definition fixed_point.hpp:362
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 int kBpsDecimals
Definition fixed_point.hpp:29
constexpr Price round_to_tick(Price p, Price tick, Side side) noexcept
Round to the tick grid in the passive direction: bids down, asks up. tick > 0.
Definition fixed_point.hpp:388
constexpr bool on_tick(Price p, Price tick) noexcept
Definition fixed_point.hpp:401
Definition fixed_point.hpp:150
constexpr bool is_negative() const noexcept
Definition fixed_point.hpp:303
friend constexpr Fixed operator%(Fixed a, Fixed b) noexcept
Definition fixed_point.hpp:317
constexpr Fixed & operator+=(Fixed o) noexcept
Definition fixed_point.hpp:318
static Fixed from_bps(double bps) noexcept
Ratio only.
Definition fixed_point.hpp:228
double to_double() const noexcept
Definition fixed_point.hpp:297
static constexpr Fixed max() noexcept
Definition fixed_point.hpp:165
static constexpr std::optional< Fixed > parse(std::string_view s) noexcept
Exact parse of a decimal in plain or exponent notation: "0.1", "2e-05", "1.5E3", "-3".
Definition fixed_point.hpp:220
double to_bps() const noexcept
Ratio only. Diagnostics only.
Definition fixed_point.hpp:242
constexpr Fixed abs() const noexcept
Definition fixed_point.hpp:304
constexpr auto operator<=>(const Fixed &) const noexcept=default
static constexpr Fixed min() noexcept
Definition fixed_point.hpp:168
std::size_t to_decimal(char *buf) const noexcept
Formats into buf (>= kMaxDecimalChars), trims trailing zeros ("1.5", "100", "0.00000001").
Definition fixed_point.hpp:250
static constexpr Fixed from_raw(std::int64_t r) noexcept
Definition fixed_point.hpp:155
friend constexpr Fixed operator*(Fixed a, std::int64_t k) noexcept
Definition fixed_point.hpp:312
friend constexpr Fixed operator/(Fixed a, std::int64_t k) noexcept
Definition fixed_point.hpp:314
constexpr Fixed operator-() const noexcept
Definition fixed_point.hpp:309
static constexpr Fixed zero() noexcept
Definition fixed_point.hpp:164
constexpr bool is_zero() const noexcept
Definition fixed_point.hpp:301
friend constexpr Fixed operator+(Fixed a, Fixed b) noexcept
Definition fixed_point.hpp:310
std::int64_t raw
Definition fixed_point.hpp:152
constexpr Fixed() noexcept=default
static Fixed from_double(double d) noexcept
Rounds to the 1e-8 grid.
Definition fixed_point.hpp:288
constexpr bool is_positive() const noexcept
Definition fixed_point.hpp:302
static std::optional< Fixed > from_bps_checked(double bps) noexcept
Definition fixed_point.hpp:233
std::int64_t rep_type
Definition fixed_point.hpp:151
constexpr std::int64_t units() const noexcept
Definition fixed_point.hpp:305
static std::optional< Fixed > from_double_checked(double d) noexcept
nullopt for NaN, +-inf and anything outside the representable range; two compares more.
Definition fixed_point.hpp:292
static constexpr Fixed from_int(std::int64_t units) noexcept
Whole units, e.g. from_int(100) == 100.00000000.
Definition fixed_point.hpp:161
constexpr Fixed & operator-=(Fixed o) noexcept
Definition fixed_point.hpp:322
static constexpr std::optional< Fixed > from_decimal(std::string_view s) noexcept
Exact parse of "[+-]digits[.digits]".
Definition fixed_point.hpp:174
constexpr Fixed & operator*=(std::int64_t k) noexcept
Definition fixed_point.hpp:326