Story: Model calendars as proper ORE Studio reference data
Table of Contents
This page documents a story in Sprint 23. It captures the goal, current status, acceptance criteria, and the tasks that compose it.
Pulled in from the product backlog's next bucket as a not-yet-started BACKLOG story.
Goal
Today, every place in ORE Studio that needs a holiday calendar
(fx_convention.advance_calendar, and the currency.holiday_calendar /
currency_pair calendar fields proposed by
Currency pair support in reference data) stores it as unvalidated free
text — a name ORE/QuantLib resolves internally at pricing time (e.g.
TARGET, UnitedStates; ORE itself already supports comma-joining
several such names into a union calendar, so "one currency needs more
than one calendar" is an ORE-side concern, not something we need to
reimplement). ORE Studio itself has no domain model for calendars: no
entity, no FK, no screens, nothing stopping a typo from silently
breaking a pricing run instead of failing at data entry.
This story gives ORE Studio its own clean calendar domain model —
enumerating valid calendar codes, screens to manage them, and a proper
FK-referenceable relationship from currency (and anything else that
needs one) to its calendar(s). Split off from the currency pair story
so calendar modelling can be scoped and built independently, with
currency pair consuming it once it exists rather than embedding a
placeholder text field.
Closed at Sprint 23's end, narrowed to what shipped: the entity model, FK relationships, Qt CRUD screens, and the currency_country junction. The two remaining acceptance items (XML export boundary, DQ publish) split into a Sprint 24 story — see Finish integrating calendar reference data: XML export and DQ publish.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent sprint | Sprint 23 |
| Now | Closed: core entity modelling, FK relationships, and Qt CRUD screens shipped (5/5 of this story's own narrowed acceptance). Remaining XML export boundary and DQ publish split into a Sprint 24 story. |
| Waiting on | Nothing. |
| Next | See the Sprint 24 follow-on story. |
| Last touched | 2026-07-21 |
Acceptance
[X]Acalendarentity exists inores.refdata, enumerating valid ORE/QuantLib calendar codes with a proper primary key, not free text.[X]Currencies reference their calendar(s) via a real FK relationship (soft or hard), supporting a currency needing more than one calendar (e.g. a country calendar and a financial-centre calendar).[X]Qt screens exist to list/add/edit calendars, following the usual entity CRUD pattern.[X]fx_convention.advance_calendar(or itscurrency_pairsuccessor) is migrated onto the new model.[X]Acurrency_countryjunction exists, since calendars are associated with countries (a currency can span several), not with currencies directly.
Tasks
| Task | State | Start | End | Description |
|---|---|---|---|---|
| Model the calendar entity for codegen | DONE | 2026-07-13 | 2026-07-14 | Model a calendar domain entity enumerating valid ORE/QuantLib calendar codes (name, optional flag image, etc.) ready to drive ores.codegen.entity. |
| Migrate currency.holiday_calendar to a real FK relationship | DONE | 2026-07-15 | 2026-07-15 | Replace currency.holiday_calendar's free-text value with a proper (soft or hard) FK relationship to the new calendar entity, supporting a currency needing more than one calendar via a junction table. |
| Migrate currency_pair_convention.advance_calendar onto the calendar model | DONE | 2026-07-16 | 2026-07-17 | Replace advance_calendar's comma-joined free-text value with a junction table validated against the calendar entity. Resolve the naming question along the way: is 'advance_calendar' idiomatic, or should it just be 'holiday_calendar'/'calendar'? |
| Qt screens for calendars: list/add/edit, combo picker, flag display | DONE | 2026-07-18 | 2026-07-18 | Build the usual entity CRUD Qt screens for calendar (list/add/edit/history), a reusable combo picker widget for other screens to select a calendar (showing the calendar's flag if it has one), and a toolbar button in Currency Pair Conventions' main window into Calendars. |
| Model a currency-country junction | DONE | 2026-07-14 | 2026-07-14 | Model a many-to-many currency_country junction linking currency to the existing country entity, since a currency can span multiple countries (e.g. EUR -> DE, FR, IT, …) and calendar.country_code needs a country to FK against. |
Decisions
- Closed at Sprint 23's end with acceptance narrowed to what shipped. The two remaining acceptance items (XML export boundary, DQ publish) split into one Sprint 24 story rather than left open indefinitely — both are about making the shipped model reachable by the rest of the system, not separate initiatives, so they stay together rather than being split further.
calendar.country_codeis a non-nullable FK to the existingcountryentity, using ISO 3166-1's reservedZZuser-assigned code as the sentinel for supranational calendars (TARGET) rather than a nullable column.calendar_typeis its own aux-type table (public_holiday,central_bank_meeting,financial_centre,data_release,other), following themonetary_nature=/=market_tierpattern — not a bare text enum oncalendar.- Sub-market calendar variants (e.g.
UnitedStates.NYSEvsUnitedStates.GovernmentBond) are separatecalendarrows sharing the dotted QuantLib token verbatim ascode, not a joined variant field — matches ORE's own XML<Calendar>vocabulary exactly. - Seeded with the full QuantLib calendar set (~60 codes) rather than
just the story's illustrative list, transcribed from a local
QuantLib checkout's
ql/time/calendars/*.hppheaders. - DQ bundle publication for
calendar=/=calendar_typewas delivered as part of the entity task rather than waiting for the dedicated DQ-publish task, since it was a natural extension of landing the entities cleanly; the junction tables' publish support is still pending their own tasks. - Junction codegen (
ores.codegen.junction) only supported the SQL address whencurrency_country(task 2) andcurrency_calendar(task 3) were modelled — confirmed againstparty_countryandcurrency_currency_group, both SQL-only at the time — so their C++ layers are hand-authored, mirroring the closest existing junction. A separate story closed that gap between task 3 and task 4 (Fix domain/generator/repository Mustache templates for junction-shaped data), socurrency_pair_convention_calendar(task 4) is the first junction in this story generated end-to-end via codegen — future junction work should use codegen, not hand-authoring, following that precedent. - Reference-data seeds that matter beyond synthetic test fixtures
(QuantLib calendars, currency-issuing countries, pair-convention
calendars) are written twice by design where possible: once as a
C++ generator (for programmatic/test use) and once as a matching
SQL populate script (so
compass db recreateseeds them without needing the C++ path to run).ores.codegen.junctiondoesn't yet support a "real data" custom generator section the wayores.codegen.entitydoes, socurrency_pair_convention_calendar's seed is SQL-only for now — a gap to close in a future junction codegen enhancement, not something to hand-edit the generated file for. currency_pair_convention.advance_calendarkeeps its name rather than being renamed tocalendar=/=holiday_calendar: it matches ORE's ownAdvanceCalendarXML field verbatim and denotes a distinct concept fromcurrency.holiday_calendar(drives date-advancing viaCalendar::advance(), not business/settlement-day membership), per the existing AdvanceCalendar knowledge doc.