본문 바로가기
금융공학

콜옵션 가격 변동 헤지 시뮬레이션: 주가가 만기까지 횡보한다면..

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

 

 

 

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

 

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

이번 글은 콜옵션 가격 변동 리스크를 없애보자! 콜옵션 가격 변동 리스크를 없애보자! 이번 글은 콜옵션의 가격 변화를 쫓아가보자 #2 콜옵션의 가격 변화를 쫓아가보자 #2 이 글은 콜옵션의 가

sine-qua-none.tistory.com

에서 이어집니다.

 

저번 글에서는 콜옵션 매도포지션을 기초자산 매매로 헤지하는 시뮬레이션을 해 보고, 헤지운용이 잘 되는지를 따져보았죠.  500개의 주가 패스를 생성하여 헤지 운용을 시뮬레이션해 본 결과는 다음과 같았습니다.

전 글의 결과 차트, 콜옵션 매도포지션의 헤지가 잘 되는 모습

 

 

그런데 유독 헤지가 평균(위 그림의 파란 선, 1.43)에서 벗어난 특이점들이 100 근처, 즉 행사가 근처에서 많이 보이죠?

행사가 근처에서 델타의 변화가 있고, 이러한 델타의 불안정성에 기인하는 것이 아닌가 싶어 

 

만기 종가가 행사가에서 끝날 때는, 과연 헤지가 잘 되려는가?

 

가 궁금하던 차였습니다. 즉, $S_0$에서 시작한 주가가 다시 $S_0$에서 끝나는 경우, 다시 말해 횡보하는 모습일 때 헤지가 어떻게 되는지 궁금합니다.

 

이번 글에서는 이러한 상황에 대해 헤지 시뮬레이션을 해보도록 하겠습니다. 만기 종가가 강제적으로 행사가에서 끝나는 경우를 만들기 위해 Brownian Bridge라는 기법을 사용해야 합니다.

 

 

Brownian Bridge 기법으로 원하는 주가패스 만들기

 

해당 내용에 대해서는 예전 글 Brownian Bridge: 미래 주가타깃을 정조준하는 주가패스를 만들자.

 

Brownian Bridge: 미래 주가타깃을 정조준하는 주가패스를 만들자.

이번 글에서는 Brownian Bridge를 사용하여 주가 패스를 만드는 과정을 알아보겠습니다. Brown Bridge를 만드는 방법은 [금융공학] - Brownian bridge: 1년 뒤 주가 타깃을 정조준하는 일일주가의 움직임을 모

sine-qua-none.tistory.com

를 참고해 주시기 바랍니다.  Brownian Bridge 에 대해 간략하게 보습을 해 보겠습니다. 일단 Bridge라는 단어 뜻 자체가 시작점과 도착점을 연결한다는 의미가 강합니다.

 

목표는 이겁니다.

Our Goal : 

$S_0$에서 시작한 주가 패스가 반드시 $S_1$으로 끝나도록 하는 GBM 주가 패스를 생성하라.

 

주가 모델을 GBM 모델 즉,

$$ dS_t/S_t =(r-q)dt +\sigma dZ_t$$

라 합시다($r,q,\sigma$는 각각 무위험 이자율, 연속배당률, 변동성) 그러면 임의의 시점 $t$에 대해,

$$ S_t = S_0 \exp \left( (r-q-\frac12\sigma^2)t +\sigma Z_t \right) \tag{1}$$

이죠. $Z_t$는 Wiener process 로서, 평균은 $0$, 표준편차가 $\sqrt{t}$인 정규분포를 따르죠.

 

Brownian Bride 만드는 절차는 아래처럼 합니다.

 

Step1.
만기 시점 Wiener값 뽑기
식 (1)에서 어떤 난수 $Z_T\sim\mathcal{N}(0,\sqrt{T}^2)$에 대해,
$$ S_T = S_0 \exp \left( (r-q-\frac12\sigma^2)T+\sigma  Z_T \right) $$
입니다.
우리는 만기종가가 현재가격가 같은 경우, 즉, $S_T=S_0$인 상황을 원하므로 위의 식에서
$$ Z_T =-\frac{(r-q-\frac12\sigma^2)T}{\sigma} \tag{2}$$
를 얻습니다.

Step2.
만기까지 time grid
시점 grid를 균등하게 만듭니다.
$$ 0=t_0<t_1<\cdots <t_{N-1}<T_N=T$$
시점 사이 간격이 $\Delta t = \frac1{250}$, 즉 일간 시점으로 합니다. 
Step3.
난수 추출
$$ W(t_0)=0~,~ W(t_{i+1}) =W(t_i) +\epsilon_i \sqrt{\Delta t}\tag{3}$$
를 만족하는 $W(t_i)$ 들을 찾습니다. $\epsilon$은 표준정규분포를 따르는 난수입니다.
Step4.
Brownian Bridge 완성
식(2)에서 구한 $Z_T$와 식(3)에서 구한 $\{W(t_i)\}$들을 가지고 
$$B(t_i)=\frac{t_i}{T}Z_T+W(t_i)-\frac{t_i}{T}W(T)\tag{4}$$
을 만듭니다.
$W(T) =W(t_N)$ 입니다.
Step5.
주가 패스 완성!
식 (4)에서 구한 $\{B(t_i)\}$들을 이용하여
$$S(t_i)=S_0 \exp\left((r-q-\frac12\sigma^2)t_i + \sigma B(t_i)\right)$$
을 만듭니다. 그러면 

$\{S(t_i)\}$들은 정확히 $S_0$에서 시작해서 만기 떄 $S_0$에서 끝나는 주가 패스가 됩니다.

 

이제 이렇게 만든 주가패스들에 대해서 콜옵션 매도 포지션을 헤지해보죠.

 

 

 

Python Code

 

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


# call option의 value, delta, gamme, 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_brownian_bridge():
    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)
    time_series = np.linspace(0, maturity, ntime + 1)
    ttm_series = np.flip(time_series)

    drift = rfr - div - 0.5 * vol ** 2

    final_spot = []
    hedge_pl = []
    stock_path_example = []

    nInteration = 50
    target_rn = -drift * maturity / (vol) # 위의 설명 step1의 Z_T 참고

    for iter in range(nInteration):

        rn = np.zeros(ntime + 1)
        for i in range(ntime):
            rn[i + 1] = rn[i] + np.sqrt(dt) * np.random.normal() # 위의 글 W(t_i) 만드는 과정 (step3)
            # 위의 글 brownian bridge만드는 과정(step4)
            bbridge = time_series / maturity * target_rn + rn - time_series / maturity * rn[-1] 
            
        # 위의 글에서 주가패스 만드는 과정(step5)
        spot_series = s0 * np.exp(drift * time_series + vol * bbridge)

        if iter < 10:   # 10개 sample 주가패스 그려보기
            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()

        final_spot.append(spot_series[-1])
        hedge_pl.append(acc_net_pl[-1])

    hedge_mean = round(np.mean(hedge_pl), 2)
    hedge_std = round(np.std(hedge_pl), 2)
    hedge_max = round(np.max(hedge_pl), 2)
    hedge_min = round(np.min(hedge_pl), 2)


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

    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])]
    for i in range(10):
        ax[0].plot(time_series, stock_path_example[i])
    ax[0].set_title('stock path example')
    
    # hedge 성과를 histogram으로 그려보기
    cnt, bins, patches = ax[1].hist(hedge_pl, bins=10, density=True, rwidth=0.2, orientation='horizontal',
                                    label='hedge p&l histotram',
                                    alpha=0.5, color='royalblue')
    # hedge 성과의 min, max를 수평선으로 plotting
    ax[1].hlines(hedge_max, xmin=0, xmax=1, color='red', linestyles='dashdot', label='hedge p&l max')
    ax[1].hlines(hedge_min, xmin=0, xmax=1, color='green', linestyles='dashdot', label='hedge p&l min')

    ax[1].legend()
    # histogram을 이루는 막대들(patches) 중, 첫번째, 마지막 막대 빨간색으로 강조
    plt.setp(patches[0], color='red')
    plt.setp(patches[-1], color='red')
    ax[1].set_title('hedge p&l simulation')
    plt.show()

 

결과를 볼까요?

 

hedge p&l mean : 2.27
hedge p&l std  : 1.06
hedge p&l max  : 4.77
hedge p&l min  : -1.76

 

생각 외로 표준편차는 작네요.  하지만 헤지성과는 임의의 주가패스에 대해 했을 때 보다 더 좋습니다.  밑의 더보기를 클릭해 보세요.

 

더보기

저번글에서 얻었던 결과는

 

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

 

였습니다. 이 결과는 만기 종가가 자유롭게 끝나는 주가 패스에 대해 시뮬레이션한 결과죠.

 

 

다음은 차트입니다.

 

Brownian Bridge 기법으로 주가 패스를 생성한 결과가 첫 번째 그래프에 여실히 드러나죠. 시작 주가와 만기주가가 똑같은 주가 패스를 얻었습니다.

 

아래 차트는 헤지 성과를 히스토그램으로 표시한 것입니다. 또, 헤지 성과의 최대, 최솟값도 표기해 봤습니다. 상대적으로 헤지 성과가 +4.77% 까지 올라가기까지 하네요.

 

 

의문점

이 글에서 헤지 성과 평균은 +2.27,  저번글에서의 평균은 +1.43입니다.

 

헤지라는 것이 본래 콜옵션 가격 변화를 추적하며 따라가는 것이기 때문에 ( 다른 지난 글 참조) 헤지 성과는 0 근처에 나오는 것이 타당해 보입니다. 그런데 결과는 0 근처가 아닌 평균적으로 (+)가 나고 있죠.

 

최근의 글 들에서 일단 가정한 것이

 

잔존만기 줄어드는 효과, 즉, 시점 변동 효과는 무시하자

 

는 것이었습니다. 이 가정이 영향을 미쳤던 것일까요?

 

다음 글에서 알아보도록 하겠습니다.

728x90
반응형

댓글