본문 바로가기
금융공학

몬테카를로 시뮬레이션과 그릭의 안정성 #1

by hustler78 2023. 7. 7.
728x90
반응형

 

 

 

이번 글은 2023.06.23 - [금융공학] - 그릭을 수치 해석적인 방법으로 해결하기

 

그릭을 수치 해석적인 방법으로 해결하기

이번 글은 2023.04.27 - [금융공학] - 델타, 감마, 스피드! 콜옵션의 가격 변화를 쫓아가보자 #1 델타, 감마, 스피드! 콜옵션의 가격 변화를 쫓아가보자 #1 이 글은 테일러 전개 : 파생상품 헤지의 준비

sine-qua-none.tistory.com

에서 이어집니다. 저번 글에서는 민감도를 구할 때, 굳이 공식을 편미분 하지 않고 유한 차분의 형태로 수치 해석적으로 구하는 방법을 설명했습니다. 이 방법이 강력한 이유는

 

 

굳이 공식(closed form)이 없더라도, 그릭들을 다 뽑아낼 수 있다! 뭘로? 유한차분으로~ 

 

입니다. Closed form이 있어야 편미분을 해서 그릭들을 구하지! 라는 편협한 생각을 접을 수 있죠.

 

 

 

만일 옵션 공식(Black Scholes Formula)가 없었다면?

 

지난 글에서는 옵션 공식이 있었기 때문에 자연스럽게 편미분이나 수치 해석을 통하여 민감도를 구할 수 있었습니다. 만일 공식이 없는 상황이라고 가정해 보죠(대다수의 파생상품은 그 가치를 구하는 공식이 없습니다.)

이때에는

 

몬테카를로 시뮬레이션(MonteCarlo Simulation)

 

이라는 방법을 써야 합니다. 예전 글(옵션 #6. 옵션 프리미엄 구하기 실습: MonteCarlo Simulation)을 참고해 보시기 바랍니다.  MC로 구하는 옵션 가격은 아래의 코드로 수할 수 있습니다.

 

 

 

콜옵션 가격 (feat. MC)

 

MonteCarlo 방법으로 콜옵션 가격을 구하는 로직은 위의 링크된 글을 살펴보시기 바랍니다.

 

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


# call option value 구하는 함수
def callvalue_by_MC(svalue, strike, maturity, rfr, div, vol, nIter):
    drift = (rfr - div - 0.5 * vol ** 2) * maturity   # 주가 패스의 drift term
    volsqrtmat = vol * np.sqrt(maturity)              # 주가 패스생성의 diffusion term

    rvec = np.random.normal(size=nIter)       # nIter (시뮬레이션 횟수) 만큼의 random number
    s_maturity = svalue * np.exp(drift + volsqrtmat * rvec)   # 만기 종가 sample 산출
    payoff = np.array([np.maximum(0, s_mat - strike) for s_mat in s_maturity])  # 각 종가 sample별 만기 payoff
    dfactor = np.exp(-rfr * maturity)         # disounct factor 산출
    opt_value = payoff.mean() * dfactor       # 만기 payoff의 평균(기댓값) * 할인율

    return opt_value

# 위의 함수를 토대로 민감도(delta, gamma, speed) 모두 구하는 함수
# 구하는 과정은 수치해석적으로 유한차분법을 통해 구한다.
def callvalueGreeks_MC(svalue, strike, maturity, rfr, div, vol, nIter):
    ds = svalue * 0.01
    val = callvalue_by_MC(svalue, strike, maturity, rfr, div, vol, nIter)
    val_up = callvalue_by_MC(svalue + ds / 2, strike, maturity, rfr, div, vol, nIter)
    val_up2 = callvalue_by_MC(svalue + ds, strike, maturity, rfr, div, vol, nIter)
    val_down = callvalue_by_MC(svalue - ds / 2, strike, maturity, rfr, div, vol, nIter)
    val_down2 = callvalue_by_MC(svalue - ds, strike, maturity, rfr, div, vol, nIter)

    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)

    return val, delta, gamma, speed

 

코드 중간에 나오는 로직은 모두 전 글(그릭을 수치 해석적인 방법으로 해결하기)을 참고하여 구하였습니다.

 

 

이제 위 함수에서 얻은 가격 및 민감도가 Closed form으로 구한 가격/민감도와 맞는지를 알아봐야 하겠죠(이런 걸 크로스 체크라 합니다.)  

위의 코드가 잘 짜였다면, 정답인 Closed forma의 결과와 엇비슷하게 나와야 합니다. 그런지 한 번 보겠습니다.

 

 

 

Python code : MC로 구한 콜옵션 가격/민감도 검증

 

def analysis_call_option_greeks_MC():
    strike = 100
    maturity = 1
    rfr = 0.03
    div = 0
    vol = 0.3
    nIter = 10000

    spot_array = np.arange(50, 150 + 1, 1)  #stock은 50 - 150 까지 1 간격으로 생성
    val_list = []   # 각 stock 별 call value를 담는 list
    delta_list = [] # delta
    gamma_list = [] # gamma
    speed_list = [] # speed 를 닮는 list
    
    for s in spot_array:
        res_analytic = CallOptionBS(s, strike, maturity, rfr, div, vol)        # call closed form
        res_mc = callvalueGreeks_MC(s, strike, maturity, rfr, div, vol, nIter) # call MC
        val_list.append([res_analytic[0], res_mc[0]])   # [closed form value, MC value] list 
        delta_list.append([res_analytic[1], res_mc[1]])
        gamma_list.append([res_analytic[2], res_mc[2]])
        speed_list.append([res_analytic[3], res_mc[3]])

    val_array = np.array(val_list)      # list의 array 화
    delta_array = np.array(delta_list)
    gamma_array = np.array(gamma_list)
    speed_array = np.array(speed_list)

    from matplotlib import gridspec
    fig = plt.figure(figsize=(30, 30))
    gs = gridspec.GridSpec(nrows=2, ncols=2)
    ax = [[fig.add_subplot(gs[0, 0]), fig.add_subplot(gs[0, 1])],
          [fig.add_subplot(gs[1, 0]), fig.add_subplot(gs[1, 1])]
          ]                             # 2*2 4구역의 chart를 그릴 예정

    # value_graph
    ax[0][0].plot(spot_array, val_array[:, 0], color='lightgray', linewidth=5, label='value_closedform')
    ax[0][0].plot(spot_array, val_array[:, 1], color='black', linewidth=2, linestyle='--'
                  , label='value numerical')
    ax[0][0].legend()

    # delta graph
    ax[0][1].plot(spot_array, delta_array[:, 0], color='lightgray', linewidth=5, label='delta_closedform')
    ax[0][1].plot(spot_array, delta_array[:, 1], color='blue', linewidth=2, linestyle='--'
                  , label='delta numerical')
    ax[0][1].legend()
    
    # close form delta와 MC delta  배열 중 차이가 가장 큰 값을 print out
    print('Maximum difference between closed form delta and MC delta : {:.10f}'.format(
        np.max(np.abs(delta_array[:, 1] - delta_array[:, 0]))))

    # gamma graph
    ax[1][0].plot(spot_array, gamma_array[:, 0], color='lightgray', linewidth=5, label='gamma_closedform')
    ax[1][0].plot(spot_array, gamma_array[:, 1], color='tomato', linewidth=2, linestyle='--', label='gamma numerical')
    ax[1][0].legend()
    print('Maximum difference between closed form gamma and MC gamma : {:.10f}'.format(
        np.max(np.abs(gamma_array[:, 1] - gamma_array[:, 0]))))
    ax[1][0].legend()

    # speed graph
    ax[1][1].plot(spot_array, speed_array[:, 0], color='lightgray', linewidth=5, label='speed_closedform')
    ax[1][1].plot(spot_array, speed_array[:, 1], color='green', linewidth=2, linestyle='--', label='speed numerical')
    ax[1][1].legend()
    print('Maximum difference between closed form speed and MC speed : {:.10f}'.format(
        np.max(np.abs(speed_array[:, 1] - speed_array[:, 0]))))
    ax[1][1].legend()

    plt.show()

 

그럼 결과를 한 번 보겠습니다.

 

 

Maximum difference between closed form delta and MC delta : 0.7694295983
Maximum difference between closed form gamma and MC gamma : 1.2779184401
Maximum difference between closed form speed and MC speed : 7.0555037445

 

 

위의 그림에서 회색 실선들이 각각 value, delta, gamma, vega입니다.  그리고 점 선들은 각각 MC의 결과를 통해 얻은 결과입니다. 콜옵션의 가격은 그런대로 잘 맞아 들어가죠. 그런데, 델타, 감마, 스피드는 어떻습니까? 값이 불안정하고 들쑥날쑥합니다 

얼마나 들쑥날쑥 이은 지, 회색 선(정답선)이 거의 0처럼 보일 정도입니다.

 

이래서는 못쓸 것 같습니다. 

과연 이러한 현상이 일어나는 이유는 무엇일까요? 다음 글에서 알아보도록 하겠습니다.

728x90
반응형

댓글