이 글은 아메리칸 옵션의 가격을 구해보는 내용으로
2022.12.27 - [금융공학] - 아메리칸 옵션의 평가 #1 (이론)
과 이항트리를 이용한 계산방법을 설명한 글인
2022.12.28 - [금융공학] - 아메리칸 옵션의 평가 #2 : 이항트리
에서 이어집니다.
저번 글에서 아메리칸 옵션의 연속가치(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 그리드에 반영되므로 옵션 프리미엄이 상승하게 되는 것입니다.
'금융공학' 카테고리의 다른 글
풋옵션 시간가치의 비밀 (0) | 2023.01.02 |
---|---|
옵션, 시간은 내편!: 시간가치와 내재가치 (0) | 2022.12.30 |
아메리칸 옵션의 평가 #2 : 이항트리 (0) | 2022.12.28 |
아메리칸 옵션의 평가 #1 (이론) (0) | 2022.12.27 |
옵션에도 아메리칸 스타일이 있다 (0) | 2022.12.27 |
댓글