이 글에서는, 바로 전 글( 변동성이 커지면 콜옵션 값어치가 올라간다!?)
에 이어서 변동성 변화에 따른 콜옵션 가격 변화가 어떤 요인으로 이루어져 있는지를 알아보겠습니다.
콜옵션을 계산하는 시점 $t$와 그 시점에서의 기초자산 $S$가 동일하고 변동성만 바뀌는 상황을 가정해 보겠습니다.
변동성은 $\sigma$에서 $\sigma+\Delta \sigma$로 바뀌었다고 하죠.
변동성 변화에 따른 콜옵션 가격 변화 근사식은?
변동성 변화에 따른 콜 가격변화 역시 테일러 전개를 이용합니다.
콜옵션 가격이 변동성 $\sigma$에 대한 함수임을 강조하기 위해
$$c(t,S;\sigma)$$
라 해보죠. 세미콜론(;)은 $\sigma$가 $t$나 $S$와는 성질이 조금 다른 파라미터임을 강조하기 위해서입니다. 시점과 기초자산 가격이 그대로 있을 때, 변동성의 변화
$\sigma \rightarrow \sigma+\Delta\sigma$에 대해 다음의 테일러 전개가 성립합니다.
$$ c(t,S;\sigma+\Delta \sigma)-c(t,S;\sigma) = \frac{\partial c}{\partial \sigma} \Delta \sigma +\frac12 \frac{\partial^2 c}{\partial \sigma^2} (\Delta \sigma)^2+\frac16 \frac{\partial^3 c}{\partial \sigma^3} (\Delta \sigma)^3 +\cdots \tag{1}$$
항의 개수가 늘어날수록 근사치는 정교해지게 되겠죠. 실제로 1,2,3차 근사식을 써보면
1차 근사
$$ c(t,S;\sigma+\Delta \sigma)-c(t,S;\sigma) \approx \frac{\partial c}{\partial \sigma} \Delta \sigma \tag{2}$$
2차 근사
$$ c(t,S;\sigma+\Delta \sigma)-c(t,S;\sigma)\approx \frac{\partial c}{\partial \sigma} \Delta \sigma +\frac12 \frac{\partial^2 c}{\partial \sigma^2} (\Delta \sigma)^2\tag{3}$$
3차 근사
$$ c(t,S;\sigma+\Delta \sigma)-c(t,S;\sigma) \approx \frac{\partial c}{\partial \sigma} \Delta \sigma +\frac12 \frac{\partial^2 c}{\partial \sigma^2} (\Delta \sigma)^2+\frac16 \frac{\partial^3 c}{\partial \sigma^3} (\Delta \sigma)^3\tag{4}$$
입니다. 여기서 편미분 값들은 모두 시점 $t$, 기초자산 $S$ 그리고 변동성 $\sigma$인 상황에서 구해진 값입니다. 또 각각의 1,2,3차 편미분 값들은 아래의 공식을 이용해 구할 수 있습니다.
구분 | 정의 | 수식 |
1차 도함수 (Vega) |
$$\mathcal{V}= \frac{\partial c}{\partial \sigma}$$ | $$Ke^{-r\tau} \phi(d_2) \sqrt{\tau}= Se^{-r\tau}\phi(d_1) \sqrt{\tau}$$ |
2차 도함수 (Volga) |
$$\frac{\partial^2 c}{\partial \sigma^2}$$ | $$ {\rm{(Vega)}} \frac{d_1d_2}{\sigma} $$ |
3차도함수 (Ultima) |
$$\frac{\partial^3 c}{\partial \sigma^3}$$ | $$ -\frac{1}{\sigma} {\rm{Vega}}\cdot \left( d_1d_2(1-d_1d_2) + d_1^2+d_2^2 \right)$$ |
그럼 식(2), (3),(4)의 근사식이 맞는지 python code를 통해 알아볼까요?
Python Code : 콜옵션 가격 변화에 변동성이 미치는 요인 분석
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
import pandas as pd
# ----------------------------------------
# call option value/Greeks function
# vega, volga, ultima 추가 (theta2 삭제)
# ----------------------------------------
def CallOptionBS(S, K, T, r, q, sigma):
Ncdf = norm.cdf
npdf = norm.pdf
if T == 0:
val = np.maximum(S - K, 0)
delta = 1 if S >= K else 0
gamma = 0
speed = 0
theta = 0
vega = 0
volga = 0
ultima = 0
else:
d1 = (np.log(S / K) + (r - q + sigma ** 2 / 2) * T) / (sigma * np.sqrt(T))
d2 = d1 - sigma * np.sqrt(T)
val = S * np.exp(-q * T) * Ncdf(d1) - K * np.exp(-r * T) * Ncdf(d2)
delta = np.exp(-q * T) * Ncdf(d1)
gamma = np.exp(-q * T) * npdf(d1) / (S * sigma * np.sqrt(T))
speed = -np.exp(-q * T) * npdf(d1) / (S ** 2 * sigma * np.sqrt(T)) * (1 + d1 / (sigma * np.sqrt(T)))
theta = -np.exp(-q * T) * S * npdf(d1) * sigma / (2 * np.sqrt(T)) - r * K * np.exp(-r * T) * Ncdf(
d2) + q * S * np.exp(-q * T) * Ncdf(d1)
# theta2 = -(r - q) * S * delta - 0.5 * sigma ** 2 * S ** 2 * gamma + r * val
# theta +(r-q)S *delta + 0.5*sigma^2*S^2 *gamma -rf =0
vega = S * np.exp(-q * T) * npdf(d1) * np.sqrt(T) # vega
volga = vega * d1 * d2 / sigma # volga
ultima = -vega / sigma ** 2 * (d1 * d2 * (1 - d1 * d2) + d1 ** 2 + d2 ** 2) #ultima
# return index
# 0. value
# 1. delta
# 2. gamma
# 3. speed
# 4. theta
# 5. vega
# 6. volga
# 7. ultima
return val, delta, gamma, speed, theta, vega, volga, ultima
def analysis_call_option_vol_risk_factor():
s0 = 100
strike = 100
maturity = 1
rfr = 0.03
div = 0
vol = 0.3 # v0
# 아래 column들을 가지는 dataframe을 만든다.
# vol_after : 변동 후 volatility
# dvol = vol_after - v0
# dc = call_after - call0
# vega_fac = vega* dvol (1차 근사, 베가 팩터)
# volga_fac = (1/2) * volga * (dvol)^2 (2차 근사, volga factor)
# ultima_fac = (1/6) * ultima * (dvol)^3 (3차 근사, ultima factor)
columns = ['vol_after', 'dvol', 'dc', 'vega_fac', 'volga_fac', 'ultima_fac']
call0 = CallOptionBS(s0, strike, maturity, rfr, div, vol) # 변경전 call 가치
val0, vega0, volga0, ultima0 = call0[0], call0[5], call0[6], call0[7] # 필요한 그릭 추출
# 500개의 sample을 생성시켜볼 예정
num_data = 500
vol_range = 0.15
# dataframe 판 만듬
df = pd.DataFrame(data=[], columns=columns)
# 다양한 volatility 생성
vol_change = np.random.uniform(vol - vol_range, vol + vol_range, num_data)
# volatility 에 따라 변경된 vol
call_vec = np.array([CallOptionBS(s0, strike, maturity, rfr, div, v)[0] for v in vol_change])
df['vol_after'] = vol_change
df['dvol'] = vol_change - vol
df['dc'] = call_vec - val0
df['vega_fac'] = vega0 * df['dvol']
df['volga_fac'] = (1 / 2) * volga0 * df['dvol'] ** 2
df['ultima_fac'] = (1 / 6) * ultima0 * df['dvol'] ** 3
df['1fac_effect'] = df.dc - df.vega_fac # 1차 근사 후 error term
df['2fac_effect'] = df.dc - df.vega_fac - df.volga_fac #2차 근사 후 error term
df['3fac_effect'] = df.dc - df.vega_fac - df.volga_fac - df.ultima_fac # 3차 근사 후 error term
pd.set_option('display.max_columns', None) #df의 all column 보이게 출력
print(df)
# 500개 sample의 1차, 2차, 3차 근사 error term을 간단히 긁어줌
# 각 1,2,3차 erorr term의 mean 구함
df_mean = df[['1fac_effect', '2fac_effect', '3fac_effect']].mean()
print(df_mean)
if __name__ == '__main__':
analysis_call_option_vol_risk_factor()
결과를 보시죠.
vol_after dvol dc vega_fac volga_fac ultima_fac \
0 0.349343 0.049343 1.905557 1.907931 -0.001961 -0.000450
1 0.260592 -0.039408 -1.524772 -1.523770 -0.001251 0.000229
2 0.221745 -0.078255 -3.028638 -3.025869 -0.004933 0.001796
3 0.215647 -0.084353 -3.264640 -3.261667 -0.005732 0.002250
4 0.223841 -0.076159 -2.947509 -2.944818 -0.004672 0.001656
.. ... ... ... ... ... ...
495 0.416306 0.116306 4.481344 4.497171 -0.010897 -0.005897
496 0.185681 -0.114319 -4.423292 -4.420355 -0.010528 0.005600
497 0.280881 -0.019119 -0.739538 -0.739270 -0.000294 0.000026
498 0.251358 -0.048642 -1.882240 -1.880814 -0.001906 0.000431
499 0.359186 0.059186 2.284992 2.288516 -0.002822 -0.000777
1fac_effect 2fac_effect 3fac_effect
0 -0.002374 -0.000413 0.000037
1 -0.001001 0.000250 0.000020
2 -0.002770 0.002163 0.000367
3 -0.002973 0.002759 0.000510
4 -0.002690 0.001982 0.000326
.. ... ... ...
495 -0.015827 -0.004930 0.000967
496 -0.002937 0.007591 0.001991
497 -0.000267 0.000027 0.000001
498 -0.001426 0.000480 0.000048
499 -0.003524 -0.000702 0.000075
[500 rows x 9 columns]
1fac_effect -0.005188
2fac_effect 0.000814
3fac_effect 0.000901
dtype: float64
Process finished with exit code 0
1차 근사 error > 2차 근사 error > 3차 근사 error
가 성립하고, 3차 근사까지 하면 call option의 가격 변화를 변도성 변화로 충분히 설명할 수 있다는 것을 알았습니다.
'금융공학' 카테고리의 다른 글
로(Rho)! 콜옵션은 이자율에 어떤 영향을 받을까? (0) | 2023.06.14 |
---|---|
로(Rho)! 콜옵션의 이자율 민감도는? (0) | 2023.06.09 |
변동성이 커지면 콜옵션 값어치가 올라간다!? (0) | 2023.06.01 |
Vega! 변동성에 대한 콜옵션 가격 민감도 (0) | 2023.05.31 |
콜옵션의 움직임을 낱낱이 파헤쳐보자: 민감도 팩터 분석 (0) | 2023.05.26 |
댓글