提示词

1
需要演示使用模拟方法对欧洲期权进行定价,请给出示例代码。

答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import norm
from tqdm import tqdm

class OptionPricing:
"""
欧洲期权定价工具类
支持蒙特卡洛模拟、Black-Scholes解析解和二叉树模型
"""

def __init__(self, S0, K, T, r, sigma, option_type='call'):
"""
初始化期权参数
S0: 标的资产现价
K: 行权价
T: 到期时间(年)
r: 无风险利率
sigma: 年化波动率
option_type: 'call'或'put'
"""
self.S0 = S0
self.K = K
self.T = T
self.r = r
self.sigma = sigma
self.option_type = option_type.lower()

# 验证期权类型
if self.option_type not in ['call', 'put']:
raise ValueError("期权类型必须是'call'或'put'")

def black_scholes(self):
"""计算Black-Scholes期权价格"""
d1 = (np.log(self.S0 / self.K) + (self.r + 0.5 * self.sigma**2) * self.T) / (self.sigma * np.sqrt(self.T))
d2 = d1 - self.sigma * np.sqrt(self.T)

if self.option_type == 'call':
price = self.S0 * norm.cdf(d1) - self.K * np.exp(-self.r * self.T) * norm.cdf(d2)
else: # put
price = self.K * np.exp(-self.r * self.T) * norm.cdf(-d2) - self.S0 * norm.cdf(-d1)

return price

def monte_carlo(self, n_simulations=10000, n_steps=252):
"""
蒙特卡洛模拟期权定价
n_simulations: 模拟路径数量
n_steps: 路径步数
"""
dt = self.T / n_steps
price_paths = np.zeros((n_simulations, n_steps + 1))
price_paths[:, 0] = self.S0

# 生成布朗运动
rv = np.random.normal(size=(n_simulations, n_steps))

# 模拟价格路径
for t in range(1, n_steps + 1):
z = rv[:, t-1]
price_paths[:, t] = price_paths[:, t-1] * np.exp(
(self.r - 0.5 * self.sigma**2) * dt +
self.sigma * np.sqrt(dt) * z
)

# 计算到期日收益
if self.option_type == 'call':
payoffs = np.maximum(price_paths[:, -1] - self.K, 0)
else: # put
payoffs = np.maximum(self.K - price_paths[:, -1], 0)

# 贴现收益
option_price = np.exp(-self.r * self.T) * np.mean(payoffs)
stderr = np.exp(-self.r * self.T) * np.std(payoffs) / np.sqrt(n_simulations)

# 保存模拟路径供可视化使用
self.price_paths = price_paths
self.payoffs = payoffs
self.mc_price = option_price

return option_price, stderr

def binomial_tree(self, n_steps=100):
"""
二叉树模型期权定价
n_steps: 二叉树步数
"""
dt = self.T / n_steps
u = np.exp(self.sigma * np.sqrt(dt)) # 上涨因子
d = 1 / u # 下跌因子
p = (np.exp(self.r * dt) - d) / (u - d) # 风险中性概率

# 初始化到期收益
stock_prices = np.zeros(n_steps + 1)
option_values = np.zeros(n_steps + 1)

# 计算到期时的股票价格和期权价值
for j in range(n_steps + 1):
stock_prices[j] = self.S0 * (u ** j) * (d ** (n_steps - j))

if self.option_type == 'call':
option_values[j] = max(stock_prices[j] - self.K, 0)
else: # put
option_values[j] = max(self.K - stock_prices[j], 0)

# 向后推导期权当前价值
for i in range(n_steps - 1, -1, -1):
for j in range(i + 1):
option_values[j] = np.exp(-self.r * dt) * (p * option_values[j + 1] + (1 - p) * option_values[j])

return option_values[0]

def visualize(self):
"""可视化蒙特卡洛模拟结果"""
if not hasattr(self, 'price_paths') or not hasattr(self, 'payoffs'):
raise RuntimeError("请先运行蒙特卡洛模拟")

# 创建画布
plt.figure(figsize=(15, 10))

# 1. 模拟价格路径
plt.subplot(2, 2, 1)
for i in range(min(100, len(self.price_paths))):
plt.plot(self.price_paths[i], lw=1, alpha=0.3, color='blue')
plt.axhline(self.K, color='red', linestyle='--', label='行权价 (K)')
plt.title(f'蒙特卡洛模拟路径 ({len(self.price_paths)}条路径)')
plt.xlabel('时间步')
plt.ylabel('价格')
plt.legend()
plt.grid(alpha=0.2)

# 2. 到期价格分布
plt.subplot(2, 2, 2)
plt.hist(self.price_paths[:, -1], bins=50, color='skyblue', edgecolor='black', alpha=0.8)
plt.axvline(self.K, color='red', linestyle='--', label='行权价 (K)')
plt.title('到期价格分布')
plt.xlabel('到期价格')
plt.ylabel('频数')
plt.legend()
plt.grid(alpha=0.2)

# 3. 收益分布
plt.subplot(2, 2, 3)
plt.hist(self.payoffs, bins=50, color='lightgreen', edgecolor='black', alpha=0.8)
plt.title('期权收益分布')
plt.xlabel('收益')
plt.ylabel('频数')
plt.grid(alpha=0.2)

# 4. 收敛分析
plt.subplot(2, 2, 4)
cum_avg_payoffs = np.cumsum(self.payoffs) / np.arange(1, len(self.payoffs) + 1)
discount_factor = np.exp(-self.r * self.T)
cumulative_price = discount_factor * cum_avg_payoffs

bs_price = self.black_scholes()

plt.plot(cumulative_price, label='蒙特卡洛价格')
plt.axhline(bs_price, color='red', linestyle='--', label='Black-Scholes价格')
plt.axhline(self.mc_price, color='purple', linestyle='-', label='最终蒙特卡洛价格')
plt.title(f'蒙特卡洛收敛性 (N={len(self.payoffs)})')
plt.xlabel('模拟次数')
plt.ylabel('期权价格')
plt.legend()
plt.grid(alpha=0.2)

plt.tight_layout()
plt.savefig('option_pricing_monte_carlo.png', dpi=300)
plt.show()

# ==========================================
# 测试与比较
# ==========================================

# 设置参数
params = {
'S0': 100, # 当前股票价格
'K': 105, # 行权价
'T': 1, # 到期时间(年)
'r': 0.05, # 无风险利率
'sigma': 0.2, # 年化波动率
'option_type': 'call' # 期权类型: call或put
}

# 创建定价器
option_pricer = OptionPricing(**params)

# 计算Black-Scholes价格
bs_price = option_pricer.black_scholes()
print(f"Black-Scholes价格: {bs_price:.4f}")

# 蒙特卡洛模拟
mc_price, stderr = option_pricer.monte_carlo(n_simulations=10000)
print(f"蒙特卡洛价格: {mc_price:.4f} (标准差: {stderr:.4f})")
print(f"与BS模型的差异: {abs(bs_price - mc_price):.4f} ({abs(bs_price - mc_price)/bs_price*100:.2f}%)")

# 二叉树模型
binomial_price = option_pricer.binomial_tree(n_steps=500)
print(f"二叉树价格(500步): {binomial_price:.4f}")

# 可视化蒙特卡洛结果
option_pricer.visualize()

# ==========================================
# 高级分析: 不同方法收敛性对比
# ==========================================

# 1. 不同模拟次数下的蒙特卡洛价格
simulations = np.arange(100, 100001, 1000)
mc_prices = []
bs_price = option_pricer.black_scholes()

print("\n蒙特卡洛收敛分析...")
for n in tqdm(simulations):
price, _ = option_pricer.monte_carlo(n_simulations=n)
mc_prices.append(price)

# 2. 不同步数下的二叉树价格
steps = np.arange(10, 1001, 10)
binomial_prices = []

print("\n二叉树收敛分析...")
for n in tqdm(steps):
price = option_pricer.binomial_tree(n_steps=n)
binomial_prices.append(price)

# 3. 可视化收敛性比较
plt.figure(figsize=(14, 10))

# 蒙特卡洛收敛
plt.subplot(2, 1, 1)
plt.plot(simulations, mc_prices, 'b-', label='蒙特卡洛价格')
plt.axhline(bs_price, color='r', linestyle='--', label='Black-Scholes')
plt.title('蒙特卡洛模拟收敛性')
plt.xlabel('模拟路径数量')
plt.ylabel('期权价格')
plt.legend()
plt.grid(alpha=0.2)

# 二叉树收敛
plt.subplot(2, 1, 2)
plt.plot(steps, binomial_prices, 'g-', label='二叉树价格')
plt.axhline(bs_price, color='r', linestyle='--', label='Black-Scholes')
plt.title('二叉树模型收敛性')
plt.xlabel('二叉树步数')
plt.ylabel('期权价格')
plt.legend()
plt.grid(alpha=0.2)

plt.tight_layout()
plt.savefig('option_pricing_convergence.png', dpi=300)
plt.show()

# ==========================================
# 希腊字母计算
# ==========================================

def calculate_greeks(option, epsilon=0.01):
"""计算期权的Delta和Gamma"""
# 原始价格
price = option.black_scholes()

# Delta计算
opt_up = OptionPricing(
S0=option.S0 * (1 + epsilon),
K=option.K,
T=option.T,
r=option.r,
sigma=option.sigma,
option_type=option.option_type
)
price_up = opt_up.black_scholes()

opt_down = OptionPricing(
S0=option.S0 * (1 - epsilon),
K=option.K,
T=option.T,
r=option.r,
sigma=option.sigma,
option_type=option.option_type
)
price_down = opt_down.black_scholes()

delta = (price_up - price_down) / (2 * epsilon * option.S0)

# Gamma计算
gamma = (price_up - 2 * price + price_down) / (epsilon**2 * option.S0**2)

return {'Delta': delta, 'Gamma': gamma}

# 计算示例希腊字母
greeks = calculate_greeks(option_pricer)
print(f"\n期权希腊字母:")
print(f"Delta: {greeks['Delta']:.4f}")
print(f"Gamma: {greeks['Gamma']:.4f}")

# ==========================================
# 参数敏感性分析
# ==========================================

def sensitivity_analysis(option, param, values):
"""计算不同参数下的期权价格"""
prices = []
orig_value = getattr(option, param)

for val in values:
setattr(option, param, val)
prices.append(option.black_scholes())

# 恢复原始值
setattr(option, param, orig_value)
return prices

# 波动率敏感性分析
vols = np.linspace(0.1, 0.5, 50)
vol_prices = sensitivity_analysis(option_pricer, 'sigma', vols)

# 到期时间敏感性分析
times = np.linspace(0.1, 2, 50)
time_prices = sensitivity_analysis(option_pricer, 'T', times)

# 可视化
plt.figure(figsize=(14, 6))

plt.subplot(1, 2, 1)
plt.plot(vols, vol_prices, 'b-')
plt.title('波动率敏感性')
plt.xlabel('波动率(σ)')
plt.ylabel('期权价格')
plt.grid(alpha=0.3)

plt.subplot(1, 2, 2)
plt.plot(times, time_prices, 'g-')
plt.title('到期时间敏感性')
plt.xlabel('到期时间(年)')
plt.ylabel('期权价格')
plt.grid(alpha=0.3)

plt.tight_layout()
plt.savefig('option_sensitivity.png', dpi=300)
plt.show()

结果:

提示词没有写模拟方法,好家伙,3种方法。不错。