Files
mp-units/example/trade_execution.cpp

136 lines
5.4 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 <mp-units/compat_macros.h>
#ifdef MP_UNITS_IMPORT_STD
import std;
#else
#include <array>
#include <iomanip>
#include <iostream>
#endif
#ifdef MP_UNITS_MODULES
import mp_units.core;
#else
#include <mp-units/core.h>
#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<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
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<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;
};
[[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;
}
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<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";
}