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] A calendar entity exists in ores.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 its currency_pair successor) is migrated onto the new model.
  • [X] A currency_country junction 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_code is a non-nullable FK to the existing country entity, using ISO 3166-1's reserved ZZ user-assigned code as the sentinel for supranational calendars (TARGET) rather than a nullable column.
  • calendar_type is its own aux-type table (public_holiday, central_bank_meeting, financial_centre, data_release, other), following the monetary_nature=/=market_tier pattern — not a bare text enum on calendar.
  • Sub-market calendar variants (e.g. UnitedStates.NYSE vs UnitedStates.GovernmentBond) are separate calendar rows sharing the dotted QuantLib token verbatim as code, 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/*.hpp headers.
  • DQ bundle publication for calendar=/=calendar_type was 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 when currency_country (task 2) and currency_calendar (task 3) were modelled — confirmed against party_country and currency_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), so currency_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 recreate seeds them without needing the C++ path to run). ores.codegen.junction doesn't yet support a "real data" custom generator section the way ores.codegen.entity does, so currency_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_calendar keeps its name rather than being renamed to calendar=/=holiday_calendar: it matches ORE's own AdvanceCalendar XML field verbatim and denotes a distinct concept from currency.holiday_calendar (drives date-advancing via Calendar::advance(), not business/settlement-day membership), per the existing AdvanceCalendar knowledge doc.

Out of scope

Emacs 29.3 (Org mode 9.6.15)