본문 바로가기
금융공학

콜옵션 가격 변동 헤지(hedge) 시뮬레이션

by hustler78 2023. 5. 9.
728x90
반응형

 

 

이번 글은 콜옵션 가격 변동 리스크를 없애보자!

 

콜옵션 가격 변동 리스크를 없애보자!

이번 글은 콜옵션의 가격 변화를 쫓아가보자 #2 콜옵션의 가격 변화를 쫓아가보자 #2 이 글은 콜옵션의 가격 변화를 쫓아가보자 #1 콜옵션의 가격 변화를 쫓아가보자 #1 이 글은 테일러 전개 : 파

sine-qua-none.tistory.com

에서 이어집니다.

저번 글에서는 

 

콜옵션 매도포지션을 델타개의 기초자산 보유로 헤지(hedge)

 

하는 방법을 설명했습니다. 보다 엄밀히 말해, 우선 시간 변화에 따른 콜옵션의 가치 변동을 제껴 두고 기초자산 변화에 따른 콜옵션 가격 변동을 헤지하는 방법은

$$ c(t_i,S_i)-c(t_i,S_{i+1})  + \frac{\partial c(t_i,S_i)}{\partial S} (S_{i+1}-S_{i}) \approx 0$$

입니다. 여기서 $c$는 콜옵션의 가치, $\{t_i\}$들은 dialy 시점, $S_i$는 $t_i$ 시점의 기초자산 가격입니다.

즉, 콜옵션의 만기 $T$까지 daily 시점

$$0=t_0<t_1<\cdots<t_{N-1}<t_N=T$$

그리드를 사용하여 각 시점 $t_i$에서 콜옵션의 델타만큼 기초자산을 보유해 나가면 헤지가 된다는 의미입니다.

 

저번 글에서는 오직 하나의 주가 패스에 대해서만 헤지 테스트를 해 봤습니다.

하지만, 주가가 어떤 식으로 흘러갈지는 아무도 모르죠.

 

따라서 아주 많은 주가 패스를 발생시켜 각각의 경우에 안전하게 헤지가 되는지를 시뮬레이션해 봐야 합니다.

 

다음처럼요.

 

 

Python Code: 헤지 시뮬레이션

 

본 시뮬레이션 코드는 500개의 주가패스를 발생하여, 각각의 경우에 대해서 저번 글처럼 헤지 테스트를 해보는 것입니다.

 

1. 주가 패스를 500개 생성한다.

 

2. 각 생성된 주가 패스에 대해서 콜옵션 매도 포지션의 손익과 기초자산 보유 포지션의 손익을 구한다.

 

3. 각 주가패스의 만기 주가와 총 손익 사이의 관계 그래프를 그려본다.

 

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

# call option value, delta, gamma, speed 산출 함수
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
    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)))

    return val, delta, gamma, speed


def call_option_hedge_simulation():
    s0 = 100
    strike = 100
    maturity = 1
    rfr = 0.03
    div = 0
    vol = 0.3

    dt = 1 / 250             # 하루를 연환산한 단위
    sqrtdt = np.sqrt(dt)
    ntime = int(maturity / dt)  # 만기까지 ntime 등분된 grid
    time_series = np.linspace(0, maturity, ntime + 1)  # time sequence
    ttm_series = np.flip(time_series)                  # 잔존만기를 위해 flip(reversed)

    drift = rfr - div - 0.5 * vol ** 2     # 기초자산의 GBM drift term

    final_spot = []               # 만기 종가를 관리하는 list
    hedge_pl = []                 # hedge 최종 손익
    stock_path_example = []       # 주가 패스 몇 개 plotting 하기 위한 list
    nInteration = 500             # 주가 패스 갯수 : 500회
    for iter in range(nInteration):
        spot_series = [s0]
        dailyspot = s0
        for _ in range(len(time_series) - 1):
            dailyspot *= np.exp(drift * dt + vol * sqrtdt * np.random.normal())  #daily 주가패스
            spot_series.append(dailyspot)  # 주가패스 list

        if iter < 10:        # 처음 생성된 10개의 주가패스 저장
            stock_path_example.append(spot_series)

        callval_series = []
        calldelta_series = []
        for ttm, spot in zip(ttm_series, spot_series):       # 지난 글 참조
            call_info = CallOptionBS(spot, strike, ttm, rfr, div, vol)
            callval_series.append(call_info[0])
            calldelta_series.append(call_info[1])

        short_call_pl = np.diff(np.array(callval_series)) * -1
        long_spot_pl = np.diff(np.array(spot_series)) * np.array(calldelta_series)[:-1]
        net_pl = short_call_pl + long_spot_pl
        acc_net_pl = net_pl.cumsum()       # hedge 최종 손익 (지난 글 참조)

        final_spot.append(spot_series[-1]) # 만기 종가 list에 저장
        hedge_pl.append(acc_net_pl[-1])    # 만기시 hedge 손익 list에 저장

    hedge_mean = round(np.mean(hedge_pl), 2)  # hedge 손익의 평균값 
    hedge_std = round(np.std(hedge_pl), 2)    # hegde 손익의 표준편차

    print('hedge p&l mean : {}'.format(hedge_mean))
    print('hedge p&l std  : {}'.format(hedge_std))

    from matplotlib import gridspec
    fig = plt.figure(figsize=(20, 20))
    gs = gridspec.GridSpec(nrows=2, ncols=1, height_ratios=[1, 3])
    ax = [fig.add_subplot(gs[0]), fig.add_subplot(gs[1])]
    
    # ax[0] 에 10개의 주가패스 예제 plotting
    for i in range(10):
        ax[0].plot(time_series, stock_path_example[i])
    ax[0].set_title('stock path example') 
    
    # ax[1]에 만기종가 vs hedge손익 scattering 
    # ax[1]에 hedge pl의 평균선 plotting
    ax[1].scatter(final_spot, hedge_pl, label='hedge p&l', color='tomato')
    ax[1].hlines(hedge_mean, xmin=np.min(final_spot), xmax=np.max(final_spot), label='hedge p&l average',
                 color='royalblue')
    ax[1].text(np.max(final_spot), hedge_mean, hedge_mean)
    ax[1].legend()
    ax[1].set_title('hedge p&l simulation')
    plt.show()

if __name__ == '__main__':
    call_option_hedge_simulation()

 

결과를 보겠습니다.

 

hedge p&l mean : 1.43   # hedge 손익 평균
hedge p&l std  : 1.0    # hedge 손익 표준편차

주가를 100으로 산정했을 때, 헤지손익이 +1.43 정도 기대됩니다. 1.43% 정도의 헤지수익을 기대할 수 있죠.

표준편차도 생각 외로 작습니다. 거의 대부분의 주가 패스에 대해서 헤지가 잘 되고 있다는 의미겠죠.

 

다음은 그래프입니다.

 

 

 

상단 그래프는 주가패스 10개를 보여주는 것입니다. 이 10개의 주가패스에 대한 헤지 손익이 하단 scatter chart 어딘가에 찍혀 있겠죠.

 

하단의 빨간 점들이 바로 헤지 손익(성과)입니다. 평균을 많이 벗어나는 애들이 보이는데,  만기 종가가 행사가인 100 근처에서 끝날 때 쫌 튀는 거 같죠? 아무래도 만기로 다가갈수록 행사가 이하에서는 델타가 0, 행사가 이상에서는 델타가 1로서, 델타의 단차가 발생하여 헤지의 효율성이 떨어지는 것으로 보입니다.

 

 

더 알아볼 것은?

궁금증이 두 가지 정도 생깁니다.

 

첫 번째는, 과연 행사가 근처에서 주가가 끝날 때, 헤지 성과가 좀 떨어지는 것으로 보이는데요. Brownian Bridge 방법을 사용하여 강제적으로 만기종가가 행사가에서 끝날 때를 가정해서 시뮬레이션해볼 필요가 있을 것 같습니다.

 

두 번째는, 이처럼 콜옵션 매도포지션이 주가의 움직임으로 헤지가 되면, 

 

주가의 움직임 만으로 콜옵션 가격을 복제(replicating)할 수 있지 않을까? 

 

에 대한 것입니다. 

 

기초자산을 샀다 팔았다 하면서, 콜옵션 보유의 효과를 누릴 수 있지 않을까?

 

라는 의문이죠.

 

다음 글들을 통해 알아보도록 하겠습니다.

 

 

 

 

 

728x90
반응형

댓글