실제로 델타 헤지 메카니즘이 작동하는지를 확인하기 위해 주가를 시뮬레이션하고 이 주가에 다른 델타헤지 전략을 백테스트해본다.
주가는 10,000에서 시작하여 1년간 움직이도록하고 256 매매일을 가정하여 하루 단위로 시뮬레이션한다. 이자율과 변동성은 각각 5%, 25%로 가정한다.
S0 = 10000
M = 256
r = 0.05
sigma = 0.40
T = 1
def stockprice_simulation(S0, T, sigma, r, M, seed=None):
if isinstance(sigma, np.ndarray):
if len(sigma) >= M:
sigma_vec = sigma[:M]
else:
raise ValueError("dimension error!")
else:
sigma_vec = sigma * np.ones(M + 1)
if seed is not None:
np.random.seed(seed)
dt = T / M
T_vec = np.linspace(T, 0, M + 1)
S_vec = np.zeros(M + 1)
S_vec[0] = S0
for t in range(1, M + 1):
S_vec[t] = S_vec[t - 1] * np.exp((r - 0.5 * sigma_vec[t - 1] ** 2) * dt
+ sigma_vec[t - 1] * np.sqrt(dt) * np.random.standard_normal())
S_vec = np.around(S_vec)
return S_vec.T, T_vec, sigma_vec
S_vec, T_vec, sigma_vec = stockprice_simulation(S0, T, sigma, r, M, seed=3)
옵션은 행사가 10,000원인 ATM(At-The-Money) 옵션으로 만기 1년을 가정한다. 이러한 옵션을 10개 매도한다.
def call_value(S, T, sigma, r, K):
if not hasattr(T, "__len__") and T == 0:
return np.maximum(S - K, 0)
d1 = ((np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T)))
d2 = ((np.log(S / K) + (r - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T)))
value = (S * sp.stats.norm.cdf(d1, 0, 1) - K * np.exp(-r * T) * sp.stats.norm.cdf(d2, 0, 1))
return value
def call_delta(S, T, sigma, r, K):
if not hasattr(T, "__len__") and T == 0:
return int(S > K)
d1 = ((np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T)))
delta = sp.stats.norm.cdf(d1, 0, 1)
return delta
K = 10000
NomialAmount = 10
V_vec = NomialAmount * call_value(S_vec, T_vec, sigma_vec, r, K)
D_vec = NomialAmount * call_delta(S_vec, T_vec, sigma_vec, r, K)
주가의 움직임과 이에 따른 옵션의 가치 및 델타를 그리면 아래와 같다.
plt.subplot(311)
plt.plot(S_vec, 'r-')
plt.subplot(312)
plt.plot(V_vec, 'g-')
plt.subplot(313)
plt.plot(D_vec, 'b-')
plt.show()
이를 평면상에 나타내면 아래와 같다.
maturities = np.linspace(0, 1, 13)
prices = np.linspace(8000, 12000, 101)
_T, S = np.meshgrid(maturities, prices)
V = np.zeros_like(S)
for i, s in enumerate(prices):
for j, t in enumerate(maturities):
V[i, j] = NomialAmount * call_value(s, t, sigma, r, K)
plt.plot(prices, V, alpha=0.5)
plt.xlabel('prices')
plt.ylabel('European call value')
plt.legend(["%d month" % i for i in range(13)], loc=2)
plt.hold(True)
plt.scatter(S_vec, V_vec, c=T_vec, cmap=mpl.cm.jet_r)
plt.plot(S_vec, V_vec, c="k", alpha=0.5)
plt.show()
델타헤지 백테스트 코드는 아래와 같다. 매일 다음과 같은 값을 구하여 이를 pandas 데이터 프레임에 저장한다.
def deltahedge_backtest(price, value, delta, time, option_price=None):
dtime = -np.diff(T_vec)
if option_price is None:
option_price = V_vec[0]
position = np.around(delta)
gamma = np.concatenate([position[:1], np.diff(position)])
cashflow = -gamma * price
cashflow[0] += option_price
cash = np.zeros_like(cashflow)
cash[0] = cashflow[0]
for i, (cf, dt) in enumerate(zip(cashflow[1:], dtime)):
cash[i + 1] = np.around(cash[i] * np.exp(r * dt) + cf)
stockvalue = position * price
totalvalue = cash + stockvalue
profit = totalvalue - value
df = pd.DataFrame({"time":time, "stock price":price, "option value": value, "option delta": delta,
"position": position, "cash flow": cashflow, "cash": cash, "stock value": stockvalue,
"total": totalvalue, "profit": profit},
columns=["time", "stock price", "option value", "option delta", "position",
"cash flow", "cash", "stock value", "total", "profit"])
return df
df = deltahedge_backtest(S_vec, V_vec, D_vec, T_vec)
헤지 결과는 다음과 같다.
df.head()
df.tail()
주식의 최종 가격은 11,089 원이므로 페이오프는 (11,089 - 10,000) x 10 = 10,890 원이다. 헤지 포트폴리오의 가치는 9,678원이므로 1,212원의 헤지 손실이 발생했다.
df1 = df[["stock price", "option value", "total"]]
df1.plot(figsize=(10, 10))
plt.show()
plt.figure(figsize=(9, 10))
plt.subplot(411)
plt.stem(df[["cash flow"]])
plt.title("cash flow")
plt.xlim(0, 260)
plt.subplot(412)
plt.plot(df[["cash"]])
plt.title("cash")
plt.xlim(0, 260)
plt.subplot(413)
plt.plot(df[["stock value"]])
plt.title("stock value")
plt.xlim(0, 260)
plt.subplot(414)
plt.plot(df[["total"]])
plt.title("hedge portfolio")
plt.xlim(0, 260)
plt.tight_layout()
plt.show()
이번에는 주가를 다르게 생성하여 백테스트를 한다. 이 경우에는 1,140원의 헤지 이익이 발생하였다.
seed = 7
S_vec, T_vec, sigma_vec = stockprice_simulation(S0, T, sigma, r, M, seed=seed)
V_vec = NomialAmount * call_value(S_vec, T_vec, sigma_vec, r, K)
D_vec = NomialAmount * call_delta(S_vec, T_vec, sigma_vec, r, K)
df = deltahedge_backtest(S_vec, V_vec, D_vec, T_vec)
df.tail()
df1 = df[["stock price", "option value", "total"]]
df1.plot()
plt.show()
옵션 델타 헤지가 반복되는 경우 헤지 손익이 어떤 통계적 특성을 보이는지 살펴보기 위해 100개의 서로 다른 주가를 테스트해본다.
N = 100
profits = np.zeros(N)
for i in xrange(N):
S_vec, T_vec, sigma_vec = stockprice_simulation(S0, T, sigma, r, M)
V_vec = NomialAmount * call_value(S_vec, T_vec, sigma_vec, r, K)
D_vec = NomialAmount * call_delta(S_vec, T_vec, sigma_vec, r, K)
df = deltahedge_backtest(S_vec, V_vec, D_vec, T_vec)
profits[i] = df.profit.iloc[-1]
sns.distplot(profits)
plt.show()
sns.distplot(profits/V_vec[0])
plt.show()
sm.qqplot(profits/V_vec[0], fit=True, line='45')
plt.axis("equal")
plt.show()
np.std(profits/V_vec[0])
헤지 손익이 표준 편차가 약 6.7% 인 정규 분포를 보임을 알 수 있다.
실제 주가의 변동성이 헤지 변동성과 달라지면 어떻게 되는지 살펴보자.
N = 100
profits = np.zeros(N)
for i in xrange(N):
S_vec, T_vec, sigma_vec = stockprice_simulation(S0, T, sigma * 1.1, r, M)
V_vec = NomialAmount * call_value(S_vec, T_vec, sigma_vec / 1.1, r, K)
D_vec = NomialAmount * call_delta(S_vec, T_vec, sigma_vec / 1.1, r, K)
df = deltahedge_backtest(S_vec, V_vec, D_vec, T_vec)
profits[i] = df.profit.iloc[-1]
np.mean(profits)
sns.distplot(profits)
plt.show()
옵션을 매도하고 변동성이 상대적으로 10% 증가 즉 40% -> 44% 로 절대 변동성 4%가 증가한 경우 평균 손실은 약 1,408 원이 된다.
이 값은 옵션의 베가에 의해 계산된 값 1,509 원과 유사하다.
def call_vega(S, T, sigma, r, K):
if not hasattr(T, "__len__") and T == 0:
return 0
d1 = ((np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T)))
vega = S * sp.stats.norm.pdf(d1, 0, 1) * np.sqrt(T)
return vega
call_vega(S0, T, 0.44, r, K) * NomialAmount * 0.04