3、概率分布与采样:均匀分布、正态分布、对数正态分布在金融建模中的应用

做量化金融这些年,我越来越觉得——不懂概率分布,就别谈衍生品定价。蒙特卡洛模拟说白了,就是靠大量随机样本来逼近真实分布的过程。你生成的随机数质量,直接决定了定价的精度。

今天咱们就聊聊金融建模里最常用的三种分布:均匀分布、正态分布、对数正态分布。我会结合自己踩过的坑,把它们的原理、采样方法和应用场景讲透。

3.1 均匀分布:一切随机数的起点

均匀分布是所有随机数生成的基石。你想想看,计算机生成的伪随机数,本质上都是 [0,1) 上的均匀分布。其他分布,都是通过变换均匀分布得到的。

定义:在区间 [a,b] 上,每个点出现的概率相等。概率密度函数为:

f(x) = 1/(b-a),  a ≤ x ≤ b
     = 0,        其他

金融应用

  • 生成其他分布的种子随机数
  • 模拟离散事件(如违约时间)
  • 重要性采样中的权重计算
我的经验:刚入行时我用 Python 的 random.uniform() 生成随机数,结果发现回测结果不稳定。后来改用 numpy.random.uniform(),性能提升了一个数量级。记住:金融计算永远用 numpy,别用标准库 random

3.2 正态分布:金融世界的基石

正态分布,也叫高斯分布。为什么它在金融里这么重要?因为中心极限定理告诉我们:大量独立随机变量的和,趋近于正态分布。股票收益率、利率变动,很多金融时间序列都近似服从正态分布。

定义:由均值 μ 和标准差 σ 决定。概率密度函数:

f(x) = (1/(σ√(2π))) * exp(-(x-μ)²/(2σ²))

采样方法

  1. Box-Muller 变换:从均匀分布生成两个独立的正态随机数
  2. 逆变换法:利用正态分布累积分布函数的逆函数
  3. numpy 直接采样:np.random.normal(mu, sigma, size)

避坑指南:我曾经在定价一个奇异期权时,直接用 np.random.randn() 生成随机数,结果发现方差总是不对。后来才意识到——randn() 生成的是标准正态分布(均值为0,标准差为1),需要手动缩放。正确的做法是:

# 错误示范
z = np.random.randn(10000)
returns = mu + sigma * z  # 正确!

# 错误示范
returns = np.random.normal(mu, sigma, 10000)  # 这样也行

3.3 对数正态分布:资产价格的天然模型

为什么股票价格通常用对数正态分布建模?原因很简单:价格不能为负。正态分布允许负值,但对数正态分布只取正值。而且,如果你假设收益率服从正态分布,那么价格自然服从对数正态分布。

定义:如果 ln(S) 服从正态分布,则 S 服从对数正态分布。概率密度函数:

f(S) = (1/(Sσ√(2π))) * exp(-(ln(S)-μ)²/(2σ²)),  S > 0

关键性质

  • 均值:E[S] = exp(μ + σ²/2)
  • 方差:Var[S] = exp(2μ+σ²) * (exp(σ²)-1)
  • 中位数:exp(μ)
注意:对数正态分布的均值不等于 exp(μ)。我见过不少新手直接用 exp(μ) 作为期望价格,结果定价偏差很大。正确的期望是 exp(μ + σ²/2)。这个修正项在期权定价里特别重要。

3.4 三种分布的关系与转换

这三种分布之间可以互相转换。我画了一张图,帮你理清它们的关系:

均匀分布 U(a,b) [0,1) 上的均匀 正态分布 N(μ, σ²) 标准正态 N(0,1) 对数正态分布 LogN(μ, σ²) S = exp(X) Box-Muller exp() ln() 核心转换关系 1. 均匀分布 → 正态分布:Box-Muller 变换 z1 = sqrt(-2*ln(u1)) * cos(2π*u2) 2. 正态分布 → 对数正态分布:指数变换 S = S0 * exp((r - σ²/2)*T + σ*sqrt(T)*z) 3. 对数正态分布 → 正态分布:对数变换 X = ln(S) 服从正态分布

3.5 实战:用蒙特卡洛模拟股票价格路径

说了这么多理论,咱们直接上代码。这是我最常用的股票价格模拟模板:

import numpy as np
import matplotlib.pyplot as plt

def simulate_stock_path(S0, mu, sigma, T, N, n_paths=1000):
    """
    模拟股票价格路径(几何布朗运动)
    
    参数:
    S0: 初始价格
    mu: 年化收益率
    sigma: 年化波动率
    T: 时间期限(年)
    N: 时间步数
    n_paths: 模拟路径数
    """
    dt = T / N
    # 生成正态分布随机数
    z = np.random.normal(0, 1, (n_paths, N))
    
    # 构建价格路径
    S = np.zeros((n_paths, N+1))
    S[:, 0] = S0
    
    for t in range(1, N+1):
        # 对数正态分布的核心公式
        S[:, t] = S[:, t-1] * np.exp(
            (mu - 0.5 * sigma**2) * dt + sigma * np.sqrt(dt) * z[:, t-1]
        )
    
    return S

# 使用示例
S0, mu, sigma, T = 100, 0.05, 0.2, 1.0
paths = simulate_stock_path(S0, mu, sigma, T, N=252, n_paths=5)

print("模拟的5条路径终点价格:", paths[:, -1])

我的习惯:在实际项目中,我一般会模拟 10000 条路径以上。但注意,路径数不是越多越好。我曾经为了追求精度,模拟了 100 万条路径,结果计算时间太长,反而耽误了交易时机。建议根据期权类型和精度要求,选择 5000-50000 条路径。

3.6 常见陷阱与避坑指南

陷阱 错误做法 正确做法
随机数种子 每次运行不设种子,结果不可复现 np.random.seed(42) 固定种子
正态性假设 默认所有金融数据服从正态分布 先做正态性检验(如 Jarque-Bera 检验)
对数正态均值 用 exp(μ) 作为期望价格 用 exp(μ + σ²/2) 计算期望
时间步长 直接用年化参数,忽略时间离散化 根据步数 N 调整 dt = T/N
我曾经踩过的坑:有一次做回测,我用正态分布模拟收益率,结果发现极端损失出现的频率远高于模型预测。后来才发现——真实金融数据有厚尾特征,单纯用正态分布会低估尾部风险。从那以后,我养成了习惯:先用历史数据检验分布假设,再决定用哪种分布。

3.7 小结

均匀分布、正态分布、对数正态分布,这三兄弟是蒙特卡洛模拟的基石。均匀分布是源头,正态分布是桥梁,对数正态分布是终点。记住它们之间的转换关系,你就能应对 90% 的衍生品定价场景。

嗯,这里要注意:理论模型再漂亮,也要经得起实际数据的检验。我建议你在每次建模前,先跑一遍分布检验,看看你的数据到底服从什么分布。别偷懒,这个习惯能帮你省下不少调试时间。


公众号:蓝海资料掘金营,微信deep3321