Story: Currency pair support in reference data
Table of Contents
This page documents a story in Sprint 22. It captures the goal, current status, acceptance criteria, and the tasks that compose it.
Goal
ORE Studio treats currencies as first-class reference data, but currency
pairs (conventions, quoting, cross rates) are not yet modelled as
such. This story is the umbrella for the full currency pair feature:
knowledge-base foundation (done), domain model design (done), the
ores.codegen.entity implementation of that model, and librarian
support so a new party gets a usable set of currency pairs out of the
box. It stays open across all of that — it is not a design-only story.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent sprint | Sprint 22 |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | None — 3 remaining polish items moved to Sprint 22 snags. |
| Last touched | 2026-07-10 |
Acceptance
[X]A currency pair knowledge document set exists underdoc/knowledge/, broken into small self-contained documents (conventions, quoting, cross rates, etc.) rather than one monolith.[X]The knowledge documents are cross-linked via org-roam[[id:...]]links, including to existing relevant documents (e.g. the cross rates matrix).[X]An index document ties the set together for discoverability.[X]The currency pair entity (and supporting currency fields) is modelled with all fields identified in the knowledge cluster, ready to driveores.codegen.[X]Creating a party (via librarian) results in a usable set of currency pairs being available for it, following the same publish-from-dq pattern used by the synthetic FX foundation.
Tasks
| Task | State | Start | End | Description |
|---|---|---|---|---|
| Build knowledge documents for currency pairs | DONE | 2026-07-04 | Turn freeform notes on currency pairs into a set of small, self-contained zettelkasten-style knowledge documents, cross-linked via org-roam links (including to existing docs like the cross rates matrix), with an index document tying them together. | |
| Model the currency pair entity for codegen | DONE | 2026-07-04 | Model the currency pair (and supporting currency) entities with all fields identified in the knowledge cluster (base/quote, spot days, deliverability/NDF, pip factor/tick size, classification, cut code, fixing source, etc.), ready to drive ores.codegen. | |
| Replace is_g11 flag with an extensible currency-group model | DONE | 2026-07-04 | Correct the currency_pair design: is_g11 is a fixed boolean that can't express overlapping desk groupings (Scandies, Antipodeans, commodity currencies, etc.), and G11 membership belongs on the currency, not the pair. Replace it with a many-to-many currency_group lookup + junction, mirroring the existing party_currencies junction pattern. | |
| Add librarian support for currency pairs | DONE | 2026-07-08 | 2026-07-08 | Extend the DQ librarian so creating a party yields a usable set of currency pairs out of the box, following the same publish-from-dq bundle pattern used for the synthetic FX foundation. |
| Reconcile currency_pair design with fx_convention | DONE | 2026-07-04 | fx_convention already covers most of currency_pair's ground (spot days, points factor, calendar, business day convention). Fold its extra fields into the currency_pair design and decide the pip_factor vs points_factor representation, ahead of migrating consumers off fx_convention. | |
| Migrate fx_convention consumers to currency_pair and retire it | DONE | 2026-07-06 | 2026-07-08 | Point ores.ore's conventions_mapper, the Qt CRUD screens, and the CLI registration at currency_pair instead of fx_convention, then drop fx_convention's SQL table, repository, service, Qt files, and CLI wiring. |
| Split currency_pair into identity and convention entities; finalize field model | DONE | 2026-07-04 | Split the single currency_pair design into a lean currency_pair identity entity and a currency_pair_convention entity for the fx_convention-derived quoting/date-convention fields, mirroring how ORE conceptually separates a pair from its conventions. Produce the final, settled field-by-field model for both, ready for ores.codegen.entity. | |
| Test ORE import/export of FX conventions against currency_pair | DONE | 2026-07-09 | 2026-07-09 | Verify ORE's conventions.xml <FX> element round-trips correctly through currency_pair after fx_convention is retired: import a real conventions.xml, confirm parsed values match, then export and diff against the original. |
| Implement currency_pair and currency_pair_convention via ores.codegen.entity | DONE | 2026-07-06 | 2026-07-06 | All 5 entities modelled, generated (SQL/C++/messaging/Qt), and building cleanly; new flagged_combo Qt facet added for currency-picker flag icons. |
| Add a generic FK-combo-with-flag-icons facet to ores.codegen.entity | ABANDONED | 2026-07-10 | Moved to Sprint 22 snags — real remaining work, not worth re-carrying the whole story for. | |
| Currency pair flag icons in list are too small | DONE | 2026-07-08 | 2026-07-08 | The dual-flag icon in the Currency Pairs list's Pair column renders too small; it should be sized to the width of two single-currency flags and the height of one, matching single-flag display elsewhere. |
| Base/Quote/Settlement Currency columns have no flags | DONE | 2026-07-08 | The Currency Pairs list's Base, Quote, and Settlement Currency columns show plain ISO codes with no flag icon, unlike other currency-bearing columns/screens in the app. | |
| Deliverable column should use a Yes/No badge | DONE | 2026-07-08 | The Currency Pairs list's Deliverable column shows literal true/false text; it should use a Yes/No badge widget, matching the pattern used for book (clever_dijkstra worktree, not yet merged). | |
| Classification column should use a badge | DONE | 2026-07-08 | The Currency Pairs list's Classification column shows plain text (major/minor/exotic/commodity); add a new classification badge widget and use it in the list, matching the badge pattern used for book. | |
| Currency pair detail dialog prompts to save with no changes | DONE | 2026-07-08 | Opening the Currency Pair detail dialog and immediately closing it prompts 'save changes?' even though nothing was edited — the dirty-tracking is falsely triggered, likely by a widget's initial-value-set path marking the form dirty. | |
| Currency pair detail dialog should use badges for classification/deliverable | DONE | 2026-07-08 | The Currency Pair detail dialog shows classification and deliverable as plain text/checkbox; use the same badge widgets as the list view (and as book, clever_dijkstra worktree, not yet merged). | |
| Add Currency Pairs to the main toolbar | DONE | 2026-07-08 | Currency Pairs has no shortcut button on the application's main toolbar, unlike other frequently-used reference data screens. | |
| Add a Currency Pair Conventions toolbar button to the Currency Pairs MDI window | DONE | 2026-07-08 | The Currency Pairs list MDI window's toolbar has no quick way to jump to Currency Pair Conventions; add a toolbar button there. | |
| Currency Pair Conventions list/detail have no flags for the pair | DONE | 2026-07-08 | The Currency Pair Conventions list and detail dialog show the Pair column as plain text (e.g. USD/KWD) with no flags; apply the same base/quote flag treatment used in the Currency Pairs main dialog. | |
| Business Day Convention should be a badge | DONE | 2026-07-08 | Currency Pair Conventions' Business Day Convention column/field renders as plain text; add a new badge widget for it and use it in both the list and detail dialog. | |
| Spot Relative and End of Month should be Yes/No badges | DONE | 2026-07-08 | Currency Pair Conventions' Spot Relative and End of Month columns/fields render as plain true/false text; use the Yes/No badge widget (same as Currency Pairs' Deliverable) in both list and detail. | |
| Add domain-explanation tooltips to Currency and Currency Pair Convention detail fields | ABANDONED | 2026-07-10 | Moved to Sprint 22 snags. | |
| Document currency pairs and conventions in the user manual | DONE | 2026-07-08 | 2026-07-10 | Add currency pairs and currency pair conventions to the user manual: what they are, how to manage them, and the classification/convention concepts, following the manual's existing chapter structure. |
| Pair code doesn't recompute when base/quote currency changes | DONE | 2026-07-08 | 2026-07-09 | Changing Base or Quote Currency in the Currency Pair detail dialog doesn't update pair_code; raises a design question of whether the entity should be keyed by a GUID instead, with duplicate-pair_code prevention. |
| Creating a Currency Pair with an already-used pair_code silently versions the existing row | DONE | 2026-07-09 | 2026-07-09 | The insert trigger treats a colliding pair_code as a new version of the existing record (the same create-vs-update-by-natural-key pattern used everywhere in this codebase), not a rejected duplicate. Now that pair_code is derived and immutable, a user picking an already-used base/quote combo in Add mode gets no error and silently overwrites the unrelated existing pair's data. |
| Rename deliverable flag to non_deliverable (idiomatic FX trading term) | DONE | 2026-07-09 | 2026-07-09 | currency_pair.deliverable should be renamed to non_deliverable (and the boolean sense inverted) to match the idiomatic FX trading term 'non-deliverable forward' (NDF) – avoids the double-negative confusion of deliverable=false meaning NDF. |
| pair_code inline flag icon renders tiny regardless of keyFlagIconSize() | ABANDONED | 2026-07-10 | Moved to Sprint 22 snags. | |
| Consider merging currency_pair_convention back into currency_pair | DONE | 2026-07-09 | 2026-07-09 | With non_deliverable/settlement_currency/fixing_source dropped, currency_pair is down to 4 fields (pair_code, base_currency, quote_currency, classification) – thin enough to reconsider the earlier deliberate split from currency_pair_convention. Needs a pros/cons decision before any migration. |
| Currency Pair changes never publish NATS events (missing event registrar wiring) | DONE | 2026-07-09 | 2026-07-09 | currency_pair_event_registrar.cpp/.hpp were never generated and wired into event_registrar.cpp's register_event_mappings(), unlike currency/book/country/etc – so saving a Currency Pair never publishes ores.refdata.currency_pair_changed, and no client (including the one that just saved) gets notified or auto-refreshes. |
| Capture screenshots for the Currency Pairs manual chapter | DONE | 2026-07-10 | 2026-07-10 | Test plan and execution checklist for capturing the five screenshot placeholders added in chapter_5b_currency_pairs.org. |
Decisions
- Knowledge-base foundation shipped as six zettels plus a hub note,
extending the existing CRM cluster rather than duplicating it (see the
first task's
* Plan). - Currency pair modelled as its own entity (
currency_pair), not folded intocurrency:spot_daysand calendars are derived at read time from the two constituent currencies rather than stored redundantly on the pair, to avoid the two copies drifting apart. Five new columns land oncurrencyitself (spot_days,deliverable,day_basis,base_precedence,holiday_calendar); a newcurrency_pair_classificationauxiliary type reuses the existingrounding_type=/=monetary_nature=/=market_tiersoft-FK pattern. Full field-by-field design lives in the second task's* Plan; three open questions (calendar/day-count representation, aux-type model type, fixing-source reference) are deferred to the implementation task. - Corrected (third task): the initial design put G11 membership on the
pair as a boolean
is_g11flag. That's wrong — it's a currency-level property, and desks need overlapping groupings beyond G11 (Scandies, Antipodeans, commodity currencies, …) that a boolean can't express. Replaced with acurrency_groupauxiliary type plus a many-to-manycurrency_currency_group_junction, mirroring the existingparty_currenciesjunction pattern.currency.market_tieris unaffected and coexists as the single-valued primary tier alongside the new multi-valued group system. - Discovered:
ores.refdata.fx_conventionalready exists, fully implemented, and covers most ofcurrency_pair's ground (spot days, apoints_factorreciprocal to ourpip_factor, a calendar field, business day convention, spot-relative/end-of-month flags). It exists only to round-trip ORE'sconventions.xml<FX>element — no other consumer. Decision: retire it in favour ofcurrency_pair, folding in its extra fields (see the reconciliation task). - Split off (calendar):
holiday_calendar(oncurrency) andadvance_calendar(folded in fromfx_convention, oncurrency_pair) are both unvalidated free text pointing at an ORE/QuantLib calendar name. Rather than keep them as raw strings, calendar modelling (validated calendar codes, FK relationships, Qt screens) is split off into its own backlog story, Model calendars as proper ORE Studio reference data —currency_pairkeeps a raw-text calendar field only as an interim measure until that story lands and it can be migrated onto a real FK. - Split into two entities:
currency_pair(identity) andcurrency_pair_convention(1:1, thefx_convention-derived quoting/ date fields), matching the codebase's existing*_conventionentity family rather than bundling both concerns into one entity. See the fourth task's* Planfor the finalized field lists. - Split off (cut code):
default_cut_codehas no domain model anywhere in ORE Studio — same free-text-pointing-at-nothing problem as calendars. Split off into its own backlog story, Model market data cuts as proper ORE Studio reference data. Unlike the calendar field,default_cut_codeis dropped fromcurrency_pairentirely rather than kept as interim text — no current consumer needs it, so there's nothing to regress by adding it back as a proper FK once that story lands. - Retired
fx_convention:ores.ore, the Qt refdata plugin, and the CLI now read/writecurrency_pair=/=currency_pair_conventiondirectly; all offx_convention's SQL/C++/Qt/messaging files are deleted, not deprecated.SpotDaysround-trips through a mapper-internal transport struct rather than a persisted field, since the file-basedore roundtripimport/export path has no database access and can't derive it fromcurrencyat that point. Two gaps left by earlier tasks were closed as a prerequisite:currency_pair's SQL was generated but never wired into the master schema/RLS scripts, andcurrency.spot_dayswas modelled but never regenerated — both fixed with C++/SQL-only changes to avoid clashing with the concurrent "Commission: currency" story.
Out of scope
- The day-count and fixing-source reference entities flagged as open questions in the second task.
- Proper calendar modelling — split off to Model calendars as proper ORE Studio reference data.
- Proper cut modelling — split off to Model market data cuts as proper ORE Studio reference data.