Skip to main content

Reference


Where calendars are kept​

KindServiceSourcesNotes
Holiday, intraday and other calendarscalendarprivate, publicVersioned. A holiday calendar keeps its rules in calendar.rules, each with its plain English text in code. An intraday calendar keeps its definition in calendar.
Expiry calendarsexpiryprivate, publicVersioned. The rules are in calendar.rules.map, by tenor, each with its plain English text in rule.code.

Every save makes a new version: SERVICE/v1/SOURCE/ID/* lists the versions and SERVICE/v1/SOURCE/ID/N reads version N.


Saving from plain English​

Holiday calendar​

POST calendar/v1/private

{
"_id": "NGE_TRADING",
"_type": "HolidayCalendar",
"name": "Northgate trading days",
"description": "UK bank holidays and office closures",
"rules": ["Every Saturday", "Every Sunday", "Good Friday"]
}
FieldDescription
_idThe calendar id
_typeHolidayCalendar
name, descriptionOptional
rulesThe rules: a string is a plain English rule; an object is a stored rule (such as an embedded calendar) and is kept as it is

Every plain English rule must follow the grammar, or nothing is saved and the error names each rule that does not.

Intraday calendar​

POST calendar/v1/private

{
"_id": "NGE_DE_PEAK_HOURS",
"_type": "VarCalendar",
"name": "German power peak hours",
"description": "Hours 08:00 to 20:00 on EEX trading days",
"calendar": {
"_type": "IntradayCalendar",
"period": 3600000,
"timezoneCode": "Europe/Berlin",
"holidayCalendarCode": "#EEX_POWER",
"useHolidays": false,
"withoutHours": [0, 1, 2, 3, 4, 5, 6, 7, 20, 21, 22, 23]
}
}
FieldDescription
calendar._typeIntradayCalendar (BlockCalendar and HourBlockCalendar are intraday calendars too, and are kept as they are)
periodThe interval in milliseconds: at least one minute, at most 12 hours, and it must divide a day exactly
timezoneCodeThe time zone of the hours. Defaults to UTC.
holidayCalendarCodeOptional. The calendar that decides which days are open. It must exist and must not be an intraday calendar.
useHolidaysfalse: days the holiday calendar closes have no intervals. true: those days keep every interval (off-peak).
withoutHoursThe hours of the day (0 to 23, in the time zone) to leave out. At least one hour must be kept.

The calendar's own _id and _name are set from the record's _id and name. A definition that breaks one of these rules is refused with the reason.

note

Until this release, the holiday calendar of a stored intraday calendar was not looked up when the calendar was read, so every day was open whatever holidayCalendarCode said. It is now applied. A calendar whose holiday calendar is DAILY (every day) does not change; check any other stored intraday calendar with a holiday calendar before the release, because its indexes on weekends and holidays will change.

Monthly, weekly and offset daily calendars​

POST calendar/v1/private with a VarCalendar record, as for an intraday calendar, and one of these as calendar:

{"_type": "MonthlyCalendar", "period": 1, "dom": 32, "holidayCalendarCode": "NGE_TRADING", "rollBackwards": true}
{"_type": "WeeklyCalendar", "period": 2, "dow": 5, "refdate": "2026-01-02", "holidayCalendarCode": "NGE_TRADING", "rollBackwards": true}
{"_type": "OffsetDailyCalendar", "timezone": "Europe/London", "hour": 5, "dayOffset": 0}
FieldCalendarDescription
periodMonthly1, 2, 3, 4, 6 or 12 months, counted from January
domMonthlyThe day of the month, 1 to 31, or 32 for the last day of the month
periodWeekly1 to 52 weeks
dowWeeklyThe day of the week, 1 (Monday) to 7 (Sunday)
refdateWeeklyA date (yyyy-mm-dd) in one of the weeks the calendar keeps; needed when period is more than 1
holidayCalendarCodeMonthly, weeklyOptional. A holiday calendar; a date that is not one of its business days is moved
rollBackwardsMonthly, weeklytrue to move such a date to the business day before, false (the default) to the next business day
timezone, hourOffset dailyThe time zone, and the hour (0 to 23) each day starts in it
dayOffsetOffset dailyMoves the index of each day by this many days (-7 to 7)

A definition that breaks one of these rules is refused with the reason. The calendar's own id and name are set from the record (an offset daily calendar keeps them in code and name).

note

This release also fixes three platform bugs in these calendars:

  • Monthly calendars with a period of more than a month or a holiday calendar. The dates of a range were stepped on from the previous date after any holiday move, so they drifted (half-yearly on the last day: 2 February, then 1 September instead of 31 July), and a month whose date was moved into the next month could be missed. A range starting in a month outside the period started the period there (quarterly from February: February, May, August, November). Monthly on the 1st with holidays moved back could also loop forever when the 1st was a holiday. Each date now comes from its own month. Monthly calendars without a holiday calendar and with a period of one month give exactly the same dates as before.
  • Weekly calendars of more than two weeks, and dates before the reference date. A date off the cycle was moved by the remainder rather than to the next week of the cycle, so a 3 week calendar could land off its cycle, and before the reference date it moved backwards. Weekly calendars of one week, and of two weeks after their reference date, give the same dates as before.
  • A monthly or weekly calendar whose holiday calendar is not a holiday calendar (such as DAILY) could not be read at all. Such a holiday calendar is now ignored when the calendar is read, and refused when it is saved.

Expiry calendar​

POST expiry/v1/private

{
"_id": "NGE_TTF_EXPIRY",
"_type": "ExpiryCalendar",
"name": "TTF expiries",
"tradingCalendar": "NGE_TRADING",
"deliveryCalendar": "NGE_TRADING",
"rules": ["go to the beginning of the current month then go back 2 days using calendar for months"]
}
FieldDescription
tradingCalendarRequired. The calendar the expiry rules move on. It must exist.
deliveryCalendarOptional. Defaults to the trading calendar.
rulesAt least one plain English rule; every rule must follow the grammar

Financial expiry calendar​

POST expiry/v1/private

{
"_id": "NGE_GBPUSD_FX",
"_type": "FinancialExpiryCalendar",
"name": "GBP/USD FX forwards",
"tradingCalendar": "#UKBANKHOLIDAYS",
"crossCalendar": "NGE_USD_SETTLEMENT",
"baseCalendar": "#UKBANKHOLIDAYS",
"offset": 2
}
FieldDescription
tradingCalendarRequired. The trading (quoting) calendar.
crossCalendarRequired. The cross calendar: for an FX pair, the calendar of the other currency.
baseCalendarRequired. The base calendar.
offsetThe spot offset, 0 to 10 business days (default 0)

Each calendar must exist and be a holiday calendar (a calendar of business days). It is stored as a VarExpiryCalendar whose calendar is {"_type": "FinancialExpiryCalendar", "tradingCalendarCode", "crossCode", "baseCode", "offset"}, as before. The stored names tradingCalendarCode, crossCode and baseCode are accepted in the body too.

note

This release also fixes the start and end of financial tenors (Financial in odsl-calendar), which are the delivery start and end of financial curve contracts. They were worked out separately from the expiry and could disagree with it: spot ignored the cross and base calendars, month and year ends rolled into the next month instead of back (modified following), and end-end was missed. They now come from the financial expiry calendar, as the expiry always has. Expiries do not change. In a comparison of every tenor from SP to 5Y over two years of curve dates, a few percent of start and end dates change, each to the date of the expiry.


The calendar manager functions​

RequestBody or parametersReturns
POST calendar/v1/private?_function=validate_id (any), rulesrules: a check per rule; valid
POST expiry/v1/private?_function=validate_id (any), rulesThe same, for expiry rules
POST calendar/v1/private?_function=testA holiday calendar as for saving, plus start, end (default this year and next) and check (dates)rules (checks with count, and weekly for weekly closures), holidays, months, years, checks
GET calendar/v1/SOURCE/ID?_function=teststart, endThe same, for a stored calendar
POST expiry/v1/private?_function=testAn expiry calendar as for saving, plus ondate, tenors (default Month), start, endrules (checks with count), rows (one per contract)
GET expiry/v1/SOURCE/ID?_function=testondate, tenor (comma separated), start, endThe same, for a stored calendar
POST expiry/v1/private?_function=testA financial expiry calendar as for saving, plus ondate and codes (tenors, default SP to 10Y)valid, problems, spot, spotDow, endEnd, offset, the three calendars, rows (one per tenor: code, group, start, end, expiry, dow, days, nominal, move, endEnd, closed, error)
GET expiry/v1/SOURCE/ID?_function=test on a financial expiry calendarondate, codes (comma separated)The same, for the stored calendar
POST expiry/v1/private?_function=traceAn expiry calendar, ondate, contractThe contract's dates, and the steps of its expiry, delivery start and delivery end rules. For a financial expiry calendar, contract is a tenor (SP, ON, 3M...) and the result has spot, spotRule and expiryRule steps
GET calendar/v1/SOURCE?_function=health&_id=A,BCalendar idsA health check per calendar
GET expiry/v1/SOURCE?_function=health&_id=A,BExpiry calendar idsA health check per expiry calendar
GET calendar/v1/private?_function=access (or expiry)admin: the user is an OpenDataDSL user; publicEdit: the user can create, change and delete public calendars
GET calendar/v1/private?_function=usage-indexesforce=true to check every collection againindexes: per collection, the indexes made sure of (ok) and any that failed. Indexes the calendar fields that Calendar Usage Across Services searches: private collections for every user, public ones for OpenDataDSL users
POST calendar/v1/private?_function=testA calendar that is not a holiday calendar, as for saving (_id, name, calendar), plus start and end (days in its time zone; default the next 7 days, at most 92, for an intraday calendar)valid, problems, total, days (one per day: count, first, last, hours, holiday, dayHours), intervals (at most 3,000: local, offset, utc)
GET calendar/v1/SOURCE/ID?_function=test on a calendar that is not a holiday calendarstart, endThe same, for the stored calendar

A rule check​

FieldDescription
indexThe rule's position (from 1)
ruleThe rule text
validWhether the rule follows the grammar and could be built
kindactual, ordinal, last, every, named, penultimate; or expiry, delivery start, delivery end
errorsEach error: column (from 0), length, message and expected (the words the grammar accepts there)
detailsWhat was understood: name, validFrom, validTo, repeats, repeatStart; or periodType, limit, limitBefore

A trace​

FieldDescription
contract, absoluteThe period code asked for and its absolute code
periodStart, expiry, deliveryStart, deliveryEndThe contract's dates
expiryRule, deliveryStartRule, deliveryEndRuleEach with rule, ruleNumber and steps: step (the text of the step), date (after it) and note
notesWhat is not ruled, or could not be worked out

Who can change public calendars​

Public calendars and expiry calendars are saved with POST calendar/v1/public and POST expiry/v1/public (the Calendar Manager uses the source that matches the id: public for an id starting with #), and deleted with DELETE .../public/ID. The platform allows it for:

UserWhy
Users of the OpenDataDSL tenantThe built-in policies ManagePublic-calendar-Built-In and ManagePublic-expiry-Built-In: create, read, update and delete on the public calendar and expiry services
Users with the admin roleThe built-in policy ManageAllPublicData-Built-In, which covers every public service

Everyone else can only read public calendars: a save or delete is refused.


Extension contents​

ItemId
Extensionodsl.calendar_manager (preview until the functions above are released)
ViewCalendars, in Manage, with the Insights tab
Featureodsl.calendar_manager.manage, granted to everyone by the feature policy extension.odsl.calendar_manager
Insight reportsodsl.calendar_manager.health, odsl.calendar_manager.compare, odsl.calendar_manager.workdays, odsl.calendar_manager.changes

Troubleshooting​

What you seeWhy, and what to do
The rules could not be checkedThe platform does not have the calendar manager functions yet (the extension is a preview), or the request failed: the message gives the reason.
Choose a trading calendar to test the rulesAn expiry calendar needs its trading calendar. Pick one from the list.
Trading calendar ... Not FoundThe trading or delivery calendar id does not exist. Check the spelling and the # of public ids.
This expiry calendar has no rule for ...No rule covers that tenor: add a rule with no for, or one for that tenor.
A rule gives no holidays in the test rangeIts from and to years are outside the range, or another rule gives the same date.
A holiday is marked on a closed dayIt falls on a weekend. Add an on Saturday shift or on Sunday shift option if a substitute day is needed.
Save is not shownThe calendar is public, or another calendar with nothing to edit. Use Save as copy for a public calendar.
... is not a financial tenorA financial expiry calendar only has financial tenors: SP, ON, TN, SN, SW, or a number of weeks, months or years such as 2W, 3M, 1Y.
A month tenor of a financial expiry calendar is Closed in its base calendarMonth and year tenors are moved on the trading and cross calendars only: see Financial expiry calendars.
The interval must divide a day exactlyAn intraday interval such as 7 minutes leaves part of a day over. Choose one from the list.
... is not a time zoneUse a time zone name such as Europe/Paris, not an abbreviation such as CET.
An intraday calendar has intervals on weekendsIt has no holiday calendar, or its holidays setting is Every interval of the day (off-peak).
... is not a holiday calendarA monthly or weekly calendar's holiday calendar must have holiday rules, such as #BUSINESS, not a daily calendar.
A calendar of every N weeks needs a reference dateGive a date in one of the weeks the calendar keeps.
A quarterly calendar gives January, April, July and OctoberPeriods are counted from January, so a quarterly calendar cannot start in another month.
Request failed (403) when savingYour security policies do not allow you to update calendars, or the calendar is public and you are not an OpenDataDSL user.