ores.cpp.oresmd.projections
Table of Contents
The ORE projection helpers: ore_type and ore_<asset_class>_metric
switches generated from the spec's Quote types table (the metric cases
grouped by metric, preserving first-appearance order), and the quote_key_*
bodies, which are genuinely per-class shapes and sit in asset-class-keyed
sections. The ir helpers (index_name_ir, curve_key_ir,
default_metric, qualifier_includes_index, ore_vol_model) are static.
The ORE spellings the tables emit live in the static ore_type_spec,
ore_metric_spec, and ore_vol_spec constant namespaces (a "constexpr set
of constants"); the generated switches reference them by name from the
spec's ore_constant column. The static inverse projection
(from_ore_key, ORE key string to market_data_identifier) dispatches on
the same constants, so a rename stays in sync in both directions.
The per-class function order (fx, equity, credit, commodity, inflation, and
commodity's quote_key_ at the end) mirrors the hand-crafted file.
1. Template
{{! GENERATED FILE — tangled from ores.cpp.oresmd.projections.org. Edit the org source. }}
{{{cpp_license}}}
#include "ores.marketdata.core/oresmd/oresmd_projections.hpp"
#include "ores.marketdata.core/oresmd/detail/oresmd_index_family_utils.hpp"
#include "ores.marketdata.core/oresmd/detail/oresmd_string_utils.hpp"
#include <algorithm>
#include <cctype>
#include <format>
#include <magic_enum/magic_enum.hpp>
#include <sstream>
#include <vector>
namespace {
using namespace ores::marketdata::domain;
using ores::marketdata::core::detail::is_overnight;
using ores::marketdata::core::detail::to_lower;
using ores::marketdata::core::detail::to_upper;
/*
* ORE spelling tables. The first segment of a quote key is the ORE series type, the
* second is the metric; the swaption family also carries a volatility model. These
* constants are the single source of truth for those spellings: the forward
* ore_type()/ore_metric()/ore_vol_model() tables and the inverse dispatcher below both
* reference them, so a rename stays in sync in both directions.
*/
namespace ore_type_spec {
constexpr std::string_view fx{"FX"};
constexpr std::string_view fxfwd{"FXFWD"};
constexpr std::string_view ir_swap{"IR_SWAP"};
constexpr std::string_view discount{"DISCOUNT"};
constexpr std::string_view mm{"MM"};
constexpr std::string_view fra{"FRA"};
constexpr std::string_view imm_fra{"IMM_FRA"};
constexpr std::string_view basis_swap{"BASIS_SWAP"};
constexpr std::string_view bma_swap{"BMA_SWAP"};
constexpr std::string_view cc_basis_swap{"CC_BASIS_SWAP"};
constexpr std::string_view cc_fix_float_swap{"CC_FIX_FLOAT_SWAP"};
constexpr std::string_view zero{"ZERO"};
constexpr std::string_view mm_future{"MM_FUTURE"};
constexpr std::string_view oi_future{"OI_FUTURE"};
constexpr std::string_view swaption{"SWAPTION"};
constexpr std::string_view equity{"EQUITY"};
constexpr std::string_view equity_fwd{"EQUITY_FWD"};
constexpr std::string_view equity_dividend{"EQUITY_DIVIDEND"};
constexpr std::string_view commodity{"COMMODITY"};
constexpr std::string_view commodity_fwd{"COMMODITY_FWD"};
constexpr std::string_view cpr{"CPR"};
constexpr std::string_view cds{"CDS"};
constexpr std::string_view hazard_rate{"HAZARD_RATE"};
constexpr std::string_view recovery_rate{"RECOVERY_RATE"};
constexpr std::string_view cds_index{"CDS_INDEX"};
constexpr std::string_view index_cds_tranche{"INDEX_CDS_TRANCHE"};
constexpr std::string_view zc_inflation_swap{"ZC_INFLATIONSWAP"};
constexpr std::string_view yy_inflation_swap{"YY_INFLATIONSWAP"};
constexpr std::string_view seasonality{"SEASONALITY"};
constexpr std::string_view correlation{"CORRELATION"};
} // namespace ore_type_spec
namespace ore_metric_spec {
constexpr std::string_view rate{"RATE"};
constexpr std::string_view price{"PRICE"};
constexpr std::string_view basis_spread{"BASIS_SPREAD"};
constexpr std::string_view ratio{"RATIO"};
constexpr std::string_view yield_spread{"YIELD_SPREAD"};
constexpr std::string_view credit_spread{"CREDIT_SPREAD"};
constexpr std::string_view base_correlation{"BASE_CORRELATION"};
} // namespace ore_metric_spec
namespace ore_vol_spec {
constexpr std::string_view rate_lnvol{"RATE_LNVOL"};
constexpr std::string_view rate_nvol{"RATE_NVOL"};
constexpr std::string_view rate_slnvol{"RATE_SLNVOL"};
constexpr std::string_view shift{"SHIFT"};
constexpr std::string_view price{"PRICE"};
} // namespace ore_vol_spec
std::vector<std::string> split_point(const std::string& point) {
std::vector<std::string> parts;
std::stringstream ss(point);
std::string part;
while (std::getline(ss, part, ','))
parts.push_back(to_upper(part));
return parts;
}
std::string curve_id(std::string_view ccy, std::string_view tenor) {
return std::format("{}{}", ccy, to_upper(tenor));
}
/*
* Both index name and curve key are produced together, gated on `type=fixing` -- the
* design doc's own worked-examples table shows a single `type=fixing` URI producing
* BOTH an index name and a curve key (the underlying curve construct has both facets),
* and reads "--" for both columns on every `type=quote` row. See
* id:C3E053CA-0D4B-480B-9119-E11530160EC1, "Worked examples" > "Interest rates".
*/
std::optional<std::string> index_name_ir(const ir_market_data_identifier& id) {
if (id.type != instrument_type::fixing || !id.index)
return std::nullopt;
const auto family = to_upper(std::string(magic_enum::enum_name(*id.index)));
if (is_overnight(*id.index) || !id.tenor)
return std::format("{}-{}", id.ccy, family);
return std::format("{}-{}-{}", id.ccy, family, to_upper(*id.tenor));
}
std::optional<std::string> curve_key_ir(const ir_market_data_identifier& id) {
if (id.type != instrument_type::fixing || !id.tenor)
return std::nullopt;
return std::format("Yield/{}/{}", id.ccy, curve_id(id.ccy, *id.tenor));
}
// Default metric from ir_quote_type when metric is absent (e.g. quote=mm implies metric=rate).
metric default_metric(ir_quote_type qt) {
switch (qt) {
case ir_quote_type::ir_swap:
case ir_quote_type::discount:
case ir_quote_type::mm:
case ir_quote_type::fra:
case ir_quote_type::imm_fra:
case ir_quote_type::cc_fix_float_swap:
return metric::rate;
case ir_quote_type::basis_swap:
case ir_quote_type::cc_basis_swap:
return metric::basis_spread;
case ir_quote_type::bma_swap:
return metric::ratio;
case ir_quote_type::zero:
return metric::rate;
case ir_quote_type::mm_future:
case ir_quote_type::oi_future:
return metric::price;
}
return metric::rate;
}
{{#ir_ore}}
// ORE TYPE string for each ir_quote_type.
std::string_view ore_type({{asset_class}}_quote_type qt) {
switch (qt) {
{{#quote_types}}
case {{asset_class}}_quote_type::{{enum_name}}:
return ore_type_spec::{{ore_constant}};
{{/quote_types}}
}
return ore_type_spec::{{ore_constant_default}};
}
{{/ir_ore}}
// ORE METRIC string for each metric.
std::string_view ore_metric(metric m) {
switch (m) {
case metric::rate:
return ore_metric_spec::rate;
case metric::price:
return ore_metric_spec::price;
case metric::basis_spread:
return ore_metric_spec::basis_spread;
case metric::ratio:
return ore_metric_spec::ratio;
case metric::yield_spread:
return ore_metric_spec::yield_spread;
}
return ore_metric_spec::rate;
}
// Whether this ir_quote_type includes the index in the qualifier (as opposed to just
// ccy/tenor for xccy/BMA types).
bool qualifier_includes_index(ir_quote_type qt) {
switch (qt) {
case ir_quote_type::cc_basis_swap:
case ir_quote_type::cc_fix_float_swap:
case ir_quote_type::bma_swap:
return false;
default:
return true;
}
}
std::string_view ore_vol_model(volatility_model_subtype m) {
switch (m) {
case volatility_model_subtype::rate_lnvol:
return ore_vol_spec::rate_lnvol;
case volatility_model_subtype::rate_nvol:
return ore_vol_spec::rate_nvol;
case volatility_model_subtype::rate_slnvol:
return ore_vol_spec::rate_slnvol;
case volatility_model_subtype::shift:
return ore_vol_spec::shift;
case volatility_model_subtype::price:
return ore_vol_spec::price;
}
return ore_vol_spec::rate_lnvol;
}
std::optional<std::string> quote_key_ir(const ir_market_data_identifier& id) {
if (id.type == instrument_type::vol) {
// Use typed vol struct if available, else fall back to point composite.
if (id.vol && id.tenor) {
const auto& v = *id.vol;
return std::format("SWAPTION/{}/{}/{}/{}/{}",
ore_vol_model(v.model_subtype),
id.ccy,
to_upper(v.expiry),
to_upper(*id.tenor),
to_upper(v.strike));
}
if (!id.point)
return std::nullopt;
const auto parts = split_point(*id.point);
if (parts.size() != 3)
return std::nullopt;
const auto t = id.tenor ? to_upper(*id.tenor) : parts[1];
return std::format("SWAPTION/RATE_LNVOL/{}/{}/{}/{}", id.ccy, parts[0], t, parts[2]);
}
if (id.type != instrument_type::quote || !id.quote_type || !id.point || !id.tenor)
return std::nullopt;
const auto qt = *id.quote_type;
const auto m = id.metric ? *id.metric : default_metric(qt);
const auto point = to_upper(*id.point);
const auto t = to_upper(*id.tenor);
if (qualifier_includes_index(qt)) {
if (qt == ir_quote_type::ir_swap)
return std::format("{}/{}/{}/2D/{}/{}", ore_type(qt), ore_metric(m), id.ccy, t, point);
if (qt == ir_quote_type::discount)
return std::format("{}/{}/{}/{}/{}",
ore_type(qt),
ore_metric(m),
id.ccy,
curve_id(id.ccy, *id.tenor),
point);
if (!id.index)
return std::nullopt;
const auto idx = to_upper(std::string(magic_enum::enum_name(*id.index)));
return std::format("{}/{}/{}/{}/{}/{}", ore_type(qt), ore_metric(m), id.ccy, idx, t, point);
}
// No-index types: CC_BASIS_SWAP, CC_FIX_FLOAT_SWAP, BMA_SWAP
return std::format("{}/{}/{}/{}/{}", ore_type(qt), ore_metric(m), id.ccy, t, point);
}
{{#fx_ore}}
std::string_view ore_type({{asset_class}}_quote_type qt) {
switch (qt) {
{{#quote_types}}
case {{asset_class}}_quote_type::{{enum_name}}:
return ore_type_spec::{{ore_constant}};
{{/quote_types}}
}
return ore_type_spec::{{ore_constant_default}};
}
std::string_view ore_{{asset_class}}_metric({{asset_class}}_quote_type qt) {
switch (qt) {
{{#metric_groups}}
{{#names}}
case {{asset_class}}_quote_type::{{.}}:
{{/names}}
return ore_metric_spec::{{constant}};
{{/metric_groups}}
}
return ore_metric_spec::{{ore_metric_constant_default}};
}
std::optional<std::string> quote_key_fx(const {{asset_class}}_market_data_identifier& id) {
if (id.type != instrument_type::quote || id.pair.size() != 6)
return std::nullopt;
const auto qt = id.quote_type.value_or(fx_quote_type::spot);
// spot: TYPE/METRIC/CCY1/CCY2 (scalar).
if (qt == fx_quote_type::spot)
return std::format("{}/{}/{}/{}",
ore_type(qt),
ore_fx_metric(qt),
id.pair.substr(0, 3),
id.pair.substr(3, 3));
// fwd: TYPE/METRIC/CCY1/CCY2/TENOR — forward curve, needs point for tenor.
if (!id.point)
return std::nullopt;
return std::format("{}/{}/{}/{}/{}",
ore_type(qt),
ore_fx_metric(qt),
id.pair.substr(0, 3),
id.pair.substr(3, 3),
to_upper(*id.point));
}
{{/fx_ore}}
{{#equity_ore}}
std::string_view ore_type({{asset_class}}_quote_type qt) {
switch (qt) {
{{#quote_types}}
case {{asset_class}}_quote_type::{{enum_name}}:
return ore_type_spec::{{ore_constant}};
{{/quote_types}}
}
return ore_type_spec::{{ore_constant_default}};
}
std::string_view ore_{{asset_class}}_metric({{asset_class}}_quote_type qt) {
switch (qt) {
{{#metric_groups}}
{{#names}}
case {{asset_class}}_quote_type::{{.}}:
{{/names}}
return ore_metric_spec::{{constant}};
{{/metric_groups}}
}
return ore_metric_spec::{{ore_metric_constant_default}};
}
std::optional<std::string> quote_key_equity(const {{asset_class}}_market_data_identifier& id) {
if (id.type != instrument_type::quote)
return std::nullopt;
const auto qt = id.quote_type.value_or(equity_quote_type::spot);
// spot: EQUITY/PRICE/TICKER/CCY (scalar, no tenor).
if (qt == equity_quote_type::spot)
return std::format("{}/{}/{}/{}", ore_type(qt), ore_equity_metric(qt), id.ticker, id.ccy);
// dividend/fwd: TYPE/METRIC/TICKER/CCY/TENOR — curves, need point for the tenor dimension.
if (!id.point)
return std::nullopt;
return std::format("{}/{}/{}/{}/{}",
ore_type(qt),
ore_equity_metric(qt),
id.ticker,
id.ccy,
to_upper(*id.point));
}
{{/equity_ore}}
{{#credit_ore}}
std::string_view ore_type({{asset_class}}_quote_type qt) {
switch (qt) {
{{#quote_types}}
case {{asset_class}}_quote_type::{{enum_name}}:
return ore_type_spec::{{ore_constant}};
{{/quote_types}}
}
return ore_type_spec::{{ore_constant_default}};
}
std::string_view ore_{{asset_class}}_metric({{asset_class}}_quote_type qt) {
switch (qt) {
{{#metric_groups}}
{{#names}}
case {{asset_class}}_quote_type::{{.}}:
{{/names}}
return ore_metric_spec::{{constant}};
{{/metric_groups}}
}
return ore_metric_spec::{{ore_metric_constant_default}};
}
std::optional<std::string> quote_key_credit(const {{asset_class}}_market_data_identifier& id) {
if (id.type != instrument_type::quote)
return std::nullopt;
const auto qt = id.quote_type.value_or(credit_quote_type::cds);
const auto qm = ore_credit_metric(qt);
if (!id.point)
return std::nullopt;
const auto parts = split_point(*id.point);
// RECOVERY_RATE/RATE/ENTITY/SENIORITY/CCY — point is just the seniority.
if (qt == credit_quote_type::recovery_rate) {
if (parts.size() != 1)
return std::nullopt;
return std::format(
"{}/{}/{}/{}/{}", ore_type(qt), qm, id.reference_entity, parts[0], id.ccy);
}
// CDS_INDEX/BASE_CORRELATION/INDEX/TENOR/DETACHMENT — no ccy dimension.
// INDEX_CDS_TRANCHE/BASE_CORRELATION/INDEX/SERIES_TENOR/DETACHMENT — no ccy dimension.
if (qt == credit_quote_type::cds_index || qt == credit_quote_type::index_cds_tranche) {
if (parts.size() != 2)
return std::nullopt;
return std::format(
"{}/{}/{}/{}/{}", ore_type(qt), qm, id.reference_entity, parts[0], parts[1]);
}
// CDS/CREDIT_SPREAD/ENTITY/SENIORITY/CCY/TENOR — point=seniority,tenor (2 parts).
// HAZARD_RATE/RATE/ENTITY/SENIORITY/CCY/TENOR — same 6-segment shape.
if (parts.size() != 2)
return std::nullopt;
return std::format(
"{}/{}/{}/{}/{}/{}", ore_type(qt), qm, id.reference_entity, parts[0], id.ccy, parts[1]);
}
{{/credit_ore}}
{{#commodity_ore}}
std::string_view ore_type({{asset_class}}_quote_type qt) {
switch (qt) {
case commodity_quote_type::spot:
return ore_type_spec::commodity;
case commodity_quote_type::fwd:
return ore_type_spec::commodity_fwd;
// NOTE: Real ORE CPR/RATE quotes are security-level, keyed by ISIN
// (e.g. CPR/RATE/ISIN:XS0983610930, a scalar inside <Security> blocks in
// curveconfig.xml), not commodity/ccy/tenor. Modelling CPR under
// commodity_market_data_identifier is a deliberate simplification since
// ORE Studio has no security-level identifier yet; the key emitted here
// (CPR/RATE/CODE/CCY/TENOR) is ORE-Studio-internal shaped, not ORE-native.
case commodity_quote_type::cpr:
return ore_type_spec::cpr;
}
return ore_type_spec::commodity;
}
std::string_view ore_commodity_metric(commodity_quote_type qt) {
switch (qt) {
case commodity_quote_type::spot:
case commodity_quote_type::fwd:
return ore_metric_spec::price;
case commodity_quote_type::cpr:
return ore_metric_spec::rate;
}
return ore_metric_spec::price;
}
{{/commodity_ore}}
{{#inflation_ore}}
std::string_view ore_type({{asset_class}}_quote_type qt) {
switch (qt) {
{{#quote_types}}
case {{asset_class}}_quote_type::{{enum_name}}:
return ore_type_spec::{{ore_constant}};
{{/quote_types}}
}
return ore_type_spec::{{ore_constant_default}};
}
std::string_view ore_{{asset_class}}_metric({{asset_class}}_quote_type qt) {
switch (qt) {
{{#metric_groups}}
{{#names}}
case {{asset_class}}_quote_type::{{.}}:
{{/names}}
return ore_metric_spec::{{constant}};
{{/metric_groups}}
}
return ore_metric_spec::{{ore_metric_constant_default}};
}
std::optional<std::string> quote_key_inflation(const {{asset_class}}_market_data_identifier& id) {
if (id.type != instrument_type::quote || !id.quote_type || !id.point)
return std::nullopt;
const auto qt = *id.quote_type;
// SEASONALITY/RATE/MULT/<INDEX>/<POINT> — 5-segment key with literal MULT.
if (qt == inflation_quote_type::seasonality)
return std::format("{}/{}/MULT/{}/{}",
ore_type(qt),
ore_inflation_metric(qt),
id.index_code,
to_upper(*id.point));
return std::format(
"{}/{}/{}/{}", ore_type(qt), ore_inflation_metric(qt), id.index_code, to_upper(*id.point));
}
{{/inflation_ore}}
{{#correlation_ore}}
std::optional<std::string> quote_key_correlation(const {{asset_class}}_market_data_identifier& id) {
if (id.type != instrument_type::quote)
return std::nullopt;
return std::format("CORRELATION/RATE/{}", id.factor_pair);
}
{{/correlation_ore}}
{{#commodity_ore}}
std::optional<std::string> quote_key_commodity(const commodity_market_data_identifier& id) {
if (id.type != instrument_type::quote)
return std::nullopt;
const auto qt = id.quote_type.value_or(commodity_quote_type::spot);
// spot: COMMODITY/PRICE/CODE/CCY (scalar).
if (qt == commodity_quote_type::spot)
return std::format(
"{}/{}/{}/{}", ore_type(qt), ore_commodity_metric(qt), id.commodity_code, id.ccy);
// fwd/cpr: TYPE/METRIC/CODE/CCY/TENOR — curves, need point for the tenor.
if (!id.point)
return std::nullopt;
return std::format("{}/{}/{}/{}/{}",
ore_type(qt),
ore_commodity_metric(qt),
id.commodity_code,
id.ccy,
to_upper(*id.point));
}
{{/commodity_ore}}
/*
* ─── Inverse projection: ORE quote key string → oresmd identifier ─────────────
*
* Follows the quote-key forward projections above in reverse. Seeded by the
* series_key_registry's decomposition table: the segment boundaries mirror that
* table's per-type rows (qualifier vs point), and every type the table knows is
* either mapped here or has no oresmd identifier at all (BOND, the option and
* capfloor families) — those, plus unknown types and malformed keys, yield
* nullopt. Segment spelling normalisation mirrors the parser: codes/ccy upper,
* tenor/point/index lower.
*/
std::optional<std::vector<std::string>> split_key(const std::string& key) {
std::vector<std::string> parts;
std::stringstream ss(key);
std::string tok;
while (std::getline(ss, tok, '/'))
parts.push_back(tok);
if (parts.size() < 3)
return std::nullopt;
if (std::ranges::any_of(parts, [](const std::string& p) { return p.empty(); }))
return std::nullopt;
return parts;
}
template <typename Enum>
std::optional<Enum> parse_enum_lower(std::string_view value) {
return magic_enum::enum_cast<Enum>(to_lower(value));
}
std::optional<metric> parse_metric(std::string_view value) {
return parse_enum_lower<metric>(value);
}
std::optional<index_family> parse_index(std::string_view value) {
return parse_enum_lower<index_family>(value);
}
std::optional<volatility_model_subtype> parse_vol_model(std::string_view value) {
return parse_enum_lower<volatility_model_subtype>(value);
}
// The forward projections emit one fixed METRIC segment per type for every
// non-IR asset class; a key whose metric differs could not have come from a
// forward projection and is rejected.
bool metric_is(std::string_view metric_segment, std::string_view expected) {
return to_upper(metric_segment) == expected;
}
// The forward projections emit exactly three alphabetic characters per
// currency segment; a key with anything else could not have come from a
// forward projection.
bool is_currency_code(const std::string& x) {
return x.size() == 3 &&
std::ranges::all_of(x, [](unsigned char c) { return std::isalpha(c); });
}
std::optional<market_data_identifier>
from_fx_spot(const std::vector<std::string>& parts,
const ores::ore::market::fx_quote_convention_checker* checker) {
// FX/RATE/CCY1/CCY2 — scalar, no point.
if (parts.size() != 4 || !metric_is(parts[1], ore_metric_spec::rate))
return std::nullopt;
if (!is_currency_code(parts[2]) || !is_currency_code(parts[3]))
return std::nullopt;
auto base = to_upper(parts[2]);
auto quote = to_upper(parts[3]);
if (checker) {
const auto result = checker->check(base, quote);
base = result.base_currency;
quote = result.quote_currency;
}
fx_market_data_identifier id;
id.pair = base + quote;
id.type = instrument_type::quote;
id.quote_type = fx_quote_type::spot;
return id;
}
std::optional<market_data_identifier> from_fx_fwd(const std::vector<std::string>& parts) {
// FXFWD/RATE/CCY1/CCY2/TENOR.
if (parts.size() != 5 || !metric_is(parts[1], ore_metric_spec::rate))
return std::nullopt;
if (!is_currency_code(parts[2]) || !is_currency_code(parts[3]))
return std::nullopt;
fx_market_data_identifier id;
id.pair = to_upper(parts[2]) + to_upper(parts[3]);
id.type = instrument_type::quote;
id.quote_type = fx_quote_type::fwd;
id.point = to_lower(parts[4]);
return id;
}
std::optional<market_data_identifier> from_ir_swap(const std::vector<std::string>& parts) {
// IR_SWAP/METRIC/CCY/SETTLE/TENOR/POINT. The forward hardcodes the settlement
// ("2D") into the fourth segment; the identifier has no settle field, so the
// segment is accepted verbatim and dropped.
if (parts.size() != 6)
return std::nullopt;
const auto m = parse_metric(parts[1]);
if (!m)
return std::nullopt;
ir_market_data_identifier id;
id.ccy = to_upper(parts[2]);
id.type = instrument_type::quote;
id.quote_type = ir_quote_type::ir_swap;
id.metric = *m;
id.tenor = to_lower(parts[4]);
id.point = to_lower(parts[5]);
return id;
}
std::optional<market_data_identifier> from_ir_discount(const std::vector<std::string>& parts) {
// DISCOUNT/METRIC/CCY/CURVE_ID/POINT, where CURVE_ID = CCY + TENOR.
if (parts.size() != 5)
return std::nullopt;
const auto m = parse_metric(parts[1]);
if (!m)
return std::nullopt;
const auto ccy = to_upper(parts[2]);
const auto curve_id = to_upper(parts[3]);
if (curve_id.size() <= ccy.size() || curve_id.substr(0, ccy.size()) != ccy)
return std::nullopt;
ir_market_data_identifier id;
id.ccy = ccy;
id.type = instrument_type::quote;
id.quote_type = ir_quote_type::discount;
id.metric = *m;
id.tenor = to_lower(curve_id.substr(ccy.size()));
id.point = to_lower(parts[4]);
return id;
}
std::optional<market_data_identifier>
from_ir_indexed(ir_quote_type qt, const std::vector<std::string>& parts) {
// TYPE/METRIC/CCY/INDEX/TENOR/POINT — the families whose qualifier includes
// the index (MM, FRA, IMM_FRA, BASIS_SWAP, ZERO, MM_FUTURE, OI_FUTURE).
if (parts.size() != 6)
return std::nullopt;
const auto m = parse_metric(parts[1]);
const auto idx = parse_index(parts[3]);
if (!m || !idx)
return std::nullopt;
ir_market_data_identifier id;
id.ccy = to_upper(parts[2]);
id.type = instrument_type::quote;
id.quote_type = qt;
id.metric = *m;
id.index = *idx;
id.tenor = to_lower(parts[4]);
id.point = to_lower(parts[5]);
return id;
}
std::optional<market_data_identifier>
from_ir_no_index(ir_quote_type qt, const std::vector<std::string>& parts) {
// TYPE/METRIC/CCY/TENOR/POINT — the xccy/BMA families whose qualifier is just
// ccy/tenor (CC_BASIS_SWAP, CC_FIX_FLOAT_SWAP, BMA_SWAP).
if (parts.size() != 5)
return std::nullopt;
const auto m = parse_metric(parts[1]);
if (!m)
return std::nullopt;
ir_market_data_identifier id;
id.ccy = to_upper(parts[2]);
id.type = instrument_type::quote;
id.quote_type = qt;
id.metric = *m;
id.tenor = to_lower(parts[3]);
id.point = to_lower(parts[4]);
return id;
}
std::optional<market_data_identifier> from_ir_swaption(const std::vector<std::string>& parts) {
// SWAPTION/MODEL/CCY/EXPIRY/TENOR/STRIKE. The forward emits either branch (typed
// vol struct or point composite) in this same six-segment shape; both collapse
// into the vol struct plus the serialised point, matching what the parser builds
// for a type=vol URI.
if (parts.size() != 6)
return std::nullopt;
const auto model = parse_vol_model(parts[1]);
if (!model)
return std::nullopt;
ir_market_data_identifier id;
id.ccy = to_upper(parts[2]);
id.type = instrument_type::vol;
id.tenor = to_lower(parts[4]);
id.point = to_lower(parts[3]) + "," + to_lower(parts[4]) + "," + to_lower(parts[5]);
volatility_surface_point v;
v.expiry = to_upper(parts[3]);
v.strike = to_upper(parts[5]);
v.model_subtype = *model;
id.vol = std::move(v);
return id;
}
std::optional<market_data_identifier> from_equity_spot(const std::vector<std::string>& parts) {
// EQUITY/PRICE/TICKER/CCY — scalar, no point.
if (parts.size() != 4 || !metric_is(parts[1], ore_metric_spec::price))
return std::nullopt;
equity_market_data_identifier id;
id.ticker = to_upper(parts[2]);
id.ccy = to_upper(parts[3]);
id.type = instrument_type::quote;
id.quote_type = equity_quote_type::spot;
return id;
}
std::optional<market_data_identifier>
from_equity_curve(equity_quote_type qt, const std::vector<std::string>& parts) {
// EQUITY_FWD/PRICE/TICKER/CCY/TENOR, EQUITY_DIVIDEND/RATE/TICKER/CCY/TENOR.
const auto expected =
(qt == equity_quote_type::dividend) ? ore_metric_spec::rate : ore_metric_spec::price;
if (parts.size() != 5 || !metric_is(parts[1], expected))
return std::nullopt;
equity_market_data_identifier id;
id.ticker = to_upper(parts[2]);
id.ccy = to_upper(parts[3]);
id.type = instrument_type::quote;
id.quote_type = qt;
id.point = to_lower(parts[4]);
return id;
}
std::optional<market_data_identifier> from_commodity_spot(const std::vector<std::string>& parts) {
// COMMODITY/PRICE/CODE/CCY — scalar, no point.
if (parts.size() != 4 || !metric_is(parts[1], ore_metric_spec::price))
return std::nullopt;
commodity_market_data_identifier id;
id.commodity_code = to_upper(parts[2]);
id.ccy = to_upper(parts[3]);
id.type = instrument_type::quote;
id.quote_type = commodity_quote_type::spot;
return id;
}
std::optional<market_data_identifier>
from_commodity_curve(commodity_quote_type qt, const std::vector<std::string>& parts) {
// COMMODITY_FWD/PRICE/CODE/CCY/TENOR, CPR/RATE/CODE/CCY/TENOR.
const auto expected =
(qt == commodity_quote_type::cpr) ? ore_metric_spec::rate : ore_metric_spec::price;
if (parts.size() != 5 || !metric_is(parts[1], expected))
return std::nullopt;
commodity_market_data_identifier id;
id.commodity_code = to_upper(parts[2]);
id.ccy = to_upper(parts[3]);
id.type = instrument_type::quote;
id.quote_type = qt;
id.point = to_lower(parts[4]);
return id;
}
std::optional<market_data_identifier>
from_credit_curve(credit_quote_type qt, const std::vector<std::string>& parts) {
// CDS/CREDIT_SPREAD/ENTITY/SENIORITY/CCY/TENOR,
// HAZARD_RATE/RATE/ENTITY/SENIORITY/CCY/TENOR — point = seniority,tenor.
const auto expected = (qt == credit_quote_type::cds) ? ore_metric_spec::credit_spread
: ore_metric_spec::rate;
if (parts.size() != 6 || !metric_is(parts[1], expected))
return std::nullopt;
credit_market_data_identifier id;
id.reference_entity = to_upper(parts[2]);
id.ccy = to_upper(parts[4]);
id.type = instrument_type::quote;
id.quote_type = qt;
id.point = to_lower(parts[3]) + "," + to_lower(parts[5]);
return id;
}
std::optional<market_data_identifier> from_credit_recovery(const std::vector<std::string>& parts) {
// RECOVERY_RATE/RATE/ENTITY/SENIORITY/CCY — scalar; point is just the seniority.
if (parts.size() != 5 || !metric_is(parts[1], ore_metric_spec::rate))
return std::nullopt;
credit_market_data_identifier id;
id.reference_entity = to_upper(parts[2]);
id.ccy = to_upper(parts[4]);
id.type = instrument_type::quote;
id.quote_type = credit_quote_type::recovery_rate;
id.point = to_lower(parts[3]);
return id;
}
std::optional<market_data_identifier>
from_credit_index(credit_quote_type qt, const std::vector<std::string>& parts) {
// CDS_INDEX/BASE_CORRELATION/INDEX/TENOR/DETACHMENT,
// INDEX_CDS_TRANCHE/BASE_CORRELATION/INDEX/SERIES_TENOR/DETACHMENT — no ccy
// dimension; point = tenor,detachment.
if (parts.size() != 5 || !metric_is(parts[1], ore_metric_spec::base_correlation))
return std::nullopt;
credit_market_data_identifier id;
id.reference_entity = to_upper(parts[2]);
id.type = instrument_type::quote;
id.quote_type = qt;
id.point = to_lower(parts[3]) + "," + to_lower(parts[4]);
return id;
}
std::optional<market_data_identifier>
from_inflation_swap(inflation_quote_type qt, const std::vector<std::string>& parts) {
// ZC_INFLATIONSWAP/RATE/INDEX/POINT, YY_INFLATIONSWAP/RATE/INDEX/POINT.
if (parts.size() != 4 || !metric_is(parts[1], ore_metric_spec::rate))
return std::nullopt;
inflation_market_data_identifier id;
id.index_code = to_upper(parts[2]);
id.type = instrument_type::quote;
id.quote_type = qt;
id.point = to_lower(parts[3]);
return id;
}
std::optional<market_data_identifier> from_inflation_seasonality(const std::vector<std::string>& parts) {
// SEASONALITY/RATE/MULT/INDEX/POINT — the forward emits the literal MULT in the
// third segment; it is accepted verbatim and dropped.
if (parts.size() != 5 || !metric_is(parts[1], ore_metric_spec::rate))
return std::nullopt;
inflation_market_data_identifier id;
id.index_code = to_upper(parts[3]);
id.type = instrument_type::quote;
id.quote_type = inflation_quote_type::seasonality;
id.point = to_lower(parts[4]);
return id;
}
std::optional<market_data_identifier> from_correlation(const std::vector<std::string>& parts) {
// CORRELATION/RATE/FACTOR_PAIR — scalar, no point.
if (parts.size() != 3 || !metric_is(parts[1], ore_metric_spec::rate))
return std::nullopt;
correlation_market_data_identifier id;
id.factor_pair = to_upper(parts[2]);
id.type = instrument_type::quote;
id.quote_type = correlation_quote_type::pairwise;
return id;
}
std::optional<market_data_identifier>
inverse_projection(const std::vector<std::string>& parts,
const ores::ore::market::fx_quote_convention_checker* checker) {
const auto type = to_upper(parts[0]);
if (type == ore_type_spec::fx)
return from_fx_spot(parts, checker);
if (type == ore_type_spec::fxfwd)
return from_fx_fwd(parts);
if (type == ore_type_spec::ir_swap)
return from_ir_swap(parts);
if (type == ore_type_spec::discount)
return from_ir_discount(parts);
if (type == ore_type_spec::swaption)
return from_ir_swaption(parts);
if (type == ore_type_spec::mm || type == ore_type_spec::fra ||
type == ore_type_spec::imm_fra || type == ore_type_spec::basis_swap ||
type == ore_type_spec::zero || type == ore_type_spec::mm_future ||
type == ore_type_spec::oi_future) {
const auto qt = parse_enum_lower<ir_quote_type>(type);
return qt ? from_ir_indexed(*qt, parts) : std::nullopt;
}
if (type == ore_type_spec::cc_basis_swap || type == ore_type_spec::cc_fix_float_swap ||
type == ore_type_spec::bma_swap) {
const auto qt = parse_enum_lower<ir_quote_type>(type);
return qt ? from_ir_no_index(*qt, parts) : std::nullopt;
}
if (type == ore_type_spec::equity)
return from_equity_spot(parts);
// The ORE spellings differ from the enum names for these types ("EQUITY_FWD" vs
// the enumerator "fwd"), so they are dispatched explicitly rather than by
// magic_enum name lookup -- mirroring the forward ore_type(quote_type) tables.
if (type == ore_type_spec::equity_fwd)
return from_equity_curve(equity_quote_type::fwd, parts);
if (type == ore_type_spec::equity_dividend)
return from_equity_curve(equity_quote_type::dividend, parts);
if (type == ore_type_spec::commodity)
return from_commodity_spot(parts);
if (type == ore_type_spec::commodity_fwd)
return from_commodity_curve(commodity_quote_type::fwd, parts);
if (type == ore_type_spec::cpr)
return from_commodity_curve(commodity_quote_type::cpr, parts);
if (type == ore_type_spec::cds || type == ore_type_spec::hazard_rate) {
const auto qt = parse_enum_lower<credit_quote_type>(type);
return qt ? from_credit_curve(*qt, parts) : std::nullopt;
}
if (type == ore_type_spec::recovery_rate)
return from_credit_recovery(parts);
if (type == ore_type_spec::cds_index || type == ore_type_spec::index_cds_tranche) {
const auto qt = parse_enum_lower<credit_quote_type>(type);
return qt ? from_credit_index(*qt, parts) : std::nullopt;
}
if (type == ore_type_spec::zc_inflation_swap)
return from_inflation_swap(inflation_quote_type::zc_swap, parts);
if (type == ore_type_spec::yy_inflation_swap)
return from_inflation_swap(inflation_quote_type::yy_swap, parts);
if (type == ore_type_spec::seasonality)
return from_inflation_seasonality(parts);
if (type == ore_type_spec::correlation)
return from_correlation(parts);
// No oresmd mapping: BOND, FX_OPTION, CAPFLOOR, INDEX_CDS_OPTION,
// EQUITY_OPTION, COMMODITY_OPTION, ZC_INFLATIONCAPFLOOR, YY_INFLATIONCAPFLOOR,
// and any type the registry does not know.
return std::nullopt;
}
}
namespace ores::marketdata::core {
std::optional<std::string>
oresmd_projections::to_index_name(const domain::market_data_identifier& identifier) {
if (const auto* ir = std::get_if<ir_market_data_identifier>(&identifier))
return index_name_ir(*ir);
return std::nullopt;
}
std::optional<std::string>
oresmd_projections::to_curve_key(const domain::market_data_identifier& identifier) {
if (const auto* ir = std::get_if<ir_market_data_identifier>(&identifier))
return curve_key_ir(*ir);
return std::nullopt;
}
std::optional<std::string>
oresmd_projections::to_quote_key(const domain::market_data_identifier& identifier) {
return std::visit(
[](const auto& id) -> std::optional<std::string> {
using T = std::decay_t<decltype(id)>;
{{#oresmd_variant_specs}}
{{#variant_first}} if constexpr{{/variant_first}}{{^variant_first}} else if constexpr{{/variant_first}} (std::is_same_v<T, {{asset_class}}_market_data_identifier>)
return quote_key_{{asset_class}}(id);
{{/oresmd_variant_specs}}
},
identifier);
}
std::optional<market_series_key>
oresmd_projections::split_market_series_key(const std::string& key) {
std::vector<std::string> parts;
std::stringstream ss(key);
std::string tok;
while (std::getline(ss, tok, '/'))
parts.push_back(tok);
if (parts.size() < 3)
return std::nullopt;
market_series_key result;
result.series_type = parts[0];
result.metric = parts[1];
result.qualifier = parts[2];
for (std::size_t i = 3; i < parts.size(); ++i)
result.qualifier += "/" + parts[i];
return result;
}
std::optional<domain::market_data_identifier>
oresmd_projections::from_ore_key(const std::string& key) {
const auto parts = split_key(key);
if (!parts)
return std::nullopt;
return inverse_projection(*parts, nullptr);
}
std::optional<domain::market_data_identifier> oresmd_projections::from_ore_key(
const std::string& key, const ores::ore::market::fx_quote_convention_checker& checker) {
const auto parts = split_key(key);
if (!parts)
return std::nullopt;
return inverse_projection(*parts, &checker);
}
}