提示词

1
现在有一个名叫returns的Pandas Series,存储的是某股票的每日简单收益,使用Python定义一个函数,用于模拟该数据的几何布朗运动,请给出示例代码。

答案

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
import numpy as np
import pandas as pd

def simulate_gbm(returns, S0, T=1.0, dt=1/252, num_sims=1000):
"""
使用几何布朗运动模拟股票价格路径

参数:
returns : pandas.Series - 股票每日简单收益率
S0 : float - 初始股票价格
T : float - 模拟总时间(年)
dt : float - 时间步长(年,默认1/252表示交易日)
num_sims: int - 模拟路径数量

返回:
numpy.ndarray - 形状为(num_steps, num_sims)的模拟价格路径
"""
# 1. 计算对数收益率
log_returns = np.log(1 + returns)

# 2. 估计模型参数(年化)
mu = log_returns.mean() * 252 # 年化漂移率
sigma = log_returns.std() * np.sqrt(252) # 年化波动率
adj_drift = mu - 0.5 * sigma**2 # 调整后的漂移项

# 3. 设置时间参数
num_steps = int(T / dt) # 总步数

# 4. 生成随机路径
dW = np.random.normal(0, np.sqrt(dt), size=(num_steps, num_sims)) # 维纳过程增量
price_paths = np.zeros((num_steps, num_sims))
price_paths[0] = S0 # 设置初始价格

# 5. 迭代计算价格路径
for t in range(1, num_steps):
price_paths[t] = price_paths[t-1] * np.exp(
adj_drift * dt + sigma * dW[t]
)

return price_paths

# 示例用法
if __name__ == "__main__":
# 生成示例收益率数据(假设有252个交易日)
# np.random.seed(42)
# returns = pd.Series(np.random.normal(0.001, 0.02, 252))

# 模拟参数
S0 = returns.loc[returns.index[-1],] # 初始价格
T = 1.0 # 1年
dt = 1/252 # 每日时间步长
num_sims = 5 # 模拟5条路径

# 运行模拟
paths = simulate_gbm(returns, S0, T, dt, num_sims)

# 可视化结果
import matplotlib.pyplot as plt

plt.figure(figsize=(12, 6))
for i in range(num_sims):
plt.plot(paths[:, i])
plt.title('几何布朗运动 - 股票价格模拟')
plt.xlabel('交易日')
plt.ylabel('价格')
plt.grid(alpha=0.3)
plt.show()

结果:

使用定义好的函数模拟股价变动

ai实际一步到位可视化了,不过我们还是简化代码加描述。再来一次

提示词

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
# Python代码
# 首先我们需要将数据拆分一下
train = returns['2022']
test = returns['2023']

# 然后我们使用训练集计算相关参数
T = len(test)
N = len(test)
S_0 = df.loc[train.index[-1], "收盘"]# 初始的股票价格
N_SIM = 50 # 模拟的次数
mu = train.mean()# 公式中的mu
sigma = train.std()# 公式中的sigma
def geometric_brownian_motion(s_0, mu, sigma, n_sims, T, N, random_seed=42):
"""
定义一个函数,用于模拟股票价格的几何布朗运动过程。
参数:
s_0: 股票的初始价格
mu: 预期年化收益率
sigma: 年化波动率
n_sims: 模拟次数
T: 模拟的总时间(以年为单位)
N: 时间步长数,即总时间被分割成的块数
random_seed: 随机种子,用于产生随机数(默认为42)
返回值:
模拟的股票价格序列,形状为(n_sims, N+1),其中N+1是因为在模拟开始时插入了一个初始价格s_0。
"""
# 设置随机种子,确保每次运行产生的随机数相同
np.random.seed(random_seed)
# 计算时间步长,即每个时间块的大小
dt = T/N
# 生成N个标准正态分布的随机扰动项,每个扰动项的大小由时间步长决定
dW = np.random.normal(scale=np.sqrt(dt),
size=(n_sims, N))
# 对每行(每个模拟)的扰动项进行累加,得到W序列
W = np.cumsum(dW, axis=1)
# 创建一个从dt到T的时间步长序列,用于描述整个模拟时间区间
time_step = np.linspace(dt, T, N)
# 将时间步长序列扩展到(n_sims, N)的形状,以便于每个模拟都有对应的时间步长
time_steps = np.broadcast_to(time_step, (n_sims, N))
# 定义一个表达式,描述股票价格的随机变化过程
S_t = (
s_0 * np.exp((mu - 0.5 * sigma**2) * time_steps + sigma * W)
)
# 在S_t的每行的开头插入初始价格s_0,形成完整的股票价格序列
S_t = np.insert(S_t, 0, s_0, axis=1)
# 返回模拟的股票价格序列
return S_t

接下来使用上面定义好的几何布朗运动函数模拟returns数据,并将结果可视化,请给出示例代码。

答案

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
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.ticker import FuncFormatter

# 运行模拟
simulated_paths = geometric_brownian_motion(S_0, mu, sigma, N_SIM, T, N)

# 创建与测试集对应的时间索引
date_index = pd.date_range(start=train.index[-1], periods=N+1, freq='D')[:N+1]

# ======================
# 可视化部分
# ======================

# 1. 模拟路径与真实价格对比
plt.figure(figsize=(14, 7))
plt.title(f"股票价格蒙特卡洛模拟 (N={N_SIM}次)\nμ={mu:.4f}, σ={sigma:.4f}", fontsize=14)

# 绘制所有模拟路径(半透明处理)
for i in range(N_SIM):
plt.plot(date_index, simulated_paths[i], lw=1, alpha=0.15, color='steelblue')

# 计算并绘制关键统计量
mean_path = np.mean(simulated_paths, axis=0)
upper_95 = np.percentile(simulated_paths, 95, axis=0)
lower_5 = np.percentile(simulated_paths, 5, axis=0)

plt.plot(date_index, mean_path, 'b-', lw=2, label='模拟均值路径')
plt.plot(date_index, upper_95, 'r--', lw=1.5, label='95%分位数')
plt.plot(date_index, lower_5, 'g--', lw=1.5, label='5%分位数')

# 添加实际价格曲线(测试期)
test_period = df.loc[test.index, '收盘']
plt.plot(test_period.index, test_period.values, 'k-', lw=2.5, label='实际价格')

# 美化坐标轴
plt.gca().xaxis.set_major_locator(mdates.MonthLocator())
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
plt.gcf().autofmt_xdate() # 自动旋转日期标签

# 添加图例和网格
plt.legend(loc='upper left')
plt.grid(True, alpha=0.3)
plt.xlabel('日期')
plt.ylabel('价格')
plt.tight_layout()
plt.savefig('GBM_模拟路径对比.png', dpi=300)
plt.show()

# 2. 模拟路径与真实价格差异分析(最后一天)
final_prices = simulated_paths[:, -1]
actual_final_price = test_period.iloc[-1]

plt.figure(figsize=(10, 6))
plt.hist(final_prices, bins=50, alpha=0.7, color='skyblue', edgecolor='navy')
plt.axvline(x=actual_final_price, color='red', linestyle='--', lw=2,
label=f'实际价格: {actual_final_price:.2f}')
plt.axvline(x=np.mean(final_prices), color='green', linestyle='-', lw=2,
label=f'模拟均值: {np.mean(final_prices):.2f}')

# 添加统计指标
plt.text(0.75, 0.9, f'模拟标准差: {np.std(final_prices):.2f}',
transform=plt.gca().transAxes, fontsize=12)
plt.text(0.75, 0.85, f'偏度: {pd.Series(final_prices).skew():.2f}',
transform=plt.gca().transAxes, fontsize=12)

plt.title('模拟期末价格分布 vs 实际价格', fontsize=14)
plt.xlabel('期末价格')
plt.ylabel('频率')
plt.legend()
plt.grid(alpha=0.2)
plt.savefig('GBM_期末价格分布.png', dpi=300)
plt.show()

# 3. 动态预测区间(随时间变化的置信区间)
plt.figure(figsize=(14, 7))
plt.fill_between(date_index,
np.percentile(simulated_paths, 5, axis=0),
np.percentile(simulated_paths, 95, axis=0),
color='lightblue', alpha=0.5, label='90%置信区间')

plt.plot(date_index, mean_path, 'b-', lw=2, label='模拟均值')
plt.plot(test_period.index, test_period.values, 'k-', lw=2.5, label='实际价格')

# 标记关键事件点
max_dev_idx = np.argmax(np.abs(test_period.values - mean_path[1:]))
event_date = test_period.index[max_dev_idx]
plt.axvline(x=event_date, color='purple', linestyle=':', alpha=0.8)
plt.text(event_date, plt.ylim()[0]*0.95, f'最大偏离点\n{event_date.strftime("%Y-%m-%d")}',
ha='center', fontsize=9)

plt.title('GBM模拟的90%置信区间与实际价格对比', fontsize=14)
plt.xlabel('日期')
plt.ylabel('价格')
plt.legend(loc='upper left')
plt.gca().xaxis.set_major_locator(mdates.MonthLocator(interval=2))
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
plt.gcf().autofmt_xdate()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('GBM_动态置信区间.png', dpi=300)
plt.show()

# 4. 计算预测误差指标
simulated_mean = mean_path[1:]
actual_prices = test_period.values

mae = np.mean(np.abs(simulated_mean - actual_prices))
rmse = np.sqrt(np.mean((simulated_mean - actual_prices)**2))

print("\n模型性能评估:")
print(f"平均绝对误差(MAE): {mae:.4f}")
print(f"均方根误差(RMSE): {rmse:.4f}")
print(f"年化波动率预测: {sigma:.4f} (实际: {test.std()*np.sqrt(252):.4f})")

# 5. 关键日期对比表
compare_dates = [test_period.index[0], test_period.index[len(test)//2], test_period.index[-1]]
compare_data = []

valid_dates = [d for d in compare_dates if d in date_index]
# valid_dates

for date in valid_dates:
idx = np.where(date_index == date)[0][0]
actual = df.loc[date, '收盘']
sim_mean = mean_path[idx]
sim_median = np.median(simulated_paths[:, idx])

compare_data.append([
date.strftime('%Y-%m-%d'),
actual,
sim_mean,
sim_median,
actual - sim_mean
])

# 创建对比表格
compare_df = pd.DataFrame(compare_data,
columns=['日期', '实际价格', '模拟均值', '模拟中位数', '偏差'])
print("\n关键日期价格对比:")
print(compare_df.to_string(index=False))

结果: