Schedule 클래스
QuantLib Class | QuantLib-Python | QuantLib | 2025.04.22
1️⃣ Schedule 클래스 소개
채권 한 종목만 해도 수십 번의 이자지급일이 존재합니다. 3년 만기 채권이 분기마다 이자를 지급한다면 총 12개의 지급일을 하나하나 계산해야 하는데, 여기에 주말·휴일 조정 규칙까지 적용하면 손으로 처리하기 어렵습니다. QuantLib의 Schedule 클래스는 시작일, 만기일, 주기, 달력, 영업일 조정 규칙을 받아 현금흐름 날짜 시퀀스를 자동으로 생성하는 핵심 도구입니다.
마치 공장의 생산 일정표처럼, Schedule 클래스는 금융 상품의 모든 현금흐름이 발생하는 날짜를 체계적으로 관리합니다. 앞서 배운 Date, Period, Calendar, DayCounter 네 클래스를 하나로 통합하여 실무 수준의 금융 일정 계산을 가능하게 하는 클래스입니다.
2️⃣ 금융에서 일정 생성이 중요한 이유
금융 상품의 현금흐름은 미래의 특정 날짜들에 집중되어 있습니다. 가격 책정, 리스크 측정, 회계 처리 등 모든 금융 분석은 이 날짜들을 정확히 파악하는 것에서 시작됩니다.
금융에서 일정 관리가 중요한 이유
- 현금흐름 할인: 각 지급일까지의 기간을 정확히 알아야 현재가치(PV)를 계산할 수 있습니다
- 미래 준비: 이자지급일에 앞서 충분한 유동성을 확보하려면 일정을 사전에 파악해야 합니다
- 회계 처리: 발생주의 회계에서는 어느 날짜에 수익/비용이 귀속되는지가 결정적입니다
- 리스크 관리: 듀레이션·컨벡시티 계산에 현금흐름 날짜가 직접 사용됩니다
- 규제 보고: 금융 당국의 보고서는 정확한 지급일 정보를 요구합니다
예를 들어, 3년 만기 채권 두 종목이 동일한 표면금리를 가지더라도 이자지급 주기(월별 vs 반기별)와 휴일 처리 방식에 따라 가격이 달라집니다. 일정을 잘못 계산하면 가격 오차, 결제 실패, 규제 위반으로 이어질 수 있습니다.
3️⃣ Schedule 객체 생성하기
Schedule 객체는 여러 매개변수를 조합하여 생성하며, 각 매개변수는 앞서 학습한 클래스들을 활용합니다. 처음에는 매개변수가 많아 보이지만, 하나씩 이해하면 매우 직관적입니다.
✅ 기본 생성 방법
가장 일반적인 방법은 7개의 필수 매개변수를 직접 지정하는 것입니다:
import QuantLib as ql
# 기본 Schedule 생성
schedule = ql.Schedule(
ql.Date(1, 1, 2025), # effectiveDate: 효력발생일
ql.Date(1, 1, 2027), # terminationDate: 만기일
ql.Period(6, ql.Months), # tenor: 지급 주기 (6개월)
ql.SouthKorea(), # calendar: 달력 (한국)
ql.ModifiedFollowing, # convention: 영업일 조정 규칙
ql.ModifiedFollowing, # terminationDateConvention: 만기일 조정 규칙
ql.DateGeneration.Forward, # rule: 날짜 생성 방향
False # endOfMonth: 월말 고정 여부
)
# 생성된 날짜 출력
print(f"총 날짜 수: {len(schedule)}")
for i, date in enumerate(schedule):
print(f" {i}: {date}")
# 총 날짜 수: 5
# 0: January 2nd, 2025 ← 효력발생일 (1/1 휴일 → 조정)
# 1: July 1st, 2025
# 2: January 2nd, 2026
# 3: July 1st, 2026
# 4: January 2nd, 2027 ← 만기일 (1/1 휴일 → 조정)✅ 영업일 조정 규칙(BusinessDayConvention) 이해하기
영업일 조정 규칙은 휴일이나 주말에 해당하는 날짜를 어느 방향으로 밀지를 결정합니다:
import QuantLib as ql
# 2025년 5월 3일(토요일)을 각 규칙으로 조정
test_date = ql.Date(3, 5, 2025) # 토요일
calendar = ql.SouthKorea()
conventions = {
"Following": ql.Following,
"ModifiedFollowing": ql.ModifiedFollowing,
"Preceding": ql.Preceding,
"ModifiedPreceding": ql.ModifiedPreceding,
"Unadjusted": ql.Unadjusted,
}
print(f"원래 날짜: {test_date} (토요일)")
print()
for name, conv in conventions.items():
adjusted = calendar.adjust(test_date, conv)
print(f" {name:25s}: {adjusted}")
# 원래 날짜: May 3rd, 2025 (토요일)
#
# Following : May 5th, 2025 ← 다음 영업일
# ModifiedFollowing : May 2nd, 2025 ← 같은 달 다음 영업일 없으면 이전
# Preceding : May 2nd, 2025 ← 이전 영업일
# ModifiedPreceding : May 5th, 2025 ← 같은 달 이전 영업일 없으면 다음
# Unadjusted : May 3rd, 2025 ← 조정 없음✅ 날짜 생성 방향(DateGeneration) 이해하기
날짜를 시작일에서 만기일 방향으로 생성할지, 반대로 만기일에서 시작일 방향으로 생성할지를 지정합니다:
import QuantLib as ql
start = ql.Date(15, 1, 2025)
end = ql.Date(15, 10, 2025)
tenor = ql.Period(3, ql.Months)
cal = ql.SouthKorea()
conv = ql.ModifiedFollowing
# Forward: 시작일 → 만기일
fwd_schedule = ql.Schedule(
start, end, tenor, cal, conv, conv,
ql.DateGeneration.Forward, False
)
# Backward: 만기일 → 시작일 (만기일 고정, 중간 날짜를 역방향으로 계산)
bwd_schedule = ql.Schedule(
start, end, tenor, cal, conv, conv,
ql.DateGeneration.Backward, False
)
print("Forward 방식:")
for d in fwd_schedule:
print(f" {d}")
print("\nBackward 방식:")
for d in bwd_schedule:
print(f" {d}")
# Forward 방식:
# January 15th, 2025
# April 15th, 2025
# July 15th, 2025
# October 15th, 2025
# Backward 방식:
# January 15th, 2025
# April 15th, 2025
# July 15th, 2025
# October 15th, 2025
# ※ 이 예제에서는 결과가 동일하지만,
# 비정형 첫 번째/마지막 구간(odd coupon)이 있을 때 차이가 납니다4️⃣ 실용 예제 1: 국고채 이자지급 일정 생성
한국 국고채는 반기(6개월)마다 이자를 지급합니다. 실제 국고채 발행 조건에 맞춰 Schedule을 생성하고 각 지급일과 이자 금액을 계산해보겠습니다.
import QuantLib as ql
# ────────────────────────────────────────────
# 국고채 발행 조건
# ────────────────────────────────────────────
issue_date = ql.Date(10, 4, 2025) # 발행일
maturity_date = ql.Date(10, 4, 2030) # 만기일 (5년)
coupon_rate = 0.0350 # 표면금리 3.50%
face_value = 100_000_000 # 액면가 1억원
tenor = ql.Period(6, ql.Months)
calendar = ql.SouthKorea()
convention = ql.ModifiedFollowing
day_counter = ql.ActualActual(ql.ActualActual.ISMA)
# Schedule 생성
schedule = ql.Schedule(
issue_date,
maturity_date,
tenor,
calendar,
convention,
convention,
ql.DateGeneration.Backward, # 만기일 기준 역방향 생성 (국채 관행)
False
)
# ────────────────────────────────────────────
# 지급 일정 출력
# ────────────────────────────────────────────
print(f"국고채 이자지급 일정 (액면 {face_value:,}원, 연 {coupon_rate*100:.2f}%)")
print(f"{'회차':>4} {'지급일':20} {'구간 일수':>8} {'이자금액':>12}")
print("-" * 55)
prev_date = issue_date
total_coupon = 0
for i, date in enumerate(schedule):
if i == 0:
prev_date = date
continue
# 실제 경과 일수 기반 이자 계산
year_fraction = day_counter.yearFraction(prev_date, date)
coupon = face_value * coupon_rate * year_fraction
total_coupon += coupon
days = date - prev_date
print(f" {i:>2}회 {str(date):20} {days:>8}일 {coupon:>12,.0f}원")
prev_date = date
print("-" * 55)
print(f"{'만기 수령 총 이자':>36} {total_coupon:>12,.0f}원")
print(f"{'원금':>36} {face_value:>12,}원")
print(f"{'총 수령액':>36} {total_coupon + face_value:>12,.0f}원")
# 국고채 이자지급 일정 (액면 100,000,000원, 연 3.50%)
# 회차 지급일 구간 일수 이자금액
# -------------------------------------------------------
# 1회 October 10th, 2025 183일 1,753,425원
# 2회 April 10th, 2026 182일 1,743,836원
# 3회 October 13th, 2026 186일 1,782,192원
# ...
# 10회 April 10th, 2030 181일 1,734,247원
# -------------------------------------------------------
# 만기 수령 총 이자 17,500,685원
# 원금 100,000,000원
# 총 수령액 117,500,685원이 예제에서 볼 수 있듯이, 같은 이자율이라도 구간마다 실제 일수가 다르기 때문에 지급 금액이 조금씩 다릅니다. Schedule과 DayCounter를 함께 사용하면 이러한 미세한 차이를 정확하게 반영할 수 있습니다.
5️⃣ 실용 예제 2: 이자율 스왑 지급 일정 생성
이자율 스왑(IRS)은 고정금리와 변동금리를 교환하는 계약으로, 두 다리(Leg)의 지급 일정이 다른 것이 특징입니다. 고정금리는 반기 지급, 변동금리는 분기 지급인 일반적인 구조를 구현해보겠습니다.
import QuantLib as ql
# ────────────────────────────────────────────
# 스왑 기본 조건
# ────────────────────────────────────────────
effective_date = ql.Date(20, 6, 2025)
termination_date = ql.Date(20, 6, 2030)
notional = 10_000_000_000 # 명목원금 100억원
calendar = ql.SouthKorea()
convention = ql.ModifiedFollowing
def build_schedule(tenor):
"""지급 주기만 다른 Schedule을 생성하는 헬퍼 함수"""
return ql.Schedule(
effective_date,
termination_date,
ql.Period(tenor, ql.Months),
calendar,
convention,
convention,
ql.DateGeneration.Forward,
False
)
# 고정금리 다리 (반기, 6개월)
fixed_schedule = build_schedule(6)
# 변동금리 다리 (분기, 3개월)
float_schedule = build_schedule(3)
print(f"스왑 개요: {effective_date} ~ {termination_date}")
print(f"명목원금: {notional:,}원\n")
# ────────────────────────────────────────────
# 고정금리 다리 출력
# ────────────────────────────────────────────
fixed_rate = 0.0380 # 고정금리 3.80%
day_counter = ql.Thirty360(ql.Thirty360.BondBasis)
print(f"[고정금리 다리] 연 {fixed_rate*100:.2f}%, 반기 지급 — 총 {len(fixed_schedule)-1}회")
print(f"{'회차':>4} {'지급일':20} {'고정금리 지급액':>16}")
print("-" * 45)
prev = effective_date
total_fixed = 0
for i, date in enumerate(fixed_schedule):
if i == 0:
prev = date
continue
yf = day_counter.yearFraction(prev, date)
payment = notional * fixed_rate * yf
total_fixed += payment
print(f" {i:>2}회 {str(date):20} {payment:>16,.0f}원")
prev = date
print(f"{'합계':>27} {total_fixed:>16,.0f}원\n")
# ────────────────────────────────────────────
# 변동금리 다리 (금리 미확정, 일정만 출력)
# ────────────────────────────────────────────
print(f"[변동금리 다리] 3M CD금리 연동, 분기 지급 — 총 {len(float_schedule)-1}회")
print(f"{'회차':>4} {'리셋일(시작)':20} {'지급일(종료)':20}")
print("-" * 50)
dates = list(float_schedule)
for i in range(1, len(dates)):
print(f" {i:>2}회 {str(dates[i-1]):20} {str(dates[i]):20}")
# 스왑 개요: June 20th, 2025 ~ June 20th, 2030
# 명목원금: 10,000,000,000원
#
# [고정금리 다리] 연 3.80%, 반기 지급 — 총 10회
# 회차 지급일 고정금리 지급액
# ---------------------------------------------
# 1회 December 22nd, 2025 190,000,000원
# 2회 June 22nd, 2026 190,000,000원
# ...
# 10회 June 20th, 2030 190,000,000원
# 합계 1,900,000,000원
#
# [변동금리 다리] 3M CD금리 연동, 분기 지급 — 총 20회
# 회차 리셋일(시작) 지급일(종료)
# --------------------------------------------------
# 1회 June 20th, 2025 September 22nd, 2025
# 2회 September 22nd, 2025 December 22nd, 2025
# ...
# 20회 March 20th, 2030 June 20th, 2030이 예제는 스왑의 두 다리가 서로 다른 주기를 가지면서도 일관된 영업일 조정 규칙이 적용되는 것을 보여줍니다. 실무에서는 이 일정을 기반으로 각 구간의 변동금리를 조회하고 순 지급액(Net Payment)을 계산하게 됩니다.
6️⃣ 다른 클래스와의 연계
Schedule 클래스는 앞서 배운 네 클래스를 모두 흡수하여 금융 상품 모델링의 진입점 역할을 합니다. Schedule이 만들어지면 이를 기반으로 채권, 스왑, 플로터 등 고수준 상품 클래스를 바로 구성할 수 있습니다.
✅ FixedRateBond와의 연계
Schedule은 QuantLib의 채권 클래스에 직접 전달됩니다:
import QuantLib as ql
# Schedule 생성
schedule = ql.Schedule(
ql.Date(10, 4, 2025),
ql.Date(10, 4, 2028),
ql.Period(6, ql.Months),
ql.SouthKorea(),
ql.ModifiedFollowing,
ql.ModifiedFollowing,
ql.DateGeneration.Backward,
False
)
# FixedRateBond 생성 — Schedule을 그대로 전달
bond = ql.FixedRateBond(
1, # 결제 지연 일수 (T+1)
100.0, # 액면가
schedule, # ← Schedule 전달
[0.035], # 표면금리 3.5%
ql.ActualActual(ql.ActualActual.ISMA)
)
print("채권 현금흐름:")
for cf in bond.cashflows():
print(f" {cf.date()} : {cf.amount():>10.2f}원")
# 채권 현금흐름:
# October 10th, 2025 : 1753.42원
# April 10th, 2026 : 1743.84원
# ...
# April 10th, 2028 : 101734.25원 ← 원금 포함✅ 클래스 연계 구조
Date ──┐
Period ├──► Schedule ──► FixedRateBond
Calendar├──► ──► FloatingRateBond
Conv ─┘ ──► VanillaSwap
──► CapFloor
DayCounter ──────────────► 이자 계산- Date: 시작일·만기일 제공
- Period: 지급 주기 제공
- Calendar: 휴일 목록·영업일 판단
- BusinessDayConvention: 휴일 충돌 시 조정 방향
- Schedule: 위 요소를 통합하여 날짜 배열 생성
- DayCounter: Schedule의 각 구간을 연분율로 환산
7️⃣ 주의사항 및 실무 팁
Schedule 생성 시 주의사항
- Forward vs Backward: 시장 관행에 맞는 방향을 선택하세요. 국채·스왑은 대부분
Backward(만기일 고정), 단기 상품은Forward를 씁니다. - endOfMonth 옵션:
True로 설정하면 모든 지급일이 해당 월의 마지막 영업일로 고정됩니다. 월말(EOM) 채권에서 사용하며, 일반 채권에는False가 맞습니다. - 만기일 조정 규칙 분리:
terminationDateConvention을convention과 다르게 설정할 수 있습니다. 만기일은 계약서에 명시된 날짜를 그대로 사용해야 하는 경우Unadjusted를 씁니다.
흔한 실수
- Calendar 불일치: 스왑에서 고정·변동 두 다리의 달력을 다르게 설정해 지급일이 어긋나는 경우가 발생합니다. 반드시 계약서의 달력 조항을 확인하세요.
- 시작일 == 만기일:
effectiveDate >= terminationDate이면 빈 Schedule이 생성됩니다. 입력 유효성을 사전에 검사하세요. - Tenor와 만기 불일치: 만기까지의 기간이 Tenor의 정수배가 아닌 경우 첫 번째 또는 마지막 구간이 짧거나 길어지는 Short/Long Coupon이 발생합니다. 이는 정상 동작이지만 계획된 것인지 확인하세요.
실무 활용 팁
- 이미 생성된 Schedule 재사용: 동일한 조건의 Schedule은 한 번만 만들고 참조를 공유하세요. 수백 개의 채권을 동시에 처리할 때 성능 차이가 납니다.
- 일정 검증:
schedule.dates()로 전체 날짜 배열을 가져와 첫/마지막 날짜가 예상과 일치하는지 확인하는 습관을 기르세요. - 휴일 목록 갱신: 한국 거래소 휴일은 매년 변경됩니다.
SouthKorea()캘린더에 커스텀 휴일을 추가(addHoliday)하거나, 최신 QuantLib 버전을 유지하세요.
8️⃣ 요약 및 다음 단계
Schedule 클래스는 Date·Period·Calendar를 통합해 금융 상품의 현금흐름 날짜 시퀀스를 자동으로 생성하는 핵심 도구입니다. 이번 섹션에서 학습한 내용을 정리하면:
- Schedule 생성: 효력발생일·만기일·Tenor·달력·영업일 조정 규칙 지정
- 영업일 조정 규칙: Following, ModifiedFollowing, Preceding, Unadjusted
- 날짜 생성 방향: Forward(시작→만기), Backward(만기→시작)
- 실무 활용: 국고채 이자지급 일정, 이자율 스왑 지급 일정
- 상위 클래스 연계: FixedRateBond, VanillaSwap 등에 직접 전달
다음 단계에서는 InterestRate 클래스를 학습하여 금리 자체를 객체로 다루는 방법을 배워보겠습니다. 단순히 숫자로만 금리를 다루는 것이 아니라, 복리 방식(Compounding)과 이자 계산 규칙(DayCounter)을 함께 캡슐화한 InterestRate 객체를 사용하면 할인인자 계산과 수익률 변환이 훨씬 안전하고 명확해집니다.