
用 Python 揭秘均值回归策略:你的收益从何而来?
2026年重磅升级已全面落地!欢迎加入专注财经数据与量化投研的【数据科学实战】知识星球!您将获取持续更新的《财经数据宝典》与《量化投研宝典》,双典协同提供系统化指引;星球内含 500 篇以上独有高质量文章,深度覆盖策略开发、因子分析、风险管理等核心领域,内容基本每日更新;同步推出的「量化因子专题教程」系列(含完整可运行代码与实战案例),系统详解因子构建、回测与优化全流程,并实现日更迭代。我们持续扩充独家内容资源,全方位赋能您的投研效率与专业成长。无论您是量化新手还是资深研究者,这里都是助您少走弯路、事半功倍的理想伙伴,携手共探数据驱动的投资未来!
如果只能对一年后的股价给出一个预测数字,那这个数字大概率是错的。但如果能生成 10000 种可能的未来,再从中统计出"亏损超过 20% 的概率是多少",这就是完全不同层次的答案了。
这正是蒙特卡洛模拟(Monte Carlo Simulation)在量化金融中的核心价值:期权定价、风险度量(VaR)、组合压力测试,背后都离不开它。本文将拆解蒙特卡洛模拟的运行机制,并用不到 40 行的 NumPy 代码,带你完整跑通一次股价模拟。
抛开金融背景,所有蒙特卡洛模拟都遵循同一套流程:
蒙特卡洛方法看似复杂,其实真正的门道几乎都在第 1 步和第 3 步——其余的只是重复。
股价通常用几何布朗运动来建模,它假设收益由两部分驱动:稳定的期望漂移(平均收益)和随机的波动(围绕均值的噪声)。每一步的价格更新公式为:
其中:
易错点提醒:公式中的 (μ - 0.5σ²) 是一个小而关键的修正项。它把平均漂移略微下调,因为"复利叠加随机收益"并不等于"复利叠加平滑的平均收益"。漏掉这个修正,模拟结果会系统性高估股票的长期增长。
只用 NumPy,每一步都清晰可见:
import numpy as np
# --- 输入参数 ---
S0 = 100 # 当前股价
mu = 0.08 # 期望年化收益率(8%)
sigma = 0.20 # 年化波动率(20%)
T = 1.0 # 时间跨度(年)
n_steps = 252 # 一年的交易日数
n_paths = 10000 # 模拟路径数量
dt = T / n_steps # 每步的时间长度
# --- 开始模拟 ---
np.random.seed(42) # 固定随机种子,保证结果可复现
Z = np.random.standard_normal((n_paths, n_steps)) # 随机冲击矩阵
# 计算每日的对数收益率,再累加得到完整价格路径
daily_returns = (mu - 0.5 * sigma**2) * dt + sigma * np.sqrt(dt) * Z
log_paths = np.cumsum(daily_returns, axis=1)
price_paths = S0 * np.exp(log_paths)
# 一年后的最终模拟价格
final_prices = price_paths[:, -1]
# --- 分析结果分布 ---
expected_price = final_prices.mean() # 期望价格
p5, p95 = np.percentile(final_prices, [5, 95]) # 5% 和 95% 分位数
prob_loss_20pct = np.mean(final_prices < S0 * 0.8) # 亏损超 20% 的概率
print(f"一年后的期望价格:{expected_price:.2f}")
print(f"5%–95% 分位区间:{p5:.2f} – {p95:.2f}")
print(f"亏损超过 20% 的概率:{prob_loss_20pct:.2%}")真实世界里,金融变量很少相互独立:组合内的股票收益会一定程度同涨同跌,利率和违约率在经济周期中也倾向于同步起落。如果对每个变量独立模拟、忽略这些关系,模拟会严重低估风险——因为它错过了"坏结果往往扎堆出现"这一事实。
标准解法是 Cholesky 分解:一种矩阵方法,能把一组相互独立的随机数,变换成符合指定相关性结构的随机数。通俗地说,它让你的模拟"知道":当股票 A 崩盘时,股票 B 也更可能跟着崩,而不是把每只股票的命运当成孤立的抛硬币。
# 两个资产之间的相关系数矩阵(相关系数为 0.6)
corr_matrix = np.array([[1.0, 0.6],
[0.6, 1.0]])
L = np.linalg.cholesky(corr_matrix) # Cholesky 分解得到下三角矩阵
# 为两个资产生成相互独立的随机数(每条路径一组)
Z_independent = np.random.standard_normal((n_paths, 2))
# 变换为具有相关性的随机数
Z_correlated = Z_independent @ L.T变换后的 Z_correlated 有两列随机冲击:每一列单独看仍是标准正态分布,但两列之间的相关系数为 0.6,远比"当作两枚不相干的硬币"更接近现实。把前面模拟中的独立随机数替换成它(按资产分别应用),就得到了一个尊重资产联动关系的双资产组合模拟。
值得强调的是:准确估计相关系数,并随市场关系变化及时更新,往往比你跑多少条路径更能决定模拟的实际价值。
单条路径很便宜,贵的是"足够多的路径"和"每步模拟的复杂度"。给一个简单期权定价可能只需几千条路径;而机构级的全面压力测试——模拟整个银行组合、数千个相互作用的风险因子、数千种情景——则可能需要大规模计算基础设施,耗时数小时。
这里有一条重要的收敛规律:误差与模拟次数的平方根成反比。也就是说,想把精度提高一倍,路径数大约要变成原来的 4 倍,而不是 2 倍。这种"边际收益递减"正是当前推动业界探索机器学习替代方案的计算瓶颈——训练一个快速模型来近似昂贵模拟的输出结果。
延伸知识:除了堆算力,量化实践中还有一类"方差缩减技术"可以在不增加路径数的前提下提升精度,例如对偶变量法(antithetic variates,对每个随机数 Z 同时模拟 -Z)和准蒙特卡洛(用低差异序列如 Sobol 序列替代伪随机数)。感兴趣的读者可以进一步研究。
一个反直觉的经验是:模拟引擎本身几乎是机械化的,真正决定结果有没有意义的,是随机数生成器的质量和相关性假设的准确度。
一个技术上完美无缺的模拟,如果建立在估计糟糕的相关系数矩阵或不切实际的波动率假设之上,依然会产出一个漂亮、自信的分布——只不过是"错误答案的漂亮分布"。这类错误的可怕之处在于代码不会报错:它安静地失败,给你一个非常精确的数字,去回答一个从一开始就问错了的问题。
如果要从中提炼一条可以带进任何建模工作的习惯,那就是:花在审视输入假设上的时间,至少要和打磨模拟本身一样多。骰子的诚实程度,取决于装填它的那只手。
2026年全面升级已落地!【数据科学实战】知识星球核心权益如下:
星球已沉淀丰富内容生态——涵盖量化文章专题教程库、因子日更系列、高频数据集、PyBroker实战课程、专家深度分享与实时答疑服务。无论您是初探量化的学习者,还是深耕领域的从业者,这里都是助您少走弯路、高效成长的理想平台。诚邀加入,共探数据驱动的投资未来!
1. 用 Python 打造股票预测系统:Transformer 模型教程(一)
2. 用 Python 打造股票预测系统:Transformer 模型教程(二)
3. 用 Python 打造股票预测系统:Transformer 模型教程(三)
4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)
6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用