ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 이자율스왑 평가하기(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"]]

     

    반응형
Designed by Tistory.