Skip to content

Multiple changesets

On top of changes to single days and single calendars, it is also possible to apply changes to an entire set of calendars in bulk via a ChangeSetDeltaDict = dict[str, ChangeSetDelta].

Applying multiple changesets

Use change_calendars() to apply multiple changesets at once.

import exchange_calendars as ec
import exchange_calendars_extensions as ecx
from exchange_calendars_extensions.changes import (
    DayChange,
    BusinessDaySpec,
    NonBusinessDaySpec,
)

ecx.apply_extensions()

calendar_xlon = ec.get_calendar("XLON")
calendar_xetr = ec.get_calendar("XETR")

assert "2022-12-27" in calendar_xlon.holidays_all.holidays()
assert "2022-12-28" not in calendar_xlon.holidays_all.holidays()
assert "2022-12-17" in calendar_xetr.weekend_days.holidays()

changeset_dict = {
    "XLON": {
        "2022-12-27": DayChange(spec=BusinessDaySpec()),
        "2022-12-28": DayChange(spec=NonBusinessDaySpec(holiday=True), name="Holiday"),
    },
    "XETR": {
        "2022-12-17": DayChange(spec=BusinessDaySpec()),
    }
}

ecx.change_calendars(changeset_dict)

calendar_xlon = ec.get_calendar("XLON")
calendar_xetr = ec.get_calendar("XETR")

assert "2022-12-27" not in calendar_xlon.holidays_all.holidays()
assert "2022-12-28" in calendar_xlon.holidays_all.holidays()
assert "2022-12-17" not in calendar_xetr.weekend_days.holidays()

Modes

change_calendars() also accepts an optional mode argument. The modes merge, update and replace work the same as for change_calendar() and apply at the calendar level. Additionally, the modereplace_all clears out all existing changesets for all calendars before applying the new ones.

import pandas as pd
import exchange_calendars as ec
import exchange_calendars_extensions as ecx
from exchange_calendars_extensions.changes import (
    DayChange,
    BusinessDaySpec,
    NonBusinessDaySpec,
)

ecx.apply_extensions()

changeset_dict = {
    "XLON": {
        "2022-12-27": DayChange(spec=BusinessDaySpec()),
        "2022-12-28": DayChange(spec=NonBusinessDaySpec(holiday=True), name="Holiday"),
    },
    "XETR": {
        "2022-12-17": DayChange(spec=BusinessDaySpec()),
    }
}

ecx.change_calendars(changeset_dict)

changeset_dict_2 = {
    "XWBO": {
        pd.Timestamp("2022-12-17"): DayChange(spec=BusinessDaySpec()),
    }
}

ecx.change_calendars(changeset_dict_2, mode="replace_all")

assert ecx.get_changes() == changeset_dict_2

Clearing changes

Similar to change_calendar(), pass an empty dictionary and mode="replace_all" to remove all changesets for all calendars.

import exchange_calendars as ec
import exchange_calendars_extensions as ecx
from exchange_calendars_extensions.changes import (
    DayChange,
    BusinessDaySpec,
    NonBusinessDaySpec,
)

ecx.apply_extensions()

changeset_dict = {
    "XLON": {
        "2022-12-27": DayChange(spec=BusinessDaySpec()),
        "2022-12-28": DayChange(spec=NonBusinessDaySpec(holiday=True), name="Holiday"),
    },
    "XETR": {
        "2022-12-17": DayChange(spec=BusinessDaySpec()),
    }
}

ecx.change_calendars(changeset_dict)

ecx.change_calendars({}, mode="replace_all")

assert ecx.get_changes() == {}

For convenience, remove_changes(), called without any argument, also clears all changesets to the same effect.

Retrieving changes

Use get_changes() (without any argument) to retrieve the changesets for all exchanges.

from pydantic import TypeAdapter
import exchange_calendars as ec
import exchange_calendars_extensions as ecx
from exchange_calendars_extensions import (
    DayChange,
    BusinessDaySpec,
    NonBusinessDaySpec,
    ChangeSetDict
)

ecx.apply_extensions()

changeset_dict = {
    "XLON": {
        "2022-12-27": DayChange(spec=BusinessDaySpec()),
        "2022-12-28": DayChange(spec=NonBusinessDaySpec(holiday=True), name="Holiday"),
    },
    "XETR": {
        "2022-12-17": DayChange(spec=BusinessDaySpec()),
    }
}

ecx.change_calendars(changeset_dict)

changes: ChangeSetDict = ecx.get_changes()

ta = TypeAdapter(ChangeSetDict)

print(ta.dump_json(changes, indent=2).decode())

{
  "XLON": {
    "2022-12-27 00:00:00": {
      "type": "change",
      "spec": {
        "business_day": true
      }
    },
    "2022-12-28 00:00:00": {
      "type": "change",
      "spec": {
        "business_day": false,
        "holiday": true
      },
      "name": "Holiday"
    }
  },
  "XETR": {
    "2022-12-17 00:00:00": {
      "type": "change",
      "spec": {
        "business_day": true
      }
    }
  }
}

Serialization & Deserialization

As with individual changesets, serialization and deserialization work via Pydantic.

from pydantic import TypeAdapter
import exchange_calendars as ec
import exchange_calendars_extensions as ecx
from exchange_calendars_extensions import (
    DayChange,
    BusinessDaySpec,
    NonBusinessDaySpec,
    ChangeSetDeltaDict
)

ecx.apply_extensions()

changeset_dict = {
    "XLON": {
        "2022-12-27": DayChange(spec=BusinessDaySpec()),
        "2022-12-28": DayChange(spec=NonBusinessDaySpec(holiday=True), name="Holiday"),
    },
    "XETR": {
        "2022-12-17": DayChange(spec=BusinessDaySpec()),
    }
}

ta = TypeAdapter(ChangeSetDeltaDict)

# Serialize changeset.
serialized = ta.dump_json(changeset_dict)

# Deserialize again.
changeset_dict_2 = ta.validate_json(serialized)

ecx.change_calendars(changeset_dict_2, mode="replace_all")

print(ta.dump_json(ecx.get_changes(), indent=2).decode())

{
  "XLON": {
    "2022-12-27 00:00:00": {
      "type": "change",
      "spec": {
        "business_day": true
      }
    },
    "2022-12-28 00:00:00": {
      "type": "change",
      "spec": {
        "business_day": false,
        "holiday": true
      },
      "name": "Holiday"
    }
  },
  "XETR": {
    "2022-12-17 00:00:00": {
      "type": "change",
      "spec": {
        "business_day": true
      }
    }
  }
}