이번 글은
2023.04.27 - [금융공학] - 델타, 감마, 스피드! 콜옵션의 가격 변화를 쫓아가보자 #1
와 관련이 있습니다.
파생상품의 민감도(Greeks) 들은 파생상품의 가격 공식을 미분하여 얻어집니다. 예컨대, 시점 $t$, 기초자산 $S$에서의 가격이 $f(t, S)$로 주어지는 파생상품의 델타, 감마, 스피드는 각각
$$ \Delta = \frac{\partial f}{\partial S}~,~ \Gamma = \frac{\partial^2 f}{\partial S^2}~,~ {\rm{Speed}} = \frac{\partial^3 f}{\partial S^3} \tag{1}$$
으로 정의되죠. 하지만, 이렇게 구하는 방법에는 몇 가지 애로사항이 있습니다.
○ 파생상품의 가격 $f(t,S)$가 공식으로 주어지지 않는 다면 어쩔 것이냐?
우리가 앞서 알아본 콜옵션/풋옵션은 상품 가격이 공식으로 주어집니다. 이른바 Black Scholes Formula 이죠. 그런데 대부분의 상품에 가격 공식이 있는 것이 아닙니다. 예컨대, ELS ([금융공학] - 1star 스텝다운 ELS의 계산(시뮬레이션) 참고)의 경우만 하더라도 공식이 없어서, MC나 FDM 또는 이항트리(binomial tree)를 이용하여 구하는 실정입니다. 공식이 주어지지 않으면 식(1)을 쓸 수 조차 없겠죠.
○ 미분은 틀리기 마련이다?!
식 (1)을 쓰기 위해서는 정확한 미분 실력이 있어야 합니다. 공식 자체가 복잡해지면, 한 번 미분하는 것(델타)도 어렵습니다. 하물며, 감마, 스피드는 오죽할까요? 꾸역꾸역 미분, 2차 도함수, 3차 도함수들을 구해보더라도 이것이 맞는지 검증이 필요합니다.
이번 글에서는 다른 방법으로 그릭들을 구하는 방법을 소개합니다.
수치해석적 미분
시점 $t$, 기초자산 $S$에서 어떤 파생상품의 가격을 $f(t,S)$ 라 합시다. 이 함수 $f$는 공식이 없어도 되고, 미분 가능한지 안 한 지 따질 수도 없다고 합시다(사실은 2번까지는 미분가능하다는 것이 금융공학의 전제조건이긴 합니다. 암묵적으로 $f$는 2번까지는 미분 가능합니다.)
이 때, 수치해석적인 방법으로 1차, 2차, 3차 도함수를 구할 수 있습니다(각각 델타, 감마, 스피드에 해당되겠죠.)
이번 설명에서 관심 있는 변수는 $S$이므로 $t$는 고정되어 있다고 생각하여 $f(t, S) := f(S)$로 한 변수인 것처럼 생각하겠습니다.
1차 도함수
$$\frac{\partial f}{\partial S} \approx \frac{f(S+h/2)-f(S-h/2)}{h}\tag{2}$$
입니다. 위 식에서 $h$는 (아주) 작은 양수입니다. 이렇게 정의하는 방법을 중앙차분법(central difference)라 합니다. 다른 방법으로,
$$\frac{\partial f}{\partial S} \approx \frac{f(S+h)-f(S)}{h}\tag{3}$$
$$\frac{\partial f}{\partial S} \approx \frac{f(S-h)-f(S)}{-h}\tag{4}$$
로 정의하는 경우도 많습니다. 식(3)을 전방 차분 (forward difference), 식(4)을 후방 차분(backward difference)라고 합니다. 무슨 근사치를 써도 상관은 없으나, 보통 식(2)의 중앙 차분을 많이 사용합니다.
식(2), (3), (4) 공히 $h$가 무한히 작아지게 된다면, 실제로 1차 도함수로 가죠. 고등학교 때 배웠던 미분의 정의입니다.
2차 도함수
2차 도함수는 어떻게 수치해석적 근사식을 만들 수 있을까요?
$$
\begin{align}
f''(S) & \approx \frac { f'(S+h/2)-f'(S-h/2) }{h} \\
&\approx \frac{ \frac{f(S+h/2+h/2)-f(S+h/2-h/2)}{h} -\frac{f(S-h/2+h/2)-f(S-h/2-h/2}{h}}{h}\\
&\approx \frac{f(S+h)-2f(S)+f(S-h)}{h^2}\tag{5}
\end{align}
$$
첫 번째 줄은 식(2)을 적용한 결과입니다. 두 번째 줄은 $f'(S+h/2)$와 $f'(S-h/2)$에 다시 한번 식(2)를 적용한 결과입니다.
3차 도함수
3차 도함수의 근사식은 아래처럼 구합니다.
$$
\begin{align}
f'''(S) & \approx \frac { f''(S+h/2)-f''(S-h/2) }{h} \\
&\approx \Bigg(\frac{f(S+h/2+h/2)-2f(S+h/2)+f(S+h/2-h/2)}{(h/2)^2} \\
& ~~~~~~~~~~-\frac{f(S-h/2+h/2)-2f(S-h/2)+f(S-h/2-h/2}{(h/2)^2}\Bigg)\Big/ h\\
&\approx \frac{f(x+h)-2f(x+h/2)+2f(x-h/2)-f(x-h)}{h^3/4}\tag{6}
\end{align}
$$
이제 식(2) , (5), (6)을 주목해 주시기 바랍니다. 이것들이 각각 델타, 감마, 스피드가 되니까요!
그럼 이렇게 수치해석적으로 구한 그릭들이 여기에 기재한 콜옵션의 공식 그릭과 비슷한지 아닌지 코딩을 통하여 알아보겠습니다.
Python Code: Greeks들을 수치해석적으로 구하기
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
import pandas as pd
def CallOptionBS(S, K, T, r, q, sigma):
# 전의 글들을 참고하기 바랍니다.
# -----------------------------------------------------
# ---- CallOptionBS_NumericalGreeks 함수 설명 ----------
# delta, gamma, speed를 수치해석적으로 구하는 함수
# call option value 는 Black Scholes Formula를 그대로 씀
# -----------------------------------------------------
def CallOptionBS_NumericalGreeks(S, K, T, r, q, sigma):
Ncdf = norm.cdf
npdf = norm.pdf
ds = 0.01 * S # 본문의 h로 표시한 것(h=dS), S의 크기에 1%를 h로 설정
val = CallOptionBS(S, K, T, r, q, sigma)[0]
val_up = CallOptionBS(S + ds / 2, K, T, r, q, sigma)[0] # h/2 bumping up
val_up2 = CallOptionBS(S + ds, K, T, r, q, sigma)[0] # h bumping up
val_down = CallOptionBS(S - ds / 2, K, T, r, q, sigma)[0] # h/2 bumping down
val_down2 = CallOptionBS(S - ds, K, T, r, q, sigma)[0] # h bumping down
# 본문의 식(2), (5), (6)을 코딩한 결과
delta = (val_up - val_down) / (ds)
gamma = (val_up2 - 2 * val + val_down2) / (ds ** 2)
speed = (val_up2 - 2 * val_up + 2 * val_down - val_down2) / (ds ** 3 / 4)
# 0. value
# 1. delta
# 2. gamma
# 3. speed
return val, delta, gamma, speed # value, delta, gamma, speed 를 return
def analysis_call_option_numerical_greeks():
s0 = 100
strike = 100
maturity = 1
rfr = 0.03
div = 0
vol = 0.3
spot_array = np.arange(50, 150 + 1, 1) # 기초자산은 50~ 150까지 1간격으로 array 만듬
delta_list = []
gamma_list = []
speed_list = []
for s in spot_array:
res_analytic = CallOptionBS(s, strike, maturity, rfr, div, vol) # 공식으로 구한 greeks
res_numerical = CallOptionBS_NumericalGreeks(s, strike, maturity, rfr, div, vol)
# 수치해석적 greeks
delta_list.append([res_analytic[1], res_numerical[1]]) # [공식 델타, 수치해석 델타] list
gamma_list.append([res_analytic[2], res_numerical[2]]) # [공식 감마, 수치해석 감마] list
speed_list.append([res_analytic[3], res_numerical[3]]) # [공식 speed, 수치해석 speed] list
delta_array = np.array(delta_list) #list의 array화 (밑에 slicing을 위하여)
gamma_array = np.array(gamma_list)
speed_array = np.array(speed_list)
from matplotlib import gridspec
fig = plt.figure(figsize=(10, 30))
gs = gridspec.GridSpec(nrows=3, ncols=1, height_ratios=[1, 1, 1])
ax = [fig.add_subplot(gs[0]), fig.add_subplot(gs[1]), fig.add_subplot(gs[2])]
# 기초자산 가격에 따른 공식 델타와, 수치해석 델타를 그래프에 같이 표시
ax[0].plot(spot_array, delta_array[:, 0], color='lightgray', linewidth=5, label='delta_closedform')
ax[0].plot(spot_array, delta_array[:, 1], color='blue', linewidth=2, linestyle='--'
, label='delta numerical')
ax[0].legend()
# 공식 델타와 수치해석 델타 array의 원소중 차이가 가장 큰 것을 출력
print('Maximum difference between closed form delta and numerical delta : {:.10f}'.format(
np.max(np.abs(delta_array[:, 1] - delta_array[:, 0]))))
# 기초자산 가격에 따른 공식 감마와, 수치해석 감마를 그래프에 같이 표시
ax[1].plot(spot_array, gamma_array[:, 0], color='lightgray', linewidth=5, label='gamma_closedform')
ax[1].plot(spot_array, gamma_array[:, 1], color='tomato', linewidth=2, linestyle='--', label='gamma numerical')
ax[1].legend()
# 공식 감마와 수치해석 감마 array의 원소중 차이가 가장 큰 것을 출력
print('Maximum difference between closed form gamma and numerical gamma : {:.10f}'.format(
np.max(np.abs(gamma_array[:, 1] - gamma_array[:, 0]))))
# 기초자산 가격에 따른 공식 스피드와, 수치해석 스피드를 그래프에 같이 표시
ax[2].plot(spot_array, speed_array[:, 0], color='lightgray', linewidth=5, label='speed_closedform')
ax[2].plot(spot_array, speed_array[:, 1], color='green', linewidth=2, linestyle='--', label='speed numerical')
ax[2].legend()
# 공식 스피드와 수치해석 스피드 array의 원소중 차이가 가장 큰 것을 출력
print('Maximum difference between closed form speed and numerical speed : {:.10f}'.format(
np.max(np.abs(speed_array[:, 1] - speed_array[:, 0]))))
plt.show()
if __name__ == '__main__':
analysis_call_option_numerical_greeks()
결과를 보겠습니다.
Maximum difference between closed form delta and numerical delta : 0.0000148013
Maximum difference between closed form gamma and numerical gamma : 0.0000014086
Maximum difference between closed form speed and numerical speed : 0.0000000613
기초자산 가격이 50에서 150까지 각각 델타, 감마, 스피드를 수치해석적으로 구하여, 이를 공식과 비교하여 차이가 가장 큰 값을 계산해 본 결과입니다. 공식과 수치해석 결과가 거의 차이가 나지 않는다는 것을 알 수 있습니다. 이는 아래 그래프를 봐도 알 수 있습니다. 델타, 감마, 스피드 각각 공식과 수치해석적 방법 두 그래프가 완전히 겹치는 모습이죠.
이제 이 분석을 통하여 우리는 두 가지 정보를 얻을 수 있습니다.
○ 수치해석적 방법과 공식(미분)이 거의 차이가 없으니 우리가 미분을 실수 없이 잘했었구나. 그거 그대로 믿고
사용해도 되겠다!
○ 굳이 델타, 감마 등을 미분해서 구할 필요가 있나? 계산 실수할 수도 있는데.. 그냥 수치 해석적 방법을 써야겠다!
파생상품의 가격 공식이 공식으로만 끝나는 경우는 없습니다. 이 공식을 프로그래밍하여 파생상품의 움직임을 분석하고 운용하는 게 목적이죠. 어차피 프로그래밍을 할 것이라면, 이렇게 수치해석적인 방법론이, 연습장과 연필로 푼 미분 공식값과 일치하는지 따져보는 일은 충분히 가치 있는 일이 될 것입니다.
또한, 공식이 없는 파생상품들을 위해 그릭들을 수치해석적으로 구하는 방법을 알아두면 큰 도움이 되겠죠.
'금융공학' 카테고리의 다른 글
몬테카를로 시뮬레이션과 그릭의 안정성 #2 : 시드 고정 (0) | 2023.07.12 |
---|---|
몬테카를로 시뮬레이션과 그릭의 안정성 #1 (0) | 2023.07.07 |
풋옵션의 민감도들은? feat. 풋콜패리티 (0) | 2023.06.19 |
로(Rho)! 콜옵션은 이자율에 어떤 영향을 받을까? (0) | 2023.06.14 |
로(Rho)! 콜옵션의 이자율 민감도는? (0) | 2023.06.09 |
댓글