Skip to content

Add joint calendar support: Calendar accepts a list of calendar types#247

Open
DMZ22 wants to merge 1 commit into
domokane:masterfrom
DMZ22:joint-calendars
Open

Add joint calendar support: Calendar accepts a list of calendar types#247
DMZ22 wants to merge 1 commit into
domokane:masterfrom
DMZ22:joint-calendars

Conversation

@DMZ22

@DMZ22 DMZ22 commented Jul 22, 2026

Copy link
Copy Markdown

Implements #228 — joint (multi-market) calendars for business-day adjustment, e.g. a swap with a USD SOFR floating leg and a EUR fixed leg that must avoid both US and TARGET holidays.

Design

Calendar now also accepts a list (or tuple) of CalendarTypes:

cal = Calendar([CalendarTypes.UNITED_STATES, CalendarTypes.TARGET])
cal.adjust(dt, BusDayAdjustTypes.MODIFIED_FOLLOWING)
  • A date is a holiday if it is a holiday in any constituent calendar, so a business day must be a business day in every market — the standard join-holidays convention (same as QuantLib's JointCalendar).
  • No new enum member. Existing tests (and likely user code) iterate list(CalendarTypes) and construct Calendar(t) for each member, so adding a JOINT member would break them. Instead, for a joint calendar cal_type holds the constituent types as a tuple, which never compares equal to any CalendarTypes member — so every existing code path (the NONE/WEEKEND fast paths, adjust, fast_adjust, add_business_days, get_holiday_list) works unchanged because they all funnel through is_business_day / is_holiday.
  • A one-element or duplicates-only list collapses to the equivalent plain calendar; an empty list or a non-CalendarTypes entry raises FinError, matching the existing validation style.
  • str()/repr() render as JOINT(UNITED_STATES,TARGET).

Scope

The change is deliberately confined to Calendar plus tests. Schedule and the product classes still annotate cal_type: CalendarTypes; if this API looks right to you, I'm happy to follow up with a small PR relaxing those annotations/checks so a list can be passed straight through Schedule and the swap constructors.

Testing

  • 9 new test functions (13 test cases) in unit_tests/test_FinCalendar.py, including: a day-by-day sweep over 2020–2023 asserting the joint calendar's business days are exactly the intersection of the single calendars'; adjustment chaining across consecutive UK-only holidays (Good Friday → Easter Monday 2021, where a US-only calendar stays put but US+UK must jump to the Tuesday); fast_adjustadjust for joint calendars across all conventions; add_business_days round-trips; get_holiday_list union; collapse/dedup semantics; str/repr; and error cases.
  • unit_tests/test_FinCalendar.py: 285 passed.
  • Full unit suite on Python 3.11: 675 passed, with 17 collection errors that are identical on current master (multi-line f-strings, a Python-3.12-only feature, in modules unrelated to this change — your CI runs 3.13 so it doesn't see this; I'll file it as a separate issue since pyproject.toml declares >=3.10).
  • pylint on the changed file: 10.00/10.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant