In [1]:
Copied!
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
np.random.seed(42)
print('Libraries loaded')
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
np.random.seed(42)
print('Libraries loaded')
Libraries loaded
In [2]:
Copied!
import matplotlib.pyplot as plt
# 1. 设置系统自带的中文字体(这里使用黑体 SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 如果你想用微软雅黑,可以改成 ['Microsoft YaHei']
# 2. 解决更换字体后,负号(-)显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
import matplotlib.pyplot as plt
# 1. 设置系统自带的中文字体(这里使用黑体 SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 如果你想用微软雅黑,可以改成 ['Microsoft YaHei']
# 2. 解决更换字体后,负号(-)显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
1. 几何布朗运动(GBM):股价路径模拟¶
$$dS_t = \mu S_t dt + \sigma S_t dW_t$$
离散化(Euler-Maruyama): $$S_{t+\Delta t} = S_t \cdot \exp\left[(\mu - \frac{\sigma^2}{2})\Delta t + \sigma\sqrt{\Delta t}\,Z_t\right]$$
其中 $Z_t \sim N(0,1)$ 是标准正态随机变量。
In [3]:
Copied!
S0 = 100 # 初始股价
mu_gbm = 0.08 # 年化漂移率
sigma_gbm = 0.20 # 年化波动率
T = 1.0 # 期限 1 年
dt = 1/252 # 日频步长
N = int(T/dt) # 总步数
n_paths = 2000 # 模拟路径数
# 批量模拟 GBM 路径
Z = np.random.standard_normal((N, n_paths))
log_returns = (mu_gbm - 0.5*sigma_gbm**2)*dt + sigma_gbm*np.sqrt(dt)*Z
paths = S0 * np.exp(log_returns.cumsum(axis=0)) # shape: (N, n_paths)
# 可视化部分路径
plt.figure(figsize=(12, 5))
plt.plot(paths[:, :50], alpha=0.3, lw=0.8, color='steelblue')
plt.plot(paths[:, 0], color='red', lw=1.5, label='单条路径样例')
plt.axhline(S0, color='black', linestyle='--', alpha=0.5, label=f'S0={S0}')
plt.title(f'GBM 股价路径模拟 (μ={mu_gbm:.0%}, σ={sigma_gbm:.0%}, {n_paths}条)')
plt.xlabel('交易日'); plt.ylabel('股价'); plt.legend(); plt.grid(alpha=0.3)
plt.show()
print(f'期末股价分布: 中位数={np.median(paths[-1]):.1f}, 均值={paths[-1].mean():.1f}')
S0 = 100 # 初始股价
mu_gbm = 0.08 # 年化漂移率
sigma_gbm = 0.20 # 年化波动率
T = 1.0 # 期限 1 年
dt = 1/252 # 日频步长
N = int(T/dt) # 总步数
n_paths = 2000 # 模拟路径数
# 批量模拟 GBM 路径
Z = np.random.standard_normal((N, n_paths))
log_returns = (mu_gbm - 0.5*sigma_gbm**2)*dt + sigma_gbm*np.sqrt(dt)*Z
paths = S0 * np.exp(log_returns.cumsum(axis=0)) # shape: (N, n_paths)
# 可视化部分路径
plt.figure(figsize=(12, 5))
plt.plot(paths[:, :50], alpha=0.3, lw=0.8, color='steelblue')
plt.plot(paths[:, 0], color='red', lw=1.5, label='单条路径样例')
plt.axhline(S0, color='black', linestyle='--', alpha=0.5, label=f'S0={S0}')
plt.title(f'GBM 股价路径模拟 (μ={mu_gbm:.0%}, σ={sigma_gbm:.0%}, {n_paths}条)')
plt.xlabel('交易日'); plt.ylabel('股价'); plt.legend(); plt.grid(alpha=0.3)
plt.show()
print(f'期末股价分布: 中位数={np.median(paths[-1]):.1f}, 均值={paths[-1].mean():.1f}')
期末股价分布: 中位数=105.9, 均值=108.0
2. 欧式期权的蒙特卡洛定价¶
欧式看涨期权到期日 $T$ 时的收益为: $$\text{Payoff} = \max(S_T - K, 0)$$
期权价格 = 贴现的期望收益: $$C = e^{-rT} \cdot E[\max(S_T - K, 0)]$$
In [4]:
Copied!
K = 105 # 行权价
r = 0.04 # 无风险利率
# MC 估计:期末股价的期权收益
S_T = paths[-1] # 期末股价(N条路径)
payoffs = np.maximum(S_T - K, 0)
C_mc = np.exp(-r*T) * payoffs.mean()
C_mc_std = np.exp(-r*T) * payoffs.std() / np.sqrt(n_paths) # 标准误
# Black-Scholes 解析公式(验证)
def bs_call(S, K, r, sigma, T):
d1 = (np.log(S/K) + (r + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
d2 = d1 - sigma*np.sqrt(T)
return S * stats.norm.cdf(d1) - K*np.exp(-r*T) * stats.norm.cdf(d2)
C_bs = bs_call(S0, K, r, sigma_gbm, T)
print(f'MC 期权价格: {C_mc:.4f} ± {C_mc_std*1.96:.4f} (95% CI)')
print(f'BS 解析公式: {C_bs:.4f}')
print(f'MC 误差: {abs(C_mc - C_bs):.4f} ({abs(C_mc-C_bs)/C_bs:.2%})')
K = 105 # 行权价
r = 0.04 # 无风险利率
# MC 估计:期末股价的期权收益
S_T = paths[-1] # 期末股价(N条路径)
payoffs = np.maximum(S_T - K, 0)
C_mc = np.exp(-r*T) * payoffs.mean()
C_mc_std = np.exp(-r*T) * payoffs.std() / np.sqrt(n_paths) # 标准误
# Black-Scholes 解析公式(验证)
def bs_call(S, K, r, sigma, T):
d1 = (np.log(S/K) + (r + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
d2 = d1 - sigma*np.sqrt(T)
return S * stats.norm.cdf(d1) - K*np.exp(-r*T) * stats.norm.cdf(d2)
C_bs = bs_call(S0, K, r, sigma_gbm, T)
print(f'MC 期权价格: {C_mc:.4f} ± {C_mc_std*1.96:.4f} (95% CI)')
print(f'BS 解析公式: {C_bs:.4f}')
print(f'MC 误差: {abs(C_mc - C_bs):.4f} ({abs(C_mc-C_bs)/C_bs:.2%})')
MC 期权价格: 9.8284 ± 0.6368 (95% CI) BS 解析公式: 7.5670 MC 误差: 2.2614 (29.89%)
In [5]:
Copied!
# 对偶变量法
n_antithetic = 1000
Z_half = np.random.standard_normal((N, n_antithetic))
# 正向路径
lr_pos = (mu_gbm - 0.5*sigma_gbm**2)*dt + sigma_gbm*np.sqrt(dt)*Z_half
ST_pos = S0 * np.exp(lr_pos.sum(axis=0))
# 对偶路径(用 -Z)
lr_neg = (mu_gbm - 0.5*sigma_gbm**2)*dt + sigma_gbm*np.sqrt(dt)*(-Z_half)
ST_neg = S0 * np.exp(lr_neg.sum(axis=0))
# 对偶估计量
payoff_antithetic = (np.maximum(ST_pos-K, 0) + np.maximum(ST_neg-K, 0)) / 2
C_antithetic = np.exp(-r*T) * payoff_antithetic.mean()
std_antithetic = np.exp(-r*T) * payoff_antithetic.std() / np.sqrt(n_antithetic)
# 普通 MC(同样路径数)
payoff_plain = np.maximum(S0*np.exp(lr_pos.sum(axis=0))-K, 0)
C_plain = np.exp(-r*T) * payoff_plain.mean()
std_plain = np.exp(-r*T) * payoff_plain.std() / np.sqrt(n_antithetic)
print(f'路径数: {n_antithetic}')
print(f'普通 MC: {C_plain:.4f} 标准误 = {std_plain:.4f}')
print(f'对偶变量: {C_antithetic:.4f} 标准误 = {std_antithetic:.4f}')
print(f'方差减少比: {(std_plain/std_antithetic)**2:.1f}x')
# 对偶变量法
n_antithetic = 1000
Z_half = np.random.standard_normal((N, n_antithetic))
# 正向路径
lr_pos = (mu_gbm - 0.5*sigma_gbm**2)*dt + sigma_gbm*np.sqrt(dt)*Z_half
ST_pos = S0 * np.exp(lr_pos.sum(axis=0))
# 对偶路径(用 -Z)
lr_neg = (mu_gbm - 0.5*sigma_gbm**2)*dt + sigma_gbm*np.sqrt(dt)*(-Z_half)
ST_neg = S0 * np.exp(lr_neg.sum(axis=0))
# 对偶估计量
payoff_antithetic = (np.maximum(ST_pos-K, 0) + np.maximum(ST_neg-K, 0)) / 2
C_antithetic = np.exp(-r*T) * payoff_antithetic.mean()
std_antithetic = np.exp(-r*T) * payoff_antithetic.std() / np.sqrt(n_antithetic)
# 普通 MC(同样路径数)
payoff_plain = np.maximum(S0*np.exp(lr_pos.sum(axis=0))-K, 0)
C_plain = np.exp(-r*T) * payoff_plain.mean()
std_plain = np.exp(-r*T) * payoff_plain.std() / np.sqrt(n_antithetic)
print(f'路径数: {n_antithetic}')
print(f'普通 MC: {C_plain:.4f} 标准误 = {std_plain:.4f}')
print(f'对偶变量: {C_antithetic:.4f} 标准误 = {std_antithetic:.4f}')
print(f'方差减少比: {(std_plain/std_antithetic)**2:.1f}x')
路径数: 1000 普通 MC: 9.8887 标准误 = 0.4512 对偶变量: 9.8767 标准误 = 0.2364 方差减少比: 3.6x
🎯 练习¶
- 将期权类型改为亚式期权(payoff 基于路径平均价格而非期末价格),重新计算 MC 定价。
- 绘制期权价格 vs 行权价 K 的曲线(K 从 80 到 130),与 Black-Scholes 公式对比。
- 用 MC 方法计算含 5 只股票(有相关性)的组合在 95% 置信水平下的一年期 VaR。
下一节 → 02_linear_programming.ipynb
In [ ]:
Copied!