定价引擎技术栈:Black-Scholes与局部波动率模型实现、蒙特卡洛模拟加速(GPU/C++)、随机过程与路径生成算法
做场外衍生品做市,说白了就是跟市场对赌风险。但你不能瞎赌,得有个靠谱的定价引擎。今天我就把这套技术栈拆开揉碎了讲给你听。
我个人习惯把定价引擎分成三层:模型层、计算层、加速层。模型层决定你用啥公式,计算层决定你算多准,加速层决定你算多快。这三层缺一不可。
Black-Scholes 模型:老伙计,但别小看它
BS 模型是衍生品定价的基石。虽然它假设波动率是常数,这在现实里根本不成立,但它的解析解给了我们一个很好的起点。
公式我就不抄书了,你肯定见过。关键是实现的时候要注意几个坑:
核心代码其实不长:
// C++ 实现:Black-Scholes 欧式看涨期权定价
#include <cmath>
double norm_cdf(double x) {
// 使用 Abramowitz & Stegun 近似
double a1 = 0.254829592;
double a2 = -0.284496736;
double a3 = 1.421413741;
double a4 = -1.453152027;
double a5 = 1.061405429;
double p = 0.3275911;
int sign = (x < 0) ? -1 : 1;
x = fabs(x) / sqrt(2.0);
double t = 1.0 / (1.0 + p * x);
double y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * exp(-x * x);
return 0.5 * (1.0 + sign * y);
}
double bs_call_price(double S, double K, double T, double r, double sigma) {
double d1 = (log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * sqrt(T));
double d2 = d1 - sigma * sqrt(T);
return S * norm_cdf(d1) - K * exp(-r * T) * norm_cdf(d2);
}
局部波动率模型:更贴近现实
BS 模型假设波动率不变,但市场不是这样的。你想想看,虚值期权的隐含波动率通常比平值的高,这就是所谓的波动率微笑。
局部波动率模型(LV)就是为了解决这个问题。它把波动率写成标的价格和时间的函数:σ(S, t)。
实现 LV 模型,最常用的方法是 Dupire 公式:
σ_LV(K, T)² = (∂C/∂T + rK ∂C/∂K) / (0.5 K² ∂²C/∂K²)
嗯,这里要注意。直接用这个公式会出问题——分母可能接近零,导致波动率爆炸。我在项目中遇到过这种情况,当时回测好好的,一上线就报错。
解决方案是加一个正则化项:
// 伪代码:带正则化的局部波动率计算
double local_vol(double K, double T, double[][] implied_vol_surface) {
double numerator = dC_dT(K, T) + r * K * dC_dK(K, T);
double denominator = 0.5 * K * K * d2C_dK2(K, T);
// 正则化:防止分母过小
double epsilon = 1e-6;
if (fabs(denominator) < epsilon) {
denominator = epsilon * (denominator >= 0 ? 1 : -1);
}
return sqrt(fabs(numerator / denominator));
}
蒙特卡洛模拟:万能但慢
对于路径依赖型产品(比如亚式期权、障碍期权),解析解不存在,只能用蒙特卡洛模拟。
蒙特卡洛的核心是路径生成。最基础的是欧拉离散化:
// C++:几何布朗运动的路径生成
void generate_path(double S0, double mu, double sigma, double T, int steps, double* path) {
double dt = T / steps;
double sqrt_dt = sqrt(dt);
path[0] = S0;
for (int i = 1; i <= steps; ++i) {
double z = normal_random(); // 标准正态随机数
path[i] = path[i-1] * exp((mu - 0.5 * sigma * sigma) * dt + sigma * sqrt_dt * z);
}
}
但这里有个坑:离散化误差。步数太少,结果偏差大;步数太多,计算量爆炸。
我建议用对偶变量法来减少方差。说白了就是生成一对路径:一条用随机数 z,另一条用 -z。两条路径的平均值方差更小。
GPU 加速:把计算时间从小时降到分钟
做市商每天要定价成千上万笔询价,CPU 根本扛不住。我做过一个项目,用 CPU 跑 100 万条路径的蒙特卡洛,需要 3 秒。换成 GPU,只要 15 毫秒。
GPU 加速的核心是并行化。每条路径是独立的,可以同时算。
用 CUDA 实现很简单:
// CUDA kernel:并行路径生成与定价
__global__ void mc_pricing_kernel(float* prices, float S0, float K, float T, float r, float sigma, int steps, int num_paths) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx >= num_paths) return;
float dt = T / steps;
float sqrt_dt = sqrt(dt);
float S = S0;
// 每个线程生成一条路径
curandState state;
curand_init(clock64(), idx, 0, &state);
for (int i = 0; i < steps; ++i) {
float z = curand_normal(&state);
S *= exp((r - 0.5 * sigma * sigma) * dt + sigma * sqrt_dt * z);
}
// 计算 payoff
float payoff = fmaxf(S - K, 0.0f);
prices[idx] = exp(-r * T) * payoff;
}
随机过程与路径生成算法:不止几何布朗运动
实际做市业务中,标的资产的随机过程远比几何布朗运动复杂。常用的有:
| 随机过程 | 适用场景 | 特点 |
|---|---|---|
| 几何布朗运动 (GBM) | 股票、指数 | 简单,但无法捕捉波动率微笑 |
| Heston 模型 | 波动率衍生品 | 随机波动率,能拟合微笑 |
| 跳跃扩散 (Merton) | 个股、商品 | 捕捉突发事件 |
| Variance Gamma | 外汇、加密货币 | 纯跳跃过程,尖峰厚尾 |
路径生成算法也有讲究。除了欧拉法,还有:
- Milstein 方法:比欧拉法多一个二阶项,精度更高
- Runge-Kutta 方法:适合随机波动率模型
- 精确模拟:对于某些过程(如 Ornstein-Uhlenbeck),可以直接解析生成路径
知识体系总览
下面这张图概括了定价引擎技术栈的核心逻辑:
这张图把定价引擎的架构讲得很清楚了。从上到下,模型层提供数学框架,计算层实现具体算法,加速层保证性能。三层缺一不可。
嗯,今天就先聊到这。这套技术栈我用了快十年,每次遇到新问题都能从里面找到解决方案。你先把这些基础打牢,后面我们聊 Greeks 计算和风险管理的时候,你会发现它们都建立在这套引擎之上。
公众号:蓝海资料掘金营,微信deep3321