// The MIT License (MIT) // // Copyright (c) 2018 Mateusz Pusz // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // !!! Before you commit any changes to this file please make sure to check if it !!! // !!! renders correctly in the documentation "Examples" section. !!! // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #include #ifdef MP_UNITS_IMPORT_STD import std; #else #include #include #include #endif #ifdef MP_UNITS_MODULES import mp_units.core; #else #include #endif using namespace mp_units; // 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; 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)); // clang-format off inline constexpr struct us_dollar final : named_unit<"USD", kind_of> {} 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 namespace unit_symbols { inline constexpr auto USD = us_dollar; inline constexpr auto USD_2 = us_dollar_2; inline constexpr auto USD_4 = us_dollar_4; inline constexpr auto USD_6 = us_dollar_6; inline constexpr auto USD_8 = us_dollar_8; } // namespace unit_symbols // Prices are affine: $0 is a meaningful absolute reference, not an arbitrary offset using Price = quantity_point; using Shares = quantity; using Notional = quantity; struct Fill { Price price; Shares qty; }; [[nodiscard]] Notional notional(const Fill& f) { const quantity price_in_usd8 = f.price.quantity_from_zero().force_in(us_dollar_8); return price_in_usd8 * f.qty; } std::ostream& operator<<(std::ostream& os, const Fill& f) { return os << f.qty << " @ " << f.price; } 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(cost_basis_raw))}; const Price cost_basis_r4{currency(round(cost_basis_raw))}; const Price cost_basis_r6{currency(round(cost_basis_raw))}; const Price cost_basis_r8{currency(round(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"; }