이자율스왑 평가하기(python)2: 금리 커브 일자 구하기
가치평가 기준 데이터 양식
우리는 파이썬을 활용한 이자율스왑 평가하기 1 : 중앙청산소의 평가방식에서 아래와 같은 형식의 평가기준 데이터의 샘플을 만들어 보았다.
구분 | 1일 | 3개월 | 1년 | 2년 | 3년 | 4년 | 5년 | ... |
금리 | 3.55 | 3.48 | 3.22 | 3.00 | 2.905 | 2.8625 | 2.845 | ... |
표 1: 원화이자율스왑 평가 커브(2024-08-02 기준, 1년 이상은 BID-OFFER의 평균인 MID 금리)
금리 자체는 평가 기준일에 따라서 바뀔 것이지만 구분이 추가되거나 하지는 않을 것이기 때문에 이 틀을 갖고 추가적인 작업을 해 나가면 된다. 날짜 데이터는 아래와 같이 딕셔너리 형태로 만들 수 있다.
cdcurve = {
"node": ["1D", "3M", "1Y", "2Y", "3Y", "4Y", "5Y"],
"intrate": [3.55, 3.48, 3.22, 3.00, 2.905, 2.8625, 2.845]
}
날짜 데이터를 편집하기 편하게 데이터프레임 형태로 변환하고 기준일자인 "2024-08-02" 컬럼을 추가하려면 아래와 같이 작업하면 된다.
import pandas as pd
# 딕셔너리 형태의 cdcurve 데이터
cdcurve = {
"node": ["1D", "3M", "1Y", "2Y", "3Y", "4Y", "5Y"],
"intrate": [3.55, 3.48, 3.22, 3.00, 2.905, 2.8625, 2.845]
}
# 데이터프레임으로 데이터 변환 후 basedt 변수 생성(2024-08-02)
df = pd.DataFrame(cdcurve)
basedt = "2024-08-02"
# basedt 컬럼 추가 및 컬럼 재배열
df["basedt"] = basedt
df = df[["basedt", "node", "intrate"]]
금리커브의 node 일자 구하는 방식
금리커브의 node 에 해당하는 날짜는 영업일이어야 한다. 따라서 node 값이 1D라고 해서 2024-08-02 기준으로 무조건 +1 일을 해서 2024-08-03으로 보면 안 된다. 2024-08-02는 금요일이기 때문에 node의 1D에 해당하는 날짜는 2024-08-05이어야 한다. 이렇게 커브의 기준일자가 무슨 요일인지, 월말인지 등에 따라서 커브의 각 node에 해당하는 날짜는 다양해질 수 있다. 이 부분을 정해주는 것이 영업일 관행과 일수계산 방식이다. KRX에서는 아래 그림 1과 같이 영업일 관행과 일수계산방식을 정하고 있다.
영업일 관행이 Modified Following인 경우 해당되는 날짜가 휴일인 경우 다음 영업일로 이연하되 이연한 날짜가 다음 달이 되는 경우 직전영업일의 날짜로 변경한다. 일수계산 방식인 Actual/365(Fixed)은 기본적으로 일수를 계산할 때 실제 날짜 사이의 차이를 바탕으로 하되 1년은 윤년(366일) 고려하지 않고 무조건 365일로 가정하는 방식이다.
금리 커브의 node의 일자 구하기: get_node_date, get_adj_date, extract_numbers, get_modf_eom_date
기본 구조는 아래 그림 2과 같이 짤 수 있다. 먼저 get_node_date 이라는 함수로 입수된 표 1의 데이터프레임에 "nodedate"이라는 컬럼을 추가해 주고 get_adj_date이라는 함수를 사용해서 node에 해당하는 일자를 산출해서 컬럼에 값을 입력해 준다. 그 과정에서 get_adj_date이라는 함수를 사용하는데, dateutil 라이브러리의 relativedelta 모듈의 relativedelta 클래스를 이용해서 node 가 D로 끝날 때는 기준일(basedt)에서 일수만큼 더해주고, M으로 끝날 때는 기준일(basedt)에서 월수만큼 더해주고, Y로 끝날 때는 기준일(basedt)에서 년수만큼 더해준다. 이렇게 산출한 날짜가 Modified Following을 만족하는 영업일이 아닐 수 있기 때문에 get_modf_eom_date을 이용해서 날짜를 조정해 준다.
각각의 함수는 아래와 같은 코드로 만들 수 있다.
# pandas(데이터 라이브러리), datetime(날짜 라이브러리), re(정규식 라이브러리) import
import pandas as pd
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
import re
# node에서 숫자만 뽑아내는 함수
def extract_numbers(input_string):
# 숫자인 것 전부 뽑아내서 numbers에 저장
numbers = re.findall(r'\d+', input_string)
# "" 공백 없이 합치기
return ''.join(numbers)
# modified following 만족하는 날짜 return 하는 함수
def get_modf_eom_date(date, holidays):
# 입수된 날짜의 월 변수에 저장
origin_month = date.month
# 입수된 날짜의 값이 5(토), 6(일), 휴일 일 경우 하루씩 더하는 작업 -> 휴일일 경우
# 다음 영업일로 변환하는 loop 문
while date.weekday() >= 5 or date in holidays:
date += timedelta(days=1)
# 다음영업일로 변환하는 과정에서 월이 바뀌었을 경우
if date.month != origin_month:
# 다음월의 1일에서 직전일로 end_of_month 세팅(직전월의 마지막 날짜)
end_of_month = datetime(date.year, origin_month + 1, 1) - timedelta(days=1)
# 직전월의 마지막 날자가 휴일일 경우(5(토), 6(일), 휴일) 휴일이 아닌 날짜가 될 때까지
# 직전일로 변경하는 loop 문
while end_of_month.weekday() >= 5 or end_of_month in holidays:
end_of_month -= timedelta(days=1)
date = end_of_month
return date
# node의 조정된 날짜를 구하는 함수
def get_adj_date(inputdate:datetime.date, node:str, holidays):
# node 값이 D로 끝날 경우
if node[-1] == "D":
# extract_numbers 함수로 며칠 뒤의 데이터인지 값 추출
interval_number = extract_numbers(node)
interval = int(interval_number) # 숫자로 형식 변환
date = inputdate + relativedelta(days=interval) # 날짜 더하기
# date를 modified following 을 만족하는 날짜로 변경
adjdate = get_modf_eom_date(date, holidays).strftime("%Y-%m-%d")
# node 값이 M으로 끝날 경우
elif node[-1] == "M":
# extract_numbers 함수로 몇 달 뒤의 데이터인지 값 추출
interval_number = extract_numbers(node)
interval = int(interval_number) # 숫자로 형식 변환
date = inputdate + relativedelta(months=interval) # 월 수 더하기
# date를 modified following 을 만족하는 날짜로 변경
adjdate = get_modf_eom_date(date, holidays).strftime("%Y-%m-%d")
# node 값이 Y로 끝날 경우
elif node[-1] == "Y":
# extract_numbers 함수로 몇 년 뒤의 데이터인지 값 추출
interval_number = extract_numbers(node)
interval = int(interval_number) # 숫자로 형식 변환
date = inputdate + relativedelta(years=interval) # 년 수 더하기
# date를 modified following 을 만족하는 날짜로 변경
adjdate = get_modf_eom_date(date, holidays).strftime("%Y-%m-%d")
# 예외처리
else:
adjdate = inputdate
return adjdate
# 'basedt", "node", "intrate"으로 구성된 dataframe을 받아서 "nodedate"을 추가하고
# get_adj_date을 활용해서 "nodedate"컬럼을 값을 채워나가는 함수
def get_node_date(df, basedt, holidays):
# 빈 "nodedate" 컬럼 추가
df["nodedate"] = ""
# 입수된 basedt를 날짜 형식으로 변경
basedt = datetime.strptime(basedt, "%Y-%m-%d")
# 입수된 dataframe 의 node 별로 get_adj_date 함수로 작업해서 "nodedate"컬럼에 값 채우기
for i in df["node"]:
adjdate = get_adj_date(basedt, i, holidays)
df.loc[df["node"]==i, "nodedate"] = adjdate
return df[["basedt", "node", "nodedate", "intrate"]]