본문 바로가기
금융공학

아메리칸 옵션의 평가 #3 : 함축적(implicit) FDM

by hustler78 2022. 12. 28.
728x90
반응형

이 글은 아메리칸 옵션의 가격을 구해보는 내용으로 

2022.12.27 - [금융공학] - 아메리칸 옵션의 평가 #1 (이론)

 

아메리칸 옵션의 평가 #1 (이론)

이번 글부터 몇 회에 걸쳐 아메리칸 옵션(American option)의 가격 계산에 대해 알아보도록 할까 합니다. 아메리칸 옵션에 대한 소개는 [금융공학] - 옵션에도 아메리칸 스타일이 있다 옵션에도 아메

sine-qua-none.tistory.com

과 이항트리를 이용한 계산방법을 설명한 글인

2022.12.28 - [금융공학] - 아메리칸 옵션의 평가 #2 : 이항트리

 

아메리칸 옵션의 평가 #2 : 이항트리

이 글은 아메리칸 옵션에 대한 설명 2022.12.27 - [금융공학] - 아메리칸 옵션의 평가 #1 (이론) 아메리칸 옵션의 평가 #1 (이론) 이번 글부터 몇 회에 걸쳐 아메리칸 옵션(American option)의 가격 계산에

sine-qua-none.tistory.com

에서 이어집니다.

 

저번 글에서 아메리칸 옵션의 연속가치(continuation value)와 행사가치(exercise value)에 대해서 설명하였고, 행사가치가 연속가치보다 클 때 행사가치로 치환하여 아메리칸 옵션의 가치를 계산할 수 있다고 했습니다. 

또한 이항트리 방법을 이용하여 American Put 옵션의 가격을 구했습니다.

 

마찬가지로 이 글에서도 풋옵션을 예로 들어 설명해 보겠습니다.

 

 

복습 | 유로피안 풋옵션의 계산 FDM python code

 

 

풋옵션의 closed form은 여기를 참고하시면 되고, 함축적 FDM 방법은 옵션 #5. 옵션 프리미엄 구하기 실습: 함축적 FDM 글을 참고하시면 됩니다.

 

※ 위 FDM 방법 링크 글에서는 FDM의 time 축을 잔존만기로 치환하여, 잔존만기가 0인 상황(즉, 만기인 상황)에서 잔존만기가 만기인 상황(즉, 처음 계산시점)으로 time 축을 계산하였다면, 직관적인 코딩을 위해 time 축을 그냥 시점이라고 생각하고 풀겠습니다. 그러면, time 축이 만기에서부터 현재까지 거꾸로 backward 하는 풀이가 됩니다.

 

 

아래 코드는 유로피언 풋옵션의 closed form과 함축적 FDM을 사용한 가격 산출 code입니다.

 

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
import time

N = norm.cdf
def PutOptionBS(S, K, T, r, q, sigma):
    if T == 0:
        return np.max(K - S, 0)
    else:
        d1 = (np.log(S / K) + (r - q + sigma ** 2 / 2) * T) / (sigma * np.sqrt(T))
        d2 = d1 - sigma * np.sqrt(T)
        return -S * np.exp(-q * T) * N(-d1) + K * np.exp(-r * T) * N(-d2)

def PutOption_FDM_Implicit(s0, strike, maturity, rfr, div, vol):
    Nsize, Jsize = 250, 1000
    # s variable setting
    s_min, s_max = 0, 5 * s0
    s_seq = np.linspace(s_min, s_max, Jsize + 1)
    h = s_seq[1] - s_seq[0]

    # time variable setting
    t_min, t_max = 0, maturity
    t_seq = np.linspace(t_min, t_max, Nsize + 1)
    k = t_seq[1] - t_seq[0]

    # solution grid u setting
    u = np.empty((Nsize + 1, Jsize + 1))

    # initial_condition
    u[-1, :] = np.array([max(strike - s, 0) for s in s_seq])
    # recursive formula
    for n in range(Nsize + 1)[::-1]:   # time 축을 만기시점부터 거꾸로 내려옴
        # make tridiagonal matrix
        diag = np.array([1 / k + (vol * x / h) ** 2 + rfr for x in s_seq[1:Jsize]])
        under = np.array([(rfr - div) * x / 2 / h - 1 / 2 * (vol * x / h) ** 2 for x in s_seq[1:Jsize]])
        over = np.array([-(rfr - div) * x / 2 / h - 1 / 2 * (vol * x / h) ** 2 for x in s_seq[1:Jsize]])

        diag[0] = 2 * under[0] + diag[0]
        over[0] = - under[0] + over[0]

        diag[-1] = diag[-1] + 2 * over[-1]
        under[-1] = under[-1] - over[-1]

        # solve tridiagonal matrix
        known = u[n, 1: Jsize] / k
        unknown = thomas(under, diag, over, known)   # 연립방정식을 implcit 방법으로 품

        u[n - 1, 1:Jsize] = unknown  # (n-1) time 그리드 값 update
        # set boundary condition
        u[n - 1, 0] = 2 * u[n - 1, 1] - u[n - 1, 2]
        u[n - 1, -1] = 2 * u[n - 1, -2] - u[n - 1, -3]

    put_value = np.interp(s0, s_seq, u[0, :])   #s0에서의 u 값을 선형보간으로 구하기
    return put_value, u, s_seq, t_seq


def thomas(a, b, c, d):
    # 지난 글 참고

def Calculate_put_option_closedform_fdm():
    s0 = 100
    strike = 100
    maturity = 1
    rfr = 0.02
    vol = 0.4
    div = 0.01

    s_vec = np.arange(10, 151, 10)

    put_value, u, s_seq, t_seq = PutOption_FDM_Implicit(s0, strike, maturity, rfr, div, vol)

    exact_value = np.array([PutOptionBS(s, strike, maturity, rfr, div, vol) for s in s_vec])

    fdm_value = np.interp(s_vec, s_seq, u[0, :])

    plt.plot(s_vec, exact_value, color='gray', marker='*', markersize=20, label='closed_form')
    plt.plot(s_vec, fdm_value, color='royalblue', marker='o', markersize=5, label='fdm(implicit)')
    min_difference_fdm = min(fdm_value - exact_value)

    print('Min difference between FDM and CLosedForm value : {:.3f}'.format(min_difference_fdm))
    plt.legend()
    plt.show()

if __name__ == '__main__':
    Calculate_put_option_closedform_fdm()

 

예전 글들에서 함축적 FDM 계산법 설명할 때 지겹도록 본 유형의 코드입니다.  결과는 아래와 같습니다(결론부터 말해, closed form과 FDM 계산결과가 너무도 일치하여 closed form 그래프의 marker size를 20으로 왕창 키웠습니다.)

 

Min difference between FDM and CLosedForm value : -0.009

 

너무 일치한다. 너무 일치해

 

 

closed form 가격과 함축적 FDM 가격이 일치하고 있는 것이 확인됩니다. 이제 해당 코드를 American 계산법으로 바꿔보겠습니다.  PutOption_FDM_Implicit 함수를 조금 손보도록 할게요.

 

 

 

아메리칸 풋옵션의 계산: FDM

위의 풋옵션 FDM 코드를 아주 살짝 변형하여 American Put을 계산할 수 있습니다. 아래를 보시죠.

def American_PutOption_FDM_Implicit(s0, strike, maturity, rfr, div, vol):
    Nsize, Jsize = 250, 1000
    # s variable setting
    s_min, s_max = 0, 5 * s0
    s_seq = np.linspace(s_min, s_max, Jsize + 1)
    h = s_seq[1] - s_seq[0]

    # time variable setting
    t_min, t_max = 0, maturity
    t_seq = np.linspace(t_min, t_max, Nsize + 1)
    k = t_seq[1] - t_seq[0]

    # solution grid u setting
    u = np.empty((Nsize + 1, Jsize + 1))

    # initial_condition
    u[-1, :] = np.array([max(strike - s, 0) for s in s_seq])
    # recursive formula
    for n in range(Nsize + 1)[::-1]:
        # make tridiagonal matrix
        diag = np.array([1 / k + (vol * x / h) ** 2 + rfr for x in s_seq[1:Jsize]])
        under = np.array([(rfr - div) * x / 2 / h - 1 / 2 * (vol * x / h) ** 2 for x in s_seq[1:Jsize]])
        over = np.array([-(rfr - div) * x / 2 / h - 1 / 2 * (vol * x / h) ** 2 for x in s_seq[1:Jsize]])

        diag[0] = 2 * under[0] + diag[0]
        over[0] = - under[0] + over[0]

        diag[-1] = diag[-1] + 2 * over[-1]
        under[-1] = under[-1] - over[-1]

        # solve tridiagonal matrix
        known = u[n, 1: Jsize] / k
        unknown = thomas(under, diag, over, known)
        
        exer_value = np.maximum(strike - s_seq[1:Jsize], 0)
        u[n - 1, 1:Jsize] = np.where(unknown >= exer_value, unknown, exer_value)

        # set boundary condition
        u[n - 1, 0] = 2 * u[n - 1, 1] - u[n - 1, 2]
        u[n - 1, -1] = 2 * u[n - 1, -2] - u[n - 1, -3]

    put_value = np.interp(s0, s_seq, u[0, :])
    return put_value, u, s_seq, t_seq

 

위의 European Put 옵션과의 차이점이 보이는지요? 바로 아랫부분입니다.

 

        unknown = thomas(under, diag, over, known)     # 연속가치에 해당하는 부분      
        
        exer_value = np.maximum(strike - s_seq[1:Jsize], 0)   # 행사가치, np.maximum 을 쓴다.
        u[n - 1, 1:Jsize] = np.where(unknown >= exer_value, unknown, exer_value)
                            # 연속가치>행사가치 인 index에는 unknown(연속가치) 값을 할당
                            # 아닌 경우의         index에는 exer_value(행사가치) 값을 할당

이렇게  code의 한 줄 정도 변형을 해 주면 바로 American 옵션의 가치를 구할 수 있죠. 결과를 보겠습니다.

 

 

Min difference between FDM and CLosedForm value : 0.014

특정 주가에서, 아메리칸 옵션의 가격이 0.014 정도 증가한 걸 볼 수 있고요.

 

 

위와 같이 아메리칸 옵션과 closed form의 차이를 느낄 수 있습니다.

 

이처럼 행사가치가 매 순간순간 FDM 그리드에 반영되므로 옵션 프리미엄이 상승하게 되는 것입니다. 

 

 

728x90
반응형

댓글