금융퀀트/자산평가&프로그램매매

이자율스왑 평가하기(python)2: 금리 커브 일자 구하기

문송한투자자 2024. 8. 25. 15:14
반응형

가치평가 기준 데이터 양식

우리는 파이썬을 활용한 이자율스왑 평가하기 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과 같이 영업일 관행과 일수계산방식을 정하고 있다. 

그림1: KRX 영업일 관행과 일수계산방식

영업일 관행이 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을 이용해서 날짜를 조정해 준다.

그림2: node 에 대응하는 날짜를 구하는 코드 구조

각각의 함수는 아래와 같은 코드로 만들 수 있다. 

# 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"]]

 

반응형