- Authors
- Name
- 들어가며: 파생상품이란
- 선물(Futures) 기초
- 옵션(Options) 기초
- Black-Scholes 모델과 옵션 가격 결정
- 그릭스(Greeks) 심층 분석
- 헤징 전략
- Python 구현: Monte Carlo 시뮬레이션
- 실패 사례: 역사가 가르치는 교훈
- 개인 투자자 주의사항
- 참고자료

들어가며: 파생상품이란
파생상품(Derivatives)이란 주식, 채권, 원자재, 환율 같은 기초자산(underlying asset)의 가격에서 그 가치가 파생되는 금융 상품을 말한다. 기초자산을 직접 사고팔지 않고도 해당 자산의 가격 변동에 따른 이익이나 손실에 노출될 수 있는 계약이다. 파생상품 시장의 명목 가치(notional value)는 2025년 기준 약 600조 달러가 넘으며, 이는 전 세계 GDP의 약 6배에 달하는 규모다.
파생상품은 크게 네 가지로 분류한다. 선물(Futures), 옵션(Options), 스왑(Swaps), 선도(Forwards)다. 이 글에서는 개인 투자자가 접근할 수 있고, 개발자가 수학적 모델링을 실습할 수 있는 선물과 옵션을 중심으로 다룬다.
파생상품은 원래 위험 관리(헤징)를 위해 탄생했다. 농부가 수확 전 밀의 판매 가격을 확정하고, 항공사가 연료비 급등 위험을 관리하기 위해 사용했다. 하지만 높은 레버리지와 복잡한 구조 때문에, 잘못 사용하면 원금의 수 배에 달하는 손실이 발생할 수 있다. LTCM 사태(1998년)나 니켈 숏스퀴즈(2022년) 같은 역사적 사건은 이 위험을 극명하게 보여준다.
이 글의 목표는 파생상품의 기초 메커니즘을 이해하고, 수학적 가격 결정 모델을 Python으로 직접 구현하며, 주요 헤징 전략의 원리와 한계를 파악하는 것이다.
선물(Futures) 기초
선물 계약의 구조
선물 계약은 미래의 특정 날짜에, 특정 가격으로, 특정 자산을 사거나 팔기로 하는 표준화된 계약이다. 핵심 요소는 다음과 같다.
기초자산: 계약의 대상이 되는 자산. 원유, 금, 밀 같은 원자재는 물론, 주가지수, 국채, 환율도 포함된다. 코스피200 선물, S&P 500 E-mini 선물이 대표적이다.
계약 규모(Contract Size): 선물 1계약이 대표하는 기초자산의 양. 코스피200 선물은 1포인트당 25만 원, E-mini S&P 500 선물은 1포인트당 50달러다.
만기일(Expiration Date): 계약이 종료되는 날짜. 보통 3, 6, 9, 12월의 두 번째 목요일이다. 만기일에는 실물 인도(physical delivery) 또는 현금 결제(cash settlement)로 정산된다.
마진(Margin)과 일일정산(Mark-to-Market)
선물 거래의 가장 중요한 특징은 마진 시스템이다. 선물을 매수할 때 계약 전체 금액을 지불하는 것이 아니라, 일정 비율의 증거금(margin)만 예치한다. 이 증거금은 보통 계약 가치의 5-15% 수준이다.
개시 증거금(Initial Margin): 포지션을 처음 열 때 예치해야 하는 최소 금액. 예를 들어 코스피200 선물이 350포인트이고 개시 증거금이 10%라면, 1계약당 350 x 250,000 x 10% = 8,750,000원이 필요하다.
유지 증거금(Maintenance Margin): 포지션을 유지하기 위한 최소 잔고. 일반적으로 개시 증거금의 75% 수준이다.
마진콜(Margin Call): 일일정산으로 인해 계좌 잔고가 유지 증거금 아래로 떨어지면, 거래소는 추가 증거금을 요구한다. 이를 충족하지 못하면 포지션이 강제 청산된다.
일일정산은 매일 거래 종료 후 선물 가격의 변동분을 매수자와 매도자의 계좌에 즉시 반영하는 제도다. 가격이 유리하게 변하면 계좌에 돈이 입금되고, 불리하게 변하면 출금된다. 이 메커니즘은 결제 불이행 위험을 대폭 줄여주지만, 투자자 입장에서는 일시적인 가격 변동에도 실제 현금 흐름이 발생한다는 점을 유의해야 한다.
선물의 이론 가격: 보유비용 모델
선물의 이론 가격은 보유비용 모델(Cost of Carry Model)로 결정된다.
여기서 는 선물 가격, 는 현물 가격, 은 무위험 이자율, 는 배당수익률, 는 만기까지의 기간(연 단위)이다. 현물 가격과 선물 가격의 차이를 베이시스(basis)라 하며, 만기에 가까워질수록 이 차이는 0으로 수렴한다.
옵션(Options) 기초
콜 옵션과 풋 옵션
옵션은 특정 자산을 미래의 특정 시점에 미리 정한 가격(행사가)으로 살 수 있는 권리(콜 옵션) 또는 팔 수 있는 권리(풋 옵션)를 매수하는 계약이다. 선물과의 가장 큰 차이는 "의무"가 아닌 "권리"라는 점이다. 옵션 매수자는 불리할 경우 권리를 포기(행사하지 않음)할 수 있지만, 그 대가로 프리미엄을 지불해야 한다.
콜 옵션(Call Option): 기초자산을 행사가격에 매수할 수 있는 권리. 기초자산 가격이 오를수록 가치가 올라간다. 만기 시 손익은 다. 여기서 는 만기 시 기초자산 가격, 는 행사가격, 는 프리미엄이다.
풋 옵션(Put Option): 기초자산을 행사가격에 매도할 수 있는 권리. 기초자산 가격이 떨어질수록 가치가 올라간다. 만기 시 손익은 다.
내재가치와 시간가치
옵션의 가격(프리미엄)은 내재가치(Intrinsic Value)와 시간가치(Time Value)의 합으로 구성된다.
내재가치: 지금 당장 옵션을 행사했을 때 얻을 수 있는 이익. 콜 옵션의 내재가치는 , 풋 옵션의 내재가치는 이다. 내재가치가 양수이면 내가격(In-the-Money, ITM), 0이면 등가격(At-the-Money, ATM), 음수이면 외가격(Out-of-the-Money, OTM)이라 한다.
시간가치: 만기까지 남은 기간 동안 옵션의 가치가 변할 수 있는 가능성에 대한 프리미엄. 만기가 가까워질수록 시간가치는 감소하며(time decay), 만기일에는 0이 된다. 이 감소는 선형이 아니라 가속적이며, 만기 30일 이내에 가장 급격하게 진행된다.
유럽형 vs. 미국형 옵션
유럽형 옵션(European Option)은 만기일에만 행사할 수 있고, 미국형 옵션(American Option)은 만기일 이전 언제든지 행사할 수 있다. 조기 행사가 가능한 미국형 옵션이 항상 유럽형보다 가치가 같거나 높다. 한국 거래소의 코스피200 옵션은 유럽형이고, 미국의 주식 옵션은 대부분 미국형이다.
Black-Scholes 모델과 옵션 가격 결정
1973년 Fischer Black과 Myron Scholes가 발표한 Black-Scholes-Merton(BSM) 모델은 옵션 가격 결정의 토대가 되는 공식이다. Scholes와 Merton은 이 업적으로 1997년 노벨 경제학상을 수상했다(Black은 1995년에 사망).
BSM 공식
유럽형 콜 옵션과 풋 옵션의 가격은 다음과 같이 계산된다.
여기서:
각 변수의 의미는 다음과 같다.
- : 기초자산의 현재 가격
- : 행사가격 (Strike Price)
- : 만기까지 남은 기간 (연 단위)
- : 무위험 이자율 (연율)
- : 기초자산의 변동성 (연율, 표준편차)
- : 표준정규분포의 누적분포함수
BSM 모델의 가정과 한계
BSM 모델은 다음의 가정을 전제로 한다. 첫째, 기초자산 가격은 기하 브라운 운동(Geometric Brownian Motion)을 따른다. 둘째, 변동성은 상수다. 셋째, 무위험 이자율은 상수다. 넷째, 거래 비용과 세금이 없다. 다섯째, 배당이 없다(배당 수정 모델도 존재). 여섯째, 공매도가 자유롭다.
현실에서 이 가정들은 대부분 위반된다. 특히 변동성이 상수라는 가정은 실제 시장에서 관찰되는 "변동성 스마일(Volatility Smile)"과 모순된다. 행사가격에 따라 내재변동성이 달라지는 현상을 설명하지 못하는 것이다. 이를 보완하기 위해 확률적 변동성 모델(Heston 모델), 점프 확산 모델(Merton Jump Diffusion) 등이 개발되었다.
코드 예제 1: Black-Scholes 가격 계산
import numpy as np
from scipy.stats import norm
from dataclasses import dataclass
@dataclass
class BSMParams:
"""Black-Scholes-Merton 모델 파라미터"""
S: float # 기초자산 현재 가격
K: float # 행사가격
T: float # 만기까지 기간 (연 단위)
r: float # 무위험 이자율 (연율)
sigma: float # 변동성 (연율)
q: float = 0 # 배당수익률
class BlackScholesModel:
"""Black-Scholes-Merton 옵션 가격 결정 모델"""
def __init__(self, params: BSMParams):
self.p = params
self._d1 = self._calc_d1()
self._d2 = self._calc_d2()
def _calc_d1(self) -> float:
p = self.p
numerator = (
np.log(p.S / p.K)
+ (p.r - p.q + 0.5 * p.sigma**2) * p.T
)
denominator = p.sigma * np.sqrt(p.T)
return numerator / denominator
def _calc_d2(self) -> float:
return self._d1 - self.p.sigma * np.sqrt(self.p.T)
def call_price(self) -> float:
"""유럽형 콜 옵션 가격"""
p = self.p
return (
p.S * np.exp(-p.q * p.T) * norm.cdf(self._d1)
- p.K * np.exp(-p.r * p.T) * norm.cdf(self._d2)
)
def put_price(self) -> float:
"""유럽형 풋 옵션 가격"""
p = self.p
return (
p.K * np.exp(-p.r * p.T) * norm.cdf(-self._d2)
- p.S * np.exp(-p.q * p.T) * norm.cdf(-self._d1)
)
def put_call_parity_check(self) -> dict:
"""풋-콜 패리티 검증: C - P = S*e^(-qT) - K*e^(-rT)"""
p = self.p
call = self.call_price()
put = self.put_price()
lhs = call - put
rhs = p.S * np.exp(-p.q * p.T) - p.K * np.exp(-p.r * p.T)
return {
"call_price": round(call, 4),
"put_price": round(put, 4),
"C - P": round(lhs, 4),
"S*e^(-qT) - K*e^(-rT)": round(rhs, 4),
"parity_holds": abs(lhs - rhs) < 1e-10,
}
# 사용 예시
params = BSMParams(S=100, K=105, T=0.5, r=0.05, sigma=0.2)
bsm = BlackScholesModel(params)
print(f"콜 옵션 가격: {bsm.call_price():.4f}")
print(f"풋 옵션 가격: {bsm.put_price():.4f}")
print(f"풋-콜 패리티: {bsm.put_call_parity_check()}")
그릭스(Greeks) 심층 분석
그릭스는 옵션 가격이 각 입력 변수의 변화에 얼마나 민감한지를 측정하는 지표다. 옵션 포지션의 위험을 정량화하고 헤징 전략을 수립하는 데 핵심적인 역할을 한다.
코드 예제 2: 그릭스 계산 구현
import numpy as np
from scipy.stats import norm
class GreeksCalculator:
"""옵션 그릭스(1차, 2차) 계산기"""
def __init__(self, S: float, K: float, T: float, r: float, sigma: float):
self.S, self.K, self.T, self.r, self.sigma = S, K, T, r, sigma
self.d1 = (
(np.log(S / K) + (r + 0.5 * sigma**2) * T) / (sigma * np.sqrt(T))
)
self.d2 = self.d1 - sigma * np.sqrt(T)
def delta(self, option_type: str = "call") -> float:
"""Delta: 기초자산 가격 변화에 대한 옵션 가격의 민감도"""
if option_type == "call":
return norm.cdf(self.d1)
return norm.cdf(self.d1) - 1
def gamma(self) -> float:
"""Gamma: Delta의 변화율"""
return norm.pdf(self.d1) / (self.S * self.sigma * np.sqrt(self.T))
def theta(self, option_type: str = "call") -> float:
"""Theta: 시간 경과에 따른 옵션 가격 감소율 (1일 단위)"""
common = -(self.S * norm.pdf(self.d1) * self.sigma) / (2 * np.sqrt(self.T))
if option_type == "call":
val = common - self.r * self.K * np.exp(-self.r * self.T) * norm.cdf(self.d2)
else:
val = common + self.r * self.K * np.exp(-self.r * self.T) * norm.cdf(-self.d2)
return val / 365
def vega(self) -> float:
"""Vega: 변동성 1% 변화에 대한 옵션 가격의 민감도"""
return self.S * norm.pdf(self.d1) * np.sqrt(self.T) / 100
def rho(self, option_type: str = "call") -> float:
"""Rho: 이자율 1% 변화에 대한 옵션 가격의 민감도"""
if option_type == "call":
return self.K * self.T * np.exp(-self.r * self.T) * norm.cdf(self.d2) / 100
return -self.K * self.T * np.exp(-self.r * self.T) * norm.cdf(-self.d2) / 100
def summary(self, option_type: str = "call") -> dict:
return {
"Delta": round(self.delta(option_type), 6),
"Gamma": round(self.gamma(), 6),
"Theta (daily)": round(self.theta(option_type), 6),
"Vega (per 1%)": round(self.vega(), 6),
"Rho (per 1%)": round(self.rho(option_type), 6),
}
greeks = GreeksCalculator(S=100, K=100, T=0.25, r=0.05, sigma=0.2)
print("=== ATM 콜 옵션 그릭스 ===")
for name, value in greeks.summary("call").items():
print(f" {name}: {value}")
그릭스 비교표
| 그릭스 | 수학적 정의 | 의미 | 콜 범위 | 풋 범위 | 매매 시사점 |
|---|---|---|---|---|---|
| Delta | 주가 1원 변동 시 옵션 가격 변동 | 0~1 | -1~0 | 헤징에 필요한 기초자산 수량 결정 | |
| Gamma | 주가 변동 시 Delta의 변화율 | 항상 양수 | 항상 양수 | ATM 근처에서 최대, 리밸런싱 빈도 결정 | |
| Theta | 하루 경과 시 옵션 가격 감소분 | 대부분 음수 | 대부분 음수 | 옵션 매도자에게 유리, 만기 근처 급가속 | |
| Vega | 변동성 1% 변화 시 옵션 가격 변동 | 항상 양수 | 항상 양수 | 변동성 확대 예상 시 매수, 축소 시 매도 | |
| Rho | 이자율 1% 변화 시 옵션 가격 변동 | 양수 | 음수 | 장기 옵션에서 중요, 단기에서는 미미 |
그릭스의 실전 활용
Delta 헤징: 포트폴리오의 Delta를 0으로 만들어 기초자산 가격 변동에 중립적인 포지션을 구축한다. 콜 옵션 100계약 매도(Delta = 0.5)라면, 기초자산 50주를 매수하면 Delta Neutral이 된다. 하지만 Gamma 때문에 기초자산 가격이 변할 때마다 Delta도 변하므로, 지속적인 리밸런싱이 필요하다.
Theta 활용: 옵션 매도 전략(Covered Call, Iron Condor 등)은 시간 가치 감소(Theta)에서 수익을 얻는다. 만기가 30-45일 남은 옵션에서 Theta 감소가 가장 효율적이다.
Vega 트레이딩: 변동성이 과소평가되었다고 판단하면 Vega 양수 포지션(옵션 매수)을, 과대평가되었다고 판단하면 Vega 음수 포지션(옵션 매도)을 구축한다. 실적 발표 전후의 내재변동성(IV) 변화를 활용하는 것이 대표적이다.
헤징 전략
헤징은 기존 포지션의 위험을 줄이기 위해 파생상품을 활용하는 전략이다. 완벽한 헤징은 불가능하지만, 리스크를 통제 가능한 수준으로 관리할 수 있다.
Covered Call (커버드 콜)
기초자산을 보유하면서 콜 옵션을 매도하는 전략이다. 콜 옵션 프리미엄을 수취하여 추가 수익을 얻지만, 기초자산 가격이 행사가 이상으로 급등하면 그 이상의 이익을 포기해야 한다. 완만한 상승 또는 횡보 시장에서 효과적이다.
Protective Put (프로텍티브 풋)
기초자산을 보유하면서 풋 옵션을 매수하는 전략이다. 보험과 유사한 개념으로, 기초자산 가격이 행사가 이하로 하락해도 행사가에 매도할 수 있는 권리가 보장된다. 하락 위험을 제한하지만 풋 옵션 프리미엄만큼 비용이 발생한다.
Collar (칼라)
Protective Put + Covered Call의 조합이다. 기초자산 보유 + 풋 옵션 매수 + 콜 옵션 매도로 구성된다. 풋 매수 비용을 콜 매도 수익으로 상쇄하여 저비용 헤징이 가능하지만, 상방 이익도 제한된다.
Straddle (스트래들)
같은 행사가, 같은 만기의 콜과 풋을 동시에 매수하는 전략이다. 방향에 관계없이 기초자산 가격이 크게 변동하면 수익이 발생한다. 실적 발표, 금리 결정 등 큰 변동이 예상되지만 방향을 예측하기 어려울 때 사용한다. 비용(프리미엄)이 크기 때문에 충분한 가격 변동이 없으면 양쪽 모두 손실이 된다.
코드 예제 3: 헤징 전략별 손익 다이어그램
import numpy as np
import matplotlib.pyplot as plt
def plot_strategy_payoffs():
"""주요 헤징 전략의 만기 시 손익 다이어그램"""
S = np.linspace(70, 130, 200)
K, S0, C, P = 100, 100, 5, 4
K_put, K_call = 95, 110
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 1. Covered Call
ax = axes[0, 0]
stock_pnl = S - S0
call_sold = np.where(S > K, -(S - K), 0) + C
ax.plot(S, stock_pnl, '--', alpha=0.5, label='Stock Only')
ax.plot(S, stock_pnl + call_sold, 'b-', linewidth=2, label='Covered Call')
ax.axhline(y=0, color='k', linewidth=0.5)
ax.set_title('Covered Call'); ax.legend(); ax.grid(True, alpha=0.3)
# 2. Protective Put
ax = axes[0, 1]
put_bought = np.where(S < K, K - S, 0) - P
ax.plot(S, stock_pnl, '--', alpha=0.5, label='Stock Only')
ax.plot(S, stock_pnl + put_bought, 'g-', linewidth=2, label='Protective Put')
ax.axhline(y=0, color='k', linewidth=0.5)
ax.set_title('Protective Put'); ax.legend(); ax.grid(True, alpha=0.3)
# 3. Collar
ax = axes[1, 0]
collar_put = np.where(S < K_put, K_put - S, 0) - 3
collar_call = np.where(S > K_call, -(S - K_call), 0) + 2
ax.plot(S, stock_pnl, '--', alpha=0.5, label='Stock Only')
ax.plot(S, stock_pnl + collar_put + collar_call, 'r-', linewidth=2, label='Collar')
ax.axhline(y=0, color='k', linewidth=0.5)
ax.set_title('Collar Strategy'); ax.legend(); ax.grid(True, alpha=0.3)
# 4. Straddle
ax = axes[1, 1]
straddle = np.maximum(S - K, 0) - C + np.maximum(K - S, 0) - P
ax.plot(S, straddle, 'm-', linewidth=2, label='Long Straddle')
ax.axhline(y=0, color='k', linewidth=0.5)
ax.set_title('Long Straddle'); ax.legend(); ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('hedging_payoff.png', dpi=150)
plt.show()
plot_strategy_payoffs()
Python 구현: Monte Carlo 시뮬레이션
BSM 모델의 해석적 공식은 유럽형 옵션에만 적용된다. 미국형 옵션, 경로 의존 옵션(아시안 옵션, 배리어 옵션 등)의 가격을 결정하려면 Monte Carlo 시뮬레이션이 필요하다.
코드 예제 4: Monte Carlo 옵션 가격 시뮬레이션
import numpy as np
import time
class MonteCarloOptionPricer:
"""Monte Carlo 시뮬레이션 기반 옵션 가격 결정"""
def __init__(self, S0, K, T, r, sigma, n_sims=100_000, n_steps=252):
self.S0, self.K, self.T, self.r, self.sigma = S0, K, T, r, sigma
self.n_sims, self.n_steps = n_sims, n_steps
def generate_paths(self) -> np.ndarray:
"""기하 브라운 운동(GBM)으로 주가 경로 생성 (안티테틱 변량법)"""
dt = self.T / self.n_steps
drift = (self.r - 0.5 * self.sigma**2) * dt
diffusion = self.sigma * np.sqrt(dt)
n_half = self.n_sims // 2
Z = np.random.standard_normal((n_half, self.n_steps))
Z = np.vstack([Z, -Z]) # Antithetic variates
log_returns = drift + diffusion * Z
log_paths = np.cumsum(log_returns, axis=1)
log_paths = np.insert(log_paths, 0, 0, axis=1)
return self.S0 * np.exp(log_paths)
def european_price(self, option_type="call") -> dict:
"""유럽형 옵션 가격"""
start = time.time()
paths = self.generate_paths()
S_T = paths[:, -1]
if option_type == "call":
payoffs = np.maximum(S_T - self.K, 0)
else:
payoffs = np.maximum(self.K - S_T, 0)
df = np.exp(-self.r * self.T)
price = df * np.mean(payoffs)
se = df * np.std(payoffs) / np.sqrt(len(payoffs))
return {
"price": round(price, 4), "std_error": round(se, 4),
"95_ci": [round(price - 1.96*se, 4), round(price + 1.96*se, 4)],
"elapsed": round(time.time() - start, 3),
}
def asian_price(self, option_type="call") -> dict:
"""아시안 옵션 가격 (경로 의존)"""
paths = self.generate_paths()
avg_price = np.mean(paths[:, 1:], axis=1)
if option_type == "call":
payoffs = np.maximum(avg_price - self.K, 0)
else:
payoffs = np.maximum(self.K - avg_price, 0)
df = np.exp(-self.r * self.T)
price = df * np.mean(payoffs)
se = df * np.std(payoffs) / np.sqrt(len(payoffs))
return {"price": round(price, 4), "std_error": round(se, 4)}
def barrier_price(self, barrier, barrier_type="down-and-out", option_type="call"):
"""배리어 옵션 가격"""
paths = self.generate_paths()
S_T = paths[:, -1]
if barrier_type == "down-and-out":
knocked = np.any(paths[:, 1:] <= barrier, axis=1)
else:
knocked = np.any(paths[:, 1:] >= barrier, axis=1)
payoffs = np.maximum(S_T - self.K, 0) if option_type == "call" else np.maximum(self.K - S_T, 0)
payoffs[knocked] = 0
df = np.exp(-self.r * self.T)
return {"price": round(df * np.mean(payoffs), 4), "knockout_ratio": round(np.mean(knocked), 4)}
# 실행 및 BSM과 비교
np.random.seed(42)
mc = MonteCarloOptionPricer(S0=100, K=100, T=0.5, r=0.05, sigma=0.2)
print("=== MC 유럽형 콜 ===", mc.european_price("call"))
print("=== 아시안 콜 ===", mc.asian_price("call"))
print("=== 배리어 콜 (Down-Out, B=85) ===", mc.barrier_price(85))
코드 예제 5: Delta 헤징 포트폴리오 시뮬레이션
import numpy as np
from scipy.stats import norm
class DeltaHedgingSimulator:
"""Delta 헤징 포트폴리오의 성과를 시뮬레이션"""
def __init__(self, S0, K, T, r, sigma, n_steps=63):
self.S0, self.K, self.T, self.r, self.sigma = S0, K, T, r, sigma
self.n_steps = n_steps
self.dt = T / n_steps
def _bsm_delta(self, S, tau):
if tau <= 0:
return 1.0 if S > self.K else 0.0
d1 = (np.log(S/self.K) + (self.r + 0.5*self.sigma**2)*tau) / (self.sigma*np.sqrt(tau))
return norm.cdf(d1)
def _bsm_price(self, S, tau):
if tau <= 0:
return max(S - self.K, 0)
d1 = (np.log(S/self.K) + (self.r + 0.5*self.sigma**2)*tau) / (self.sigma*np.sqrt(tau))
d2 = d1 - self.sigma*np.sqrt(tau)
return S*norm.cdf(d1) - self.K*np.exp(-self.r*tau)*norm.cdf(d2)
def simulate(self, actual_sigma=None, rebalance_freq=1, seed=42):
np.random.seed(seed)
if actual_sigma is None:
actual_sigma = self.sigma
# 주가 경로 생성
Z = np.random.standard_normal(self.n_steps)
S = np.zeros(self.n_steps + 1)
S[0] = self.S0
for i in range(self.n_steps):
S[i+1] = S[i] * np.exp(
(self.r - 0.5*actual_sigma**2)*self.dt + actual_sigma*np.sqrt(self.dt)*Z[i]
)
# 헤징 실행
premium = self._bsm_price(S[0], self.T)
cash, shares = premium, 0.0
for i in range(self.n_steps):
tau = self.T - i * self.dt
delta = self._bsm_delta(S[i], tau)
if i % rebalance_freq == 0:
trade = delta - shares
cash -= trade * S[i]
cash *= np.exp(self.r * self.dt * rebalance_freq)
shares = delta
else:
cash *= np.exp(self.r * self.dt)
# 만기 정산
payoff = max(S[-1] - self.K, 0)
portfolio = cash + shares * S[-1]
pnl = portfolio - payoff
return {
"premium": round(premium, 4), "final_S": round(S[-1], 2),
"payoff": round(payoff, 2), "hedge_pnl": round(pnl, 4),
"error_pct": round(abs(pnl)/premium*100, 2),
}
sim = DeltaHedgingSimulator(S0=100, K=100, T=0.25, r=0.05, sigma=0.2)
# 시나리오 비교
r1 = sim.simulate(actual_sigma=0.2, rebalance_freq=1)
r2 = sim.simulate(actual_sigma=0.3, rebalance_freq=1)
r3 = sim.simulate(actual_sigma=0.2, rebalance_freq=5)
print("=== 시나리오 1: 실현변동성=모델변동성 ===")
print(f" 프리미엄: {r1['premium']}, 헤징P&L: {r1['hedge_pnl']}, 오차: {r1['error_pct']}%")
print("=== 시나리오 2: 실현변동성(30%) > 모델변동성(20%) ===")
print(f" 프리미엄: {r2['premium']}, 헤징P&L: {r2['hedge_pnl']}, 오차: {r2['error_pct']}%")
print("=== 시나리오 3: 리밸런싱 주기 5일 ===")
print(f" 프리미엄: {r3['premium']}, 헤징P&L: {r3['hedge_pnl']}, 오차: {r3['error_pct']}%")
실패 사례: 역사가 가르치는 교훈
파생상품은 올바르게 사용하면 강력한 위험 관리 도구이지만, 잘못 사용하면 치명적인 결과를 초래한다.
LTCM (Long-Term Capital Management, 1998)
LTCM은 노벨 경제학상 수상자 2명(Myron Scholes, Robert Merton)을 포함한 금융 엘리트들이 운영한 헤지펀드다. 1994년 출범 후 첫 3년간 연 40%가 넘는 수익률을 기록했다. 그들의 전략은 채권 간 미세한 가격 차이를 활용한 차익거래였는데, 작은 수익을 극대화하기 위해 25:1이 넘는 레버리지를 사용했다. 파생상품의 명목 가치만 약 1.25조 달러에 달했다.
1998년 러시아 채무 불이행이 발생하자 글로벌 시장에 패닉이 번졌고, "안전자산으로의 도피(flight to quality)" 현상이 극단적으로 나타났다. LTCM의 모델은 이런 극단적 상관관계 변화를 예측하지 못했다. 수주 만에 자본의 90%인 약 46억 달러를 잃었고, 미국 연방준비은행의 중재로 14개 금융기관이 36.5억 달러를 투입하여 겨우 체계적 붕괴를 막았다.
교훈: 과거 데이터에 기반한 VaR(Value at Risk) 모델은 극단적 사건(tail risk)을 과소평가한다. 레버리지는 수익과 손실 모두를 증폭한다. 유동성이 필요할 때 가장 먼저 사라지는 것이 유동성이다.
니켈 숏스퀴즈 (London Metal Exchange, 2022년 3월)
중국의 니켈 생산업체 칭산그룹(Tsingshan Holding Group)은 보유 니켈의 가격 하락 위험을 헤징하기 위해 LME에서 니켈 선물 숏 포지션을 구축했다. 추정 규모는 10만-30만 메트릭톤으로, 세계 연간 니켈 생산량의 약 10%에 해당하는 규모였다.
2022년 2월 러시아-우크라이나 전쟁이 시작되면서 러시아산 니켈 공급 차질 우려로 니켈 가격이 급등했다. 가격이 오를수록 칭산그룹의 숏 포지션에 대한 마진콜이 쏟아졌다. 마진을 충족하기 위해 포지션을 청산(매수)하려 하자, 이 매수 물량이 추가 가격 상승을 유발하는 숏스퀴즈가 발생했다. 니켈 가격은 이틀 만에 톤당 100,000 이상으로 4배 폭등했다.
LME는 사상 초유의 조치로 거래를 중단하고, 이미 체결된 $39.5억 달러 규모의 거래를 취소했다. 총 마진 위반 규모는 해당 분기에 233억 달러에 달했는데, 이는 이전 분기 대비 수백 배의 증가였다.
교훈: 포지션 규모가 시장 유동성 대비 과도하면 헤징이 오히려 위험을 증폭한다. 숏 포지션의 손실은 이론적으로 무한하다. 거래소조차 예측 불가능한 상황에서는 정상적인 시장 기능이 마비될 수 있다.
베어링 은행 (Barings Bank, 1995)
233년 역사의 영국 최고(最古) 투자은행인 베어링 은행은 싱가포르 지점의 한 트레이더(닉 리슨)의 무단 파생상품 거래로 8.6억 파운드의 손실을 입고 파산했다. 리슨은 니케이225 선물과 옵션에 대규모 포지션을 구축했고, 1995년 고베 대지진으로 일본 주식시장이 급락하면서 손실이 걷잡을 수 없이 커졌다.
교훈: 한 개인에게 거래와 정산 두 역할을 동시에 부여해서는 안 된다(업무 분리 원칙). 포지션 한도와 손절 규칙은 예외 없이 적용해야 한다.
개인 투자자 주의사항
파생상품 투자는 높은 전문성을 요구한다. 개인 투자자가 반드시 유념해야 할 사항을 정리한다.
레버리지의 양면성을 이해하라: 선물의 마진 거래는 10-20배의 레버리지를 의미한다. 기초자산 가격이 5% 변하면 투자금의 50-100%가 변한다. 절대로 빚을 내서 파생상품 거래를 하지 말아야 한다.
옵션 매도는 극도로 위험하다: 옵션 매수자의 최대 손실은 프리미엄으로 제한되지만, 옵션 매도자의 최대 손실은 이론적으로 무한하다(특히 네이키드 콜 매도). 충분한 경험과 자본이 있는 경우에만 시도해야 한다.
유동성을 확인하라: 거래량이 적은 옵션은 매도-매수 스프레드가 넓어 불리한 가격에 거래될 수 있다. 항상 호가창의 깊이와 일일 거래량을 확인해야 한다.
만기일 관리를 철저히 하라: 옵션의 시간가치 감소(Theta)는 만기에 가까울수록 가속된다. 특히 만기 주(week)에는 예측 불가능한 가격 변동이 발생할 수 있다.
손절 규칙을 반드시 설정하라: 포지션을 열기 전에 최대 손실 허용 금액을 정하고, 해당 수준에 도달하면 예외 없이 포지션을 청산해야 한다. "곧 반등하겠지"라는 기대는 LTCM, 베어링, 칭산 모두에서 통하지 않았다.
세금과 수수료를 고려하라: 파생상품 거래는 양도소득세, 거래 수수료, 슬리피지 등 숨겨진 비용이 크다. 빈번한 거래는 이러한 비용을 누적시켜 수익을 크게 잠식한다.
모의 거래로 충분히 연습하라: 실제 자금을 투입하기 전에 모의 거래(paper trading) 시스템에서 최소 6개월 이상 전략을 검증해야 한다. 시뮬레이션에서 수익이 나도 실전에서는 슬리피지, 심리적 압박, 유동성 부족 등으로 결과가 다를 수 있다.
참고자료
- Options, Futures, and Other Derivatives - John C. Hull - 파생상품 분야의 바이블로 불리는 교재
- The Black-Scholes Model Explained (Python in Plain English) - BSM 모델과 그릭스의 Python 구현 실습 가이드
- pyBlackScholesAnalytics (GitHub) - Black-Scholes 기반 옵션 분석 Python 라이브러리
- LTCM 사례 분석 - UC Berkeley - LTCM 붕괴에서 얻는 교훈 학술 분석
- LME 니켈 시장 사건 독립 리뷰 보고서 - 2022년 LME 니켈 숏스퀴즈 공식 조사 보고서
- Black Scholes Calculator for Python (GitHub) - 3차 그릭스까지 지원하는 Python BSM 계산기
- Codearmo - Black Scholes Model Python Tutorial - Python BSM 모델 구현 튜토리얼