Trade Execution: Derived Financial Quantities and Fixed-Point Arithmetic¶
Try it live on Compiler Explorer
Overview¶
This example models the execution of a buy order filled in multiple tranches and computes the average fill price (cost basis) at several settlement precisions. It demonstrates two ideas that do not appear in the currency example:
- Derived financial quantities —
notional_value(price × market quantity) is a separate quantity specification, distinct fromcurrency, enforced by the type system. - Integer fixed-point arithmetic — prices are scaled to
USD_8(units of $10⁻⁸) and stored asstd::int64_tto avoid floating-point rounding during accumulation.
Key Concepts¶
Derived Quantity Specifications: market_quantity and notional_value¶
In finance, notional value = price × market quantity. This is not the same quantity as currency: you cannot directly add a price to a notional value, and dividing notional value by market quantity should recover currency, not notional value. The example encodes this with derived quantity specifications:
// clang-format off
inline constexpr struct dim_currency final : base_dimension<"$"> {} dim_currency;
// clang-format on
QUANTITY_SPEC(currency, dim_currency);
QUANTITY_SPEC(market_quantity, dimensionless, is_kind);
QUANTITY_SPEC(notional_value, currency, currency* market_quantity, is_kind);
inline constexpr auto Qty = market_quantity;
Both market_quantity and notional_value carry is_kind, making each the root of its
own kind family:
market_quantitywithis_kind— market quantity is its own kind, distinct from genericdimensionlessquantities (ratios, percentages, etc.). Without it, a market quantity would silently convert to a plain number.notional_valuewithis_kind— notional value is its own kind, distinct from currency. Without it, a notional value would silently convert to a plain currency amount, defeating the type safety the derived quantity is meant to provide.
is_kind on market_quantity disables implicit construction from plain integers, so a
Qty helper alias is provided: Qty(137) calls the quantity spec directly as a factory.
The relationship notional_value = currency × market_quantity is the defining
equation; explicit casts still work in both directions via that equation. The
static_asserts verify the intended semantics at compile time:
static_assert(!implicitly_convertible(notional_value, currency));
static_assert(!implicitly_convertible(currency, notional_value));
static_assert(explicitly_convertible(notional_value, currency));
static_assert(explicitly_convertible(currency, notional_value));
Integer Fixed-Point Units¶
Floating-point arithmetic accumulates rounding error across many fills. Instead, prices are stored as integers in a fine-grained unit:
// clang-format off
inline constexpr struct us_dollar final : named_unit<"USD", kind_of<currency>> {} us_dollar;
inline constexpr struct us_dollar_2 final : named_unit<"USD_2", mag_power<10, -2> * us_dollar> {} us_dollar_2;
inline constexpr struct us_dollar_4 final : named_unit<"USD_4", mag_power<10, -4> * us_dollar> {} us_dollar_4;
inline constexpr struct us_dollar_6 final : named_unit<"USD_6", mag_power<10, -6> * us_dollar> {} us_dollar_6;
inline constexpr struct us_dollar_8 final : named_unit<"USD_8", mag_power<10, -8> * us_dollar> {} us_dollar_8;
// clang-format on
USD_8 represents $10⁻⁸ (one hundred-millionth of a dollar). A price of $12.95
becomes 1,295,000,000 USD_8 — an exact integer. Multiplying by a market quantity
count gives an exact integer notional value. No floating-point arithmetic is involved
until the final display.
Type Aliases and the Fill Struct¶
// Prices are affine: $0 is a meaningful absolute reference, not an arbitrary offset
using Price = quantity_point<currency[us_dollar]>;
using Shares = quantity<market_quantity[one], int>;
using Notional = quantity<notional_value[us_dollar_8], std::int64_t>;
struct Fill {
Price price;
Shares qty;
};
Price— aquantity_point(price has an absolute zero; $0 is meaningful)Shares— a dimensionless market quantity countNotional— the derived notional value inUSD_8Fill— groups a price and market quantity count together; the natural unit of trade data
Code Walkthrough¶
Computing Notional: force_in for Lossless Scaling¶
[[nodiscard]] Notional notional(const Fill& f)
{
const quantity price_in_usd8 = f.price.quantity_from_zero().force_in<std::int64_t>(us_dollar_8);
return price_in_usd8 * f.qty;
}
force_in<std::int64_t>(us_dollar_8) converts the price unit from USD to USD_8
and changes the representation from double to std::int64_t in one step. The product
price_in_usd8 * f.qty yields a derived expression (currency × market quantity)
that is implicitly convertible to notional_value; the return converts it to
Notional.
Accumulating Fills¶
// Three partial fills of a single buy order
const std::array fills = {
Fill{Price{12.95 * USD}, Qty(137)},
Fill{Price{12.40 * USD}, Qty(126)},
Fill{Price{12.70 * USD}, Qty(85)},
};
// Notional = price × shares; the derived 'notional_value' quantity spec enforces this
// Notional bad = fills[0].price.quantity_from_zero() + fills[1].price.quantity_from_zero(); // does not
// compile
Notional total_notional = {};
Shares total_qty = {};
for (const auto& f : fills) {
total_notional += notional(f);
total_qty += f.qty;
}
Each fill is constructed with an explicit Price{...} (the constructor is explicit, so
direct initialization is required) and Qty(137) (the market_quantity spec called
directly as a factory, required because is_kind disables implicit construction from
plain integers). The accumulator variables total_notional and total_qty are
zero-initialized via copy initialization from {}. The notional_value quantity
specification ensures that adding two price quantities does not accidentally produce a
notional value — the // does not compile comment shows what the type system prevents.
Recovering the Average Fill Price¶
// Dividing notional by shares recovers currency (not notional_value), enforced at compile time
// Notional bad = total_notional / total_qty; // does not compile
const quantity cost_basis_raw = currency(total_notional / total_qty);
const Price cost_basis{cost_basis_raw};
const Price cost_basis_r2{currency(round<USD_2>(cost_basis_raw))};
const Price cost_basis_r4{currency(round<USD_4>(cost_basis_raw))};
const Price cost_basis_r6{currency(round<USD_6>(cost_basis_raw))};
const Price cost_basis_r8{currency(round<USD_8>(cost_basis_raw))};
currency(total_notional / total_qty) performs an explicit quantity cast to the
currency specification — without it, the type system sees
notional_value / market_quantity as an unreduced derived expression and round<USD_N>
cannot match it. The cast is required.
Rounding is then applied at four different settlement precisions so the output shows how much information is retained at each level.
Example Usage¶
int main()
{
using namespace unit_symbols;
// Three partial fills of a single buy order
const std::array fills = {
Fill{Price{12.95 * USD}, Qty(137)},
Fill{Price{12.40 * USD}, Qty(126)},
Fill{Price{12.70 * USD}, Qty(85)},
};
// Notional = price × shares; the derived 'notional_value' quantity spec enforces this
// Notional bad = fills[0].price.quantity_from_zero() + fills[1].price.quantity_from_zero(); // does not
// compile
Notional total_notional = {};
Shares total_qty = {};
for (const auto& f : fills) {
total_notional += notional(f);
total_qty += f.qty;
}
// Dividing notional by shares recovers currency (not notional_value), enforced at compile time
// Notional bad = total_notional / total_qty; // does not compile
const quantity cost_basis_raw = currency(total_notional / total_qty);
const Price cost_basis{cost_basis_raw};
const Price cost_basis_r2{currency(round<USD_2>(cost_basis_raw))};
const Price cost_basis_r4{currency(round<USD_4>(cost_basis_raw))};
const Price cost_basis_r6{currency(round<USD_6>(cost_basis_raw))};
const Price cost_basis_r8{currency(round<USD_8>(cost_basis_raw))};
std::cout << "Fills:\n";
for (const auto& f : fills) std::cout << " " << f << " (notional: " << notional(f) << ")\n";
std::cout << std::setprecision(12);
std::cout << "Total: " << total_qty << " shares, notional: " << total_notional << "\n";
std::cout << "Cost basis (exact): " << cost_basis << "\n";
std::cout << "Cost basis (USD_2): " << cost_basis_r2 << "\n";
std::cout << "Cost basis (USD_4): " << cost_basis_r4 << "\n";
std::cout << "Cost basis (USD_6): " << cost_basis_r6 << "\n";
std::cout << "Cost basis (USD_8): " << cost_basis_r8 << "\n";
}
Sample Output:
Fills:
137 @ 12.95 USD (notional: 177415000000 USD_8)
126 @ 12.4 USD (notional: 156240000000 USD_8)
85 @ 12.7 USD (notional: 107950000000 USD_8)
Total: 348 shares, notional: 441605000000 USD_8
Cost basis (exact): 12.68979885 USD
Cost basis (USD_2): 12.69 USD
Cost basis (USD_4): 12.6898 USD
Cost basis (USD_6): 12.689799 USD
Cost basis (USD_8): 12.68979885 USD
The notional values are large integers in USD_8; the cost basis is recovered as a
human-readable USD value at varying precisions.
Why This Matters¶
- Derived Quantities: The
notional_valuespecification makes the price × market quantity algebra compile-time-safe — you cannot confuse a notional value with a price or a plain currency amount - Kind Safety:
is_kindon bothmarket_quantityandnotional_valueensures that market quantity and notional value do not silently decay to their parent kinds (dimensionlessand currency respectively) - Lossless Accumulation: Integer arithmetic in
USD_8avoids floating-point drift across many fills; only the final display converts to floating-point - Explicit Rounding: Rounding to a specific settlement precision (
USD_2,USD_4,USD_6,USD_8) is an explicit, visible operation — not hidden inside a helper - Domain Modeling: The
Fillstruct groups related quantities, andoperator<<provides domain-appropriate output (shares @ price)
Related Example¶
The currency example focuses on multi-currency type safety and FX conversion. This example focuses on single-currency arithmetic precision. Together they cover the two main concerns in financial quantity modelling.