Skip to content

Calendar Class

QuantLib Class | QuantLib-Python | QuantLib | 2025.04.18

1️⃣ Introduction to the Calendar Class

In the financial world, "actual tradeable days" are as important as "when". Just as the New York Stock Exchange closes on Independence Day and the Korean Exchange takes holidays during Chuseok, each country and market has unique holiday and business day rules. QuantLib's Calendar class is the essential tool for accurately handling these complex business day rules.

Just as international business must consider time zones and local holidays, global financial trading must also accurately understand and apply each market's business day rules. Mastering the Calendar class prevents date-related errors that can occur in actual financial trading and enables accurate cash flow calculations.

2️⃣ Why Business Days Are Important in Finance

All financial transactions are executed only on business days. Banks close on weekends and holidays, and exchanges don't operate, making actual fund transfers or securities trading impossible. This is not just an inconvenience but an important factor that directly affects the pricing and returns of financial instruments.

Why Business Days Are Important in Finance

  • Actual Trade Execution: Transactions are physically impossible on weekends or holidays, so they are postponed to the next business day
  • Interest Calculations: Many instruments calculate interest based on business days, requiring accurate return calculations
  • Settlement Processing: International transfers and securities settlements are only possible on business days in both countries
  • Liquidity Management: Need to understand when cash will actually be deposited or withdrawn to establish funding plans
  • Regulatory Compliance: Financial authority reporting and regulatory compliance are also done on a business day basis

For example, when a Korean company pays bond interest to US investors, both Korean and US business days must be considered for actual fund delivery. This shows how business day rules are key factors determining the feasibility of financial transactions.

3️⃣ Creating Calendar Objects

QuantLib has built-in holiday rules for major financial markets worldwide, allowing accurate reflection of actual market conditions. Like a world clock, you can easily check each country's market conditions.

✅ Major Country Calendars

Let's look at the most commonly used country calendars:

python
import QuantLib as ql

# Create major country calendars
south_korea = ql.SouthKorea()           # South Korea
united_states = ql.UnitedStates(
    ql.UnitedStates.GovernmentBond
    )  # United States
united_kingdom = ql.UnitedKingdom()     # United Kingdom  
japan = ql.Japan()                      # Japan
germany = ql.Germany()                  # Germany
china = ql.China()                      # China

print("=== Major Country Calendars ===")
calendars = [
    ("South Korea", south_korea),
    ("United States", united_states), 
    ("United Kingdom", united_kingdom),
    ("Japan", japan),
    ("Germany", germany),
    ("China", china)
]

for name, calendar in calendars:
    print(f"{name}: {calendar.name()}")

# === Major Country Calendars ===
# South Korea: South-Korea exchange
# United States: US government bond market
# United Kingdom: UK settlement
# Japan: Japan
# Germany: Frankfurt stock exchange
# China: Shanghai stock exchange

✅ Special Calendars and Options

Some countries provide different calendars for different exchanges:

python
# US supports multiple exchange-specific calendars
us_settlement = ql.UnitedStates(
    ql.UnitedStates.Settlement
    )  # General settlement
us_nyse = ql.UnitedStates(
    ql.UnitedStates.NYSE
    )  # New York Stock Exchange
us_government = ql.UnitedStates(
    ql.UnitedStates.GovernmentBond
    )  # Government bond market

print("=== US Exchange-Specific Calendars ===")
us_calendars = [
    ("General Settlement", us_settlement),
    ("NYSE", us_nyse),
    ("Government Bond", us_government)
]

for name, calendar in us_calendars:
    print(f"{name}: {calendar.name()}")

# Null calendar (no holidays - for testing)
null_calendar = ql.NullCalendar()
print(f"Null calendar: {null_calendar.name()}")

# === US Exchange-Specific Calendars ===
# General Settlement: US settlement
# NYSE: New York stock exchange
# Government Bond: US government bond market
# Null calendar: Null

✅ Creating Joint Calendars

Often you need to consider holidays from multiple countries simultaneously:

python
# Create joint calendar - applying both Korean and US holidays
korea_us_joint = ql.JointCalendar(south_korea, united_states)

print(f"Korea-US joint calendar: {korea_us_joint.name()}")

# Apply multiple countries simultaneously
multi_calendar = ql.JointCalendar(
    ql.JointCalendar(south_korea, united_states),
    united_kingdom
)

print(f"Korea-US-UK joint calendar: {multi_calendar.name()}")

# Korea-US joint calendar: 
# JoinHolidays(South-Korea exchange, US government bond market)
# Korea-US-UK joint calendar: 
# JoinHolidays(JoinHolidays(South-Korea exchange, US government bond market), UK settlement)

4️⃣ Practical Example 1: International Bond Settlement Date Calculation

In international bond trading, you must consider the business days of both the issuing country and the investor's country of residence. Let's simulate an actual situation where a US investor purchases a dollar-denominated bond issued by a Korean company.

python
import QuantLib as ql

# Scenario: Trading Korean company's dollar bond in the US
trade_date = ql.Date(5, 7, 2025)  # Trade execution date (Saturday)
weekdays = ['Sunday', 
            'Monday', 
            'Tuesday', 
            'Wednesday', 
            'Thursday', 
            'Friday', 
            'Saturday']
print(f"Trade execution date: {trade_date} ({weekdays[trade_date.weekday()-1]})")

# Set up related calendars
korea_cal = ql.SouthKorea()
us_cal = ql.UnitedStates(ql.UnitedStates.GovernmentBond)
joint_cal = ql.JointCalendar(korea_cal, us_cal)

print(f"\n=== Business Day Check ===")
print(f"Korean business day? {korea_cal.isBusinessDay(trade_date)}")
print(f"US business day? {us_cal.isBusinessDay(trade_date)}")  
print(f"Joint business day? {joint_cal.isBusinessDay(trade_date)}")

# Apply various business day adjustment rules
adjustment_rules = [
    ("Following", ql.Following),           # Next business day
    ("Preceding", ql.Preceding),           # Previous business day  
    ("Modified Following", ql.ModifiedFollowing),  # Modified next business day
    ("Modified Preceding", ql.ModifiedPreceding)   # Modified previous business day
]

print(f"\n=== Settlement Date Adjustment (Korean basis) ===")
for rule_name, rule in adjustment_rules:
    adjusted_date = korea_cal.adjust(trade_date, rule)
    print(f"{rule_name:18}: {adjusted_date}")

print(f"\n=== Settlement Date Adjustment (US basis) ===")  
for rule_name, rule in adjustment_rules:
    adjusted_date = us_cal.adjust(trade_date, rule)
    print(f"{rule_name:18}: {adjusted_date}")

print(f"\n=== Settlement Date Adjustment (Joint basis) ===")
for rule_name, rule in adjustment_rules:
    adjusted_date = joint_cal.adjust(trade_date, rule) 
    print(f"{rule_name:18}: {adjusted_date}")

# T+2 settlement (2 business days after trade date)
settlement_days = 2
korea_settlement = korea_cal.advance(
    trade_date, settlement_days, ql.Days, ql.ModifiedFollowing
    )
us_settlement = us_cal.advance(
    trade_date, settlement_days, ql.Days, ql.ModifiedFollowing
    )
joint_settlement = joint_cal.advance(
    trade_date, settlement_days, ql.Days, ql.ModifiedFollowing
    )

print(f"\n=== T+{settlement_days} Settlement Date ===")
print(f"Korean basis: {korea_settlement}")
print(f"US basis: {us_settlement}")
print(f"Joint basis: {joint_settlement}")

# Calculate actual business days
korea_biz_days = korea_cal.businessDaysBetween(
    trade_date, korea_settlement
    )
us_biz_days = us_cal.businessDaysBetween(
    trade_date, us_settlement
    ) 
joint_biz_days = joint_cal.businessDaysBetween(
    trade_date, joint_settlement
    )

print(f"\n=== Actual Business Days ===")
print(f"Korean basis: {korea_biz_days} days")
print(f"US basis: {us_biz_days} days")
print(f"Joint basis: {joint_biz_days} days")

# Trade execution date: July 5th, 2025 (Saturday)

# === Business Day Check ===
# Korean business day? False
# US business day? False  
# Joint business day? False

# === Settlement Date Adjustment (Korean basis) ===
# Following         : July 7th, 2025
# Preceding         : July 4th, 2025
# Modified Following: July 7th, 2025
# Modified Preceding: July 4th, 2025

# === Settlement Date Adjustment (US basis) ===  
# Following         : July 7th, 2025
# Preceding         : July 3rd, 2025
# Modified Following: July 7th, 2025
# Modified Preceding: July 3rd, 2025

# === Settlement Date Adjustment (Joint basis) ===
# Following        : July 7th, 2025
# Preceding        : July 3rd, 2025
# Modified Following: July 7th, 2025
# Modified Preceding: July 3rd, 2025

# === T+2 Settlement Date ===
# Korean basis: July 8th, 2025
# US basis: July 8th, 2025
# Joint basis: July 8th, 2025

# === Actual Business Days ===
# Korean basis: 1 days
# US basis: 1 days
# Joint basis: 1 days

This example shows the complexity of having to consider multiple countries' business day rules simultaneously in international financial transactions. You can especially see how standard settlement practices like T+2 vary according to each country's holidays.

5️⃣ Practical Example 2: Monthly Interest Payment Date Management

Financial instruments with regular cash flows need to adjust monthly or quarterly payment dates to business days. Let's look at how to manage actual corporate bond monthly interest payment schedules using the Calendar class.

python
import QuantLib as ql

# Corporate bond interest payment schedule setup
bond_issue_date = ql.Date(15, 1, 2025)  # Bond issue date
maturity_years = 3
monthly_payment_day = 15  # Pay on 15th of each month

print(f"Bond issue date: {bond_issue_date}")
print(f"Bond maturity: {maturity_years} years")  
print(f"Interest payment date: {monthly_payment_day}th of each month")

# Use Korean calendar
korea_cal = ql.SouthKorea()

def generate_payment_schedule(start_date, 
                              years, 
                              payment_day, 
                              calendar, 
                              adjustment_rule=ql.ModifiedFollowing):
    """Function to generate monthly interest payment schedule"""
    payment_dates = []
    
    current_year = start_date.year()
    current_month = start_date.month()
    end_year = current_year + years
    
    # First payment starts from next month
    if current_month == 12:
        current_year += 1
        current_month = 1
    else:
        current_month += 1
    
    payment_count = 0
    while current_year < end_year or (current_year == end_year and current_month <= start_date.month()):
        # Generate payment date for the month
        try:
            payment_date = ql.Date(payment_day, 
                                   current_month, 
                                   current_year)
            
            # Adjust to business day
            adjusted_date = calendar.adjust(payment_date,
                                            adjustment_rule)
            
            payment_dates.append({
                'sequence': payment_count + 1,
                'original': payment_date,
                'adjusted': adjusted_date,
                'is_holiday': not calendar.isBusinessDay(payment_date),
                'adjustment_days': (adjusted_date - payment_date)
            })
            
            payment_count += 1
            
        except Exception as e:
            # Skip non-existent dates like February 30th
            pass
        
        # Move to next month
        if current_month == 12:
            current_year += 1
            current_month = 1
        else:
            current_month += 1
    
    return payment_dates

# Generate interest payment schedule
payment_schedule = generate_payment_schedule(
    bond_issue_date, maturity_years, monthly_payment_day, korea_cal
)

print(f"\n=== Monthly Interest Payment Schedule (First 12 months) ===")
print(f"{'No.':>3} {'Original Date':>15} {'Adjusted Date':>15} {'Holiday?':>8} {'Adj Days':>8}")
print("-" * 60)

for i, payment in enumerate(payment_schedule[:12]):
    holiday_mark = "Yes" if payment['is_holiday'] else "No"
    print(f"{payment['sequence']:3d} {payment['original']} {payment['adjusted']} "
          f"{holiday_mark:>8} {payment['adjustment_days']:>8}")

# Statistics for holiday adjustments
adjusted_payments = [p for p in payment_schedule if p['adjustment_days'] != 0]
print(f"\n=== Adjustment Statistics ===")
print(f"Total payments: {len(payment_schedule)} times")
print(f"Adjusted payments: {len(adjusted_payments)} times")
print(f"Adjustment ratio: {len(adjusted_payments)/len(payment_schedule)*100:.1f}%")

if adjusted_payments:
    print(f"\n=== Adjusted Payment Details ===")
    for payment in adjusted_payments[:10]:  # Show only first 10
        print(f"Payment {payment['sequence']:2d}: {payment['original']} → "
              f"{payment['adjusted']} ({payment['adjustment_days']} days adjusted)")

# Check specific holidays
special_dates = [
    ("New Year", ql.Date(1, 1, 2025)),
    ("Lunar New Year", ql.Date(29, 1, 2025)),  # 2025 Lunar New Year holiday
    ("Children's Day", ql.Date(5, 5, 2025)),
    ("Liberation Day", ql.Date(15, 8, 2025)),
    ("Chuseok", ql.Date(6, 10, 2025)),   # 2025 Chuseok
    ("Christmas", ql.Date(25, 12, 2025))
]

print(f"\n=== Korean Major Holiday Check ===")
for holiday_name, date in special_dates:
    is_holiday = not korea_cal.isBusinessDay(date)
    next_business = korea_cal.adjust(date, ql.Following)
    print(f"{holiday_name:12}: {date} - {'Holiday' if is_holiday else 'Business Day'}")
    if is_holiday:
        print(f"{'':15} Next business day: {next_business}")

# Bond issue date: January 15th, 2025
# Bond maturity: 3 years
# Interest payment date: 15th of each month

# === Monthly Interest Payment Schedule (First 12 months) ===
# No.    Original Date    Adjusted Date Holiday? Adj Days
# ------------------------------------------------------------
#   1 February 15th, 2025 February 17th, 2025      Yes        2
#   2 March 15th, 2025 March 17th, 2025      Yes        2  
#   3 April 15th, 2025 April 15th, 2025       No        0
#   4 May 15th, 2025 May 15th, 2025       No        0
#   5 June 15th, 2025 June 16th, 2025      Yes        1
#   ...

# === Adjustment Statistics ===
# Total payments: 36 times
# Adjusted payments: 13 times  
# Adjustment ratio: 36.1%
# ...

# === Korean Major Holiday Check ===
# Chuseok      : October 6th, 2025 - Holiday
#                Next business day: October 10th, 2025
# Christmas    : December 25th, 2025 - Holiday
#                Next business day: December 26th, 2025
# ...

This example shows how important the Calendar class is in managing regular cash flows. Even though it's simply set to the 15th of each month, you can see that about half are actually adjusted due to holidays.

6️⃣ Integration with Other Classes

The Calendar class works with almost all time-related classes in QuantLib to ensure actual tradeable dates. It plays a key role in adjusting dates created by other classes to match actual market conditions.

✅ Integration with Schedule

The Schedule class uses Calendar to generate regular schedules based on business days:

python
import QuantLib as ql

# Using Calendar when creating Schedule
korea_cal = ql.SouthKorea()

# Generate quarterly schedule based on Korean business days
schedule = ql.Schedule(
    ql.Date(1, 1, 2025),         # Start date
    ql.Date(1, 1, 2026),         # End date
    ql.Period(3, ql.Months),     # 3-month period
    korea_cal,                   # Korean calendar <- Using Calendar
    ql.ModifiedFollowing,        # Business day adjustment rule
    ql.ModifiedFollowing,
    ql.DateGeneration.Forward,
    False
)

print("Quarterly schedule based on Korean business days:")
for i, date in enumerate(schedule):
    if i < 5:
        is_business = korea_cal.isBusinessDay(date)
        print(f"{i+1}: {date} ({'Business day' if is_business else 'Holiday'})")

# Quarterly schedule based on Korean business days:
# 1: January 2nd, 2025 (Business day)
# 2: April 1st, 2025 (Business day)  
# 3: July 1st, 2025 (Business day)
# 4: October 1st, 2025 (Business day)
# 5: January 2nd, 2026 (Business day)

✅ Integration with DayCounter

Some DayCounters include only business days in calculations:

python
# DayCounter that calculates only business days
business_252 = ql.Business252(korea_cal)  # Based on Korean business days

start_date = ql.Date(1, 3, 2025)
end_date = ql.Date(1, 6, 2025)

# Regular day counting vs business day counting
actual_days = end_date - start_date
business_days = korea_cal.businessDaysBetween(start_date, end_date)
year_fraction = business_252.yearFraction(start_date, end_date)

print(f"Period: {start_date} ~ {end_date}")
print(f"Actual days: {actual_days} days")
print(f"Business days: {business_days} days")  
print(f"Year fraction: {year_fraction:.4f}")

# Period: March 1st, 2025 ~ June 1st, 2025
# Actual days: 92 days
# Business days: 61 days
# Year fraction: 0.2421

7️⃣ Precautions and Practical Tips

Precautions When Using Calendar

  • Holiday Data Currency
    QuantLib's holiday data is based on the time of code writing, so newly designated or changed holidays may not be reflected. For important transactions, you should separately verify the latest holiday information.

  • Variability of Religious/Cultural Holidays
    Holidays determined by lunar calendar or calculation formulas like Lunar New Year, Chuseok, Easter may have different dates each year, and sometimes QuantLib's calculations may differ from reality.

  • Handling Temporary Holidays
    Temporary holidays due to national celebrations or special circumstances are not reflected in QuantLib, so separate consideration is needed.

Performance Optimization Tips

  • Reuse Calendars: Don't repeatedly create the same calendar; create once and reuse
  • Minimize Joint Calendars: Unless absolutely necessary, don't combine multiple countries' calendars
  • Cache Business Day Calculations: It's efficient to pre-calculate and store business day counts for frequently used date ranges

Practical Usage Tips

  • Market Priority: Apply the main trading market's calendar first, then consider other markets' calendars as needed
  • Test Scenarios: Always test special periods like year-end/New Year and long holidays
  • Documentation: Clearly document which calendar and adjustment rules were used
  • Backup Plan: Prepare manual adjustment methods in case there are problems with the calendar system

8️⃣ Summary and Next Steps

The Calendar class is an essential tool for converting theoretical date calculations to actual tradeable business days, essential for accurate financial transactions in practice. Summary of what we learned in this section:

  • Calendar Object Creation: Country-specific calendars, joint calendars, special calendar usage
  • Business Day Adjustment: Date adjustment through Following, Preceding, Modified rules
  • Practical Applications: International bond settlement, regular interest payment date management
  • Integration with Other Classes: Integrated use with Schedule, DayCounter

In the next step, we will learn the DayCounter class to understand various day counting methods and year fraction calculations used in finance. DayCounter is a class that calculates precise time fractions needed for interest calculations, bond pricing, option maturity calculations, etc., based on the accurate business days provided by Calendar.

Additional Learning Resources

글쓴이의 글이 도움이 되었다면 많은 응원 부탁드립니다. -> 아래 광고 보기 ❤️

If you found this content helpful, please support the author. -> To see below ads ❤️



Made by haun with ❤️