48 return pos_[
id.value];
63 const std::int64_t f = side == Side::Buy ? qty.raw : -qty.raw;
64 const std::int64_t cur = p.
qty.
raw;
65 const bool inv = inst.inverse();
68 book(
id, &Totals::fees, fee);
71 if (cur == 0 || (cur > 0) == (f > 0)) {
73 const std::int64_t denom = cur + f;
74 p.
avg_px = inv ? harmonic_avg(cur, p.
avg_px, f, px, denom)
75 : arithmetic_avg(cur, p.
avg_px, f, px, denom);
76 p.
qty = Qty::from_raw(denom);
79 const std::int64_t abs_cur = cur < 0 ? -cur : cur;
80 const std::int64_t abs_f = f < 0 ? -f : f;
81 const std::int64_t closed = abs_f < abs_cur ? abs_f : abs_cur;
82 const std::int64_t dir = cur > 0 ? 1 : -1;
85 r = inst.inverse_pnl(p.
avg_px, px, Qty::from_raw(closed * dir));
88 r = scale(Notional::from_raw(
static_cast<std::int64_t
>(pnl)), inst);
92 book(
id, &Totals::realized, r);
93 const std::int64_t remaining = abs_f - closed;
95 p.
qty = Qty::from_raw(f > 0 ? remaining : -remaining);
98 p.
qty = Qty::from_raw(cur + f);
105 set_exposure(
id, signed_notional(p, px, inst));
126 ? inst.inverse_pnl(est_px, px, qty)
127 : scale(Notional::from_raw(detail::mul_div<kFixedScale>(px.raw - est_px.raw, qty.raw)),
131 realized_total_ += adj;
132 book(
id, &Totals::realized, adj);
135 book(
id, &Totals::fees, fee);
145 realized_total_ += amount;
146 book(
id, &Totals::realized, amount);
147 fund_->per[
id.value] += amount;
148 fund_->total += amount;
149 if (FASTMM_UNLIKELY(converting())) fund_->native[ccy_[
id.value]] += amount;
153 FASTMM_ASSERT(
id.value < kMaxInstruments);
154 return fund_->per[
id.value];
161 set_unrealized(
id, p, revalue(p, mid, inst));
162 set_exposure(
id, signed_notional(p, mid, inst));
173 if (!qty.is_zero() && !avg_px.is_positive()) {
179 set_unrealized(
id, p, revalue(p, p.
last_mark, inst));
180 set_exposure(
id, signed_notional(p, p.
last_mark, inst));
182 void reset(InstrumentId
id)
noexcept {
185 book(
id, &Totals::realized,
Notional{} - p.realized);
186 fees_total_ -= p.fees;
187 book(
id, &Totals::fees,
Notional{} - p.fees);
188 fund_->total -= fund_->per[
id.value];
189 if (converting()) fund_->native[ccy_[
id.value]] -= fund_->per[
id.value];
190 fund_->per[
id.value] = Notional{};
191 set_unrealized(
id, p, Notional{});
192 set_exposure(
id, Notional{});
200 void set_accounting(
const FxPlan& plan)
noexcept {
201 ccy_count_ = plan.count;
203 for (
auto& r : rate_) r = FxRate{};
204 rate_[0] = FxRate::identity();
205 for (
auto& t : native_) t =
Totals{};
206 for (
auto& t : conv_) t =
Totals{};
209 if (!converting())
return;
210 for (std::size_t i = 0; i < kMaxInstruments; ++i) fund_->native[ccy_[i]] += fund_->per[i];
212 for (std::size_t i = 0; i < kMaxInstruments; ++i) {
213 const Position& p = pos_[i];
214 Totals& t = native_[ccy_[i]];
215 t.realized += p.realized;
216 t.unrealized += p.unrealized;
218 t.gross += exposure_[i].abs();
219 t.net += exposure_[i];
224 void set_rate(std::size_t c, FxRate r)
noexcept {
225 if (c == 0 || c >= kMaxCurrencies)
return;
229 [[nodiscard]]
bool converting() const noexcept {
return ccy_count_ > 1; }
230 [[nodiscard]] std::size_t currency_count() const noexcept {
return ccy_count_; }
231 [[nodiscard]] FxRate rate(std::size_t c)
const noexcept {
232 return c < kMaxCurrencies ? rate_[c] : FxRate{};
241 [[nodiscard]]
Notional net_pnl() const noexcept {
return realized + unrealized - fees; }
243 [[nodiscard]]
const Totals& native(std::size_t c)
const noexcept {
return native_[c]; }
252 [[nodiscard]]
Notional gross_exposure() const noexcept {
253 return FASTMM_UNLIKELY(converting()) ? conv_total_.gross : gross_exposure_;
255 [[nodiscard]]
Notional net_exposure() const noexcept {
256 return FASTMM_UNLIKELY(converting()) ? conv_total_.net : net_exposure_;
258 [[nodiscard]]
Notional total_realized() const noexcept {
259 return FASTMM_UNLIKELY(converting()) ? conv_total_.realized : realized_total_;
261 [[nodiscard]]
Notional total_unrealized() const noexcept {
262 return FASTMM_UNLIKELY(converting()) ? conv_total_.unrealized : unrealized_total_;
264 [[nodiscard]]
Notional total_fees() const noexcept {
265 return FASTMM_UNLIKELY(converting()) ? conv_total_.fees : fees_total_;
268 [[nodiscard]]
Notional total_funding() const noexcept {
269 if (!converting())
return fund_->total;
271 for (std::size_t c = 0; c < ccy_count_; ++c) t += convert(fund_->native[c], rate_[c]);
275 [[nodiscard]] Notional native_funding(std::size_t c)
const noexcept {
276 return c < kMaxCurrencies ? fund_->native[c] : Notional{};
278 [[nodiscard]]
Notional net_pnl() const noexcept {
279 return total_realized() + total_unrealized() - total_fees();
281 [[nodiscard]]
Portfolio portfolio() const noexcept {
285 t.
fees = total_fees();
292 if (inst.contract_multiplier == Qty::from_int(1))
return n;
293 return Notional::from_raw(
mul_raw(n, inst.contract_multiplier));
296 static Price arithmetic_avg(
297 std::int64_t cur, Price avg, std::int64_t f, Price px, std::int64_t denom)
noexcept {
298 const Int128 num =
static_cast<Int128
>(cur) * avg.
raw +
static_cast<Int128
>(f) * px.raw;
299 return Price::from_raw(
static_cast<std::int64_t
>(num / denom));
303 static Price harmonic_avg(
304 std::int64_t cur, Price avg, std::int64_t f, Price px, std::int64_t denom)
noexcept {
305 if (!px.is_positive())
return avg;
306 Int128 inv =
static_cast<Int128
>(f) * kFixedScale / px.
raw;
307 if (cur != 0 && avg.is_positive()) inv +=
static_cast<Int128
>(cur) * kFixedScale / avg.raw;
308 if (inv == 0)
return px;
309 return Price::from_raw(
310 static_cast<std::int64_t
>(
static_cast<Int128
>(denom) * kFixedScale / inv));
314 static Notional signed_notional(
const Position& p, Price mark,
const Instrument& inst)
noexcept {
315 if (p.qty.is_zero() || !mark.is_positive())
return Notional{};
316 const Notional n = inst.notional(mark, p.qty.abs());
319 static Notional revalue(
const Position& p, Price mark,
const Instrument& inst)
noexcept {
320 if (p.qty.is_zero() || !mark.is_positive())
return Notional{};
321 if (inst.inverse())
return inst.inverse_pnl(p.avg_px, mark, p.qty);
323 Notional::from_raw(detail::mul_div<kFixedScale>(mark.raw - p.avg_px.raw, p.qty.raw)), inst);
327 void set_exposure(InstrumentId
id, Notional e)
noexcept {
328 Notional& cur = exposure_[
id.value];
331 gross_exposure_ += dg;
333 book(
id, &Totals::gross, dg);
334 book(
id, &Totals::net, dn);
337 void set_unrealized(InstrumentId
id, Position& p, Notional u)
noexcept {
338 const Notional d = u - p.unrealized;
339 unrealized_total_ += d;
340 book(
id, &Totals::unrealized, d);
345 FASTMM_FORCE_INLINE
void book(InstrumentId
id, Notional Totals::*f,
Notional d) noexcept {
346 if (FASTMM_UNLIKELY(converting())) book_converted(id, f, d);
348 FASTMM_NOINLINE
void book_converted(InstrumentId
id, Notional Totals::*f,
Notional d) noexcept {
349 const std::uint8_t c = ccy_[id.value];
352 const Notional v = convert(n, rate_[c]);
353 conv_total_.*f += v - conv_[c].*f;
356 void reconvert(std::size_t c)
noexcept {
358 {&Totals::realized, &Totals::unrealized, &Totals::fees, &Totals::gross, &Totals::net}) {
359 const Notional v = convert(native_[c].*f, rate_[c]);
360 conv_total_.*f += v - conv_[c].*f;
371 std::uint8_t ccy_count_ = 0;
372 std::array<std::uint8_t, kMaxInstruments> ccy_{};
373 std::array<FxRate, kMaxCurrencies> rate_{FxRate::identity()};
374 std::array<Totals, kMaxCurrencies> native_{};
375 std::array<Totals, kMaxCurrencies> conv_{};
376 Totals conv_total_{};
381 std::array<Notional, kMaxInstruments> per{};
382 std::array<Notional, kMaxCurrencies> native{};
385 std::unique_ptr<Funding> p = std::make_unique<Funding>();
386 FundingBox() =
default;
387 FundingBox(
const FundingBox& o) : p(std::make_unique<
Funding>(*o.p)) {}
388 FundingBox(FundingBox&&) noexcept = default;
389 FundingBox& operator=(const FundingBox& o) {
390 if (
this != &o) *p = *o.p;
393 FundingBox& operator=(FundingBox&&) noexcept = default;
394 ~FundingBox() = default;
395 Funding* operator->() const noexcept {
return p.get(); }