Task: Migrate fx_spot_generation_config.ore_key onto oresmd
Table of Contents
This page documents a task in the Migrate to oresmd, delete market_series qualifier and ore_key story. It captures the goal, current status, acceptance, and any notes or results.
Goal
Switch fx_spot_generation_config.ore_key's generation source from the
hand-written oresmd://fx/<pair>?type=quote currency-pair derivation
(FX/RATE/{base}/{quote}) to oresmd_projections::to_quote_key, so the
FX quote-key string is produced by the shared oresmd projection rule
instead of being duplicated inline.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Migrate to oresmd, delete market_series qualifier and ore_key |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-07-29 |
Acceptance
[X]The only productionore_keyderivation site (FxSpotRateEditor::onSaveClicked) generatesore_keyviaoresmd_projections::to_quote_keyinstead of hand-formattingFX/RATE/{base}/{quote}.[X]Output is unchanged for well-formed pairs:to_quote_keyonfx_market_data_identifier{pair=base+quote, type=quote}produces exactly the same string the old inline derivation did (verified by readingoresmd_projections.cpp'squote_key_fxand by the existing 81-caseores.marketdata.core.testssuite, which already covers this projection rule).[X]Full local build (compass build) is clean.[ ]Delete of "old derivation code": nothing to delete beyond the inline string concat replaced above – there was no separate standalone derivation function.
Plan
(Implementation strategy. Written when work starts; key decisions
are distilled into the parent story's * Decisions at close, but the
plan itself stays — it is the historical record of what we did.)
- Enumerate every write site of
fx_spot_generation_config.ore_key– found exactly one real derivation (FxSpotRateEditor.cpp:onSaveClicked,"FX/RATE/" + base + "/" + quote); every other assignment is either a mapper round-trip (repository <-> domain), a synthetic-data generator fixture (hardcodedEUR/USDfor test rows, unrelated to real currency codes), or a plain read. - Replace the inline derivation with a
marketdata::domain::fx_market_data_identifierbuilt frombase+quoteandinstrument_type::quote, projected viamarketdata::core::oresmd_projections::to_quote_key. - Link
ores.marketdata.core.lib(previously onlyores.marketdata.api.libwas linked) as aPRIVATEdependency ofores.qt.synthetic.lib, sinceoresmd_projectionslives inores.marketdata.core. - Full
compass buildto confirm no regressions across the tree. - Could not verify against existing DB rows: this environment's
database was freshly recreated with no synthetic data provisioned
(
fx_spot_generation_configtable exists but is empty) – equivalence was instead confirmed by inspectingquote_key_fx's implementation (FX/RATE/{pair[0:3]}/{pair[3:6]}, byte-identical to the old inline format) and by the projection's existing test coverage.
Notes
ores.synthetic.api/generators/fx_spot_generation_config_generator.cpp's hardcodedr.ore_key = "FX/RATE/EUR/USD"was deliberately left unmigrated: it's synthetic test-fixture data whosebase_currency_code=/=quote_currency_codeare suffixed with a disambiguating index (e.g.EUR-3) to guarantee uniqueness across generated rows, not real 3-letter ISO codes – feeding that throughoresmd_projections::to_quote_keywould fail the pair-length validation (pair.size() !6=) and returnstd::nullopt. Migrating it isn't in scope: it's fixture generation, not the production derivation this task targets.
Test Scenarios
Manual QA scenarios (scaffolded via compass add test_scenario, run
through the QA Validation Runner panel) that verify this task. Link
new ones here as they're created; the scenario doc itself links back
via its "Verifies task" field.
| Scenario | State | Notes |
|---|---|---|
PRs
| PR | Title |
|---|---|
| #1746 | [marketdata] Migrate fx_spot_generation_config.ore_key onto oresmd |
Review
| # | Comment summary | File | Decision | Notes |
|---|---|---|---|---|
| 1 | recomputeOreKey() preview label still hand-concatenates FX/RATE/base/quote, duplicating the exact formula the task set out to eliminate | FxSpotRateEditor.cpp | Accepted | Extracted a shared oreKeyFor(base, quote) helper (anonymous namespace) wrapping oresmd_projections::to_quote_key; both recomputeOreKey() and onSaveClicked() now call it, so there is exactly one implementation of the FX ore_key format in this file. |
| 2 | to_quote_key(…).value_or(std::string()) silently persists an empty ore_key if the projection ever returns nullopt, unlike every other validation failure in onSaveClicked which warns and returns | FxSpotRateEditor.cpp | Accepted | onSaveClicked now checks oreKeyFor(…)'s result explicitly: on nullopt it shows a QMessageBox::warning ("Could not derive an ORE key for this currency pair.") and returns without saving, consistent with the function's other validation checks. |
Result
FxSpotRateEditor::onSaveClicked (the only production write site for
fx_spot_generation_config.ore_key) now derives ore_key via
marketdata::core::oresmd_projections::to_quote_key on a
fx_market_data_identifier{pair=base+quote, type=quote}, instead of the
hand-written FX/RATE/{base}/{quote} string concatenation – byte-identical
output for well-formed pairs, now sourced from the shared oresmd projection
rule instead of a duplicated local formula. ores.marketdata.core.lib added
as a PRIVATE link dependency of ores.qt.synthetic.lib to reach
oresmd_projections. Full compass build is clean; no separate legacy
derivation function existed to delete – the acceptance's "delete the old
derivation code" is satisfied by the inline replacement itself. Couldn't
verify against existing DB rows (this environment's database has no
synthetic data provisioned), so equivalence rests on reading
quote_key_fx's implementation plus the existing 81-case
ores.marketdata.core.tests coverage of that projection rule.