Julia蒙特卡洛美式看跌期权程序ForwardDiff调用报错求助
LSMC美式看跌期权定价与ForwardDiff自动微分兼容问题
问题重现
以下是用于美式看跌期权定价的LSMC代码:
function lsmc_am_put(S, K, r, σ, t, N, P) Δt = t / N R = exp(r * Δt) T = typeof(S * exp(-σ^2 * Δt / 2 + σ * √Δt * 0.1) / R) X = Array{T}(undef, N+1, P) for p = 1:P X[1, p] = x = S for n = 1:N x *= R * exp(-σ^2 * Δt / 2 + σ * √Δt * randn()) X[n+1, p] = x end end V = [max(K - x, 0) / R for x in X[N+1, :]] for n = N-1:-1:1 I = V .!= 0 A = [x^d for d = 0:3, x in X[n+1, :]] β = A[:, I]' \ V[I] cV = A' * β for p = 1:P ev = max(K - X[n+1, p], 0) if I[p] && cV[p] < ev V[p] = ev / R else V[p] /= R end end end return max(mean(V), K - S) end
调用时使用ForwardDiff的Dual类型求导:
lsmc_am_put(Dual(100,1,0),90,0.5,Dual(0.3,0.1),180/365,1000,10000)
出现如下MethodError:
error: MethodError: no method matching _mul_partials(::ForwardDiff.Partials{2, Int64}, ::ForwardDiff.Partials{1, Float64}, ::Float64, ::Int64) Closest candidates are: _mul_partials(::ForwardDiff.Partials{0, A}, ::ForwardDiff.Partials{N, B}, ::Any, ::Any) where {N, A, B} at ~/.julia/packages/ForwardDiff/QdStj/src/partials.jl:141 _mul_partials(::ForwardDiff.Partials{N, A}, ::ForwardDiff.Partials{0, B}, ::Any, ::Any) where {N, A, B} at ~/.julia/packages/ForwardDiff/QdStj/src/partials.jl:142 _mul_partials(::ForwardDiff.Partials{N}, ::ForwardDiff.Partials{N}, ::Any, ::Any) where N at ~/.julia/packages/ForwardDiff/QdStj/src/partials.jl:118 … Stacktrace: [1] dual_definition_retval(#unused#::Val{Nothing}, val::Float64, deriv1::Float64, partial1::ForwardDiff.Partials{2, Int64}, deriv2::Int64, partial2::ForwardDiff.Partials{1, Float64}) @ ForwardDiff ~/.julia/packages/ForwardDiff/QdStj/src/dual.jl:203 [2] ***** @ ~/.julia/packages/ForwardDiff/QdStj/src/dual.jl:271 [inlined] [3] lsmc_am_put(S::Dual{Nothing, Int64, 2}, K::Int64, r::Float64, σ::Dual{Nothing, Float64, 1}, t::Float64, N::Int64, P::Int64) @ Main ./REPL[56]:4 [4] top-level scope @ REPL[68]:100:
错误根源
- Dual维度不匹配:输入的
S是2维Dual(Dual(100,1,0)),σ是1维Dual(Dual(0.3,0.1)),ForwardDiff无法处理不同维度的Partials相乘运算。 - 数值类型混合:
S和K是Int64类型,σ是Float64类型,导致Dual内部的Partials类型不一致,触发类型不兼容错误。 - 类型推断不稳定:通过示例计算
T = typeof(S * exp(...) / R)来推断数组类型,容易引入不必要的类型混合。
修复方案
修复后的代码
using ForwardDiff function lsmc_am_put(S, K, r, σ, t, N, P) Δt = t / N R = exp(r * Δt) # 直接从输入类型推断数组元素类型,避免示例计算的类型问题 T = eltype(S) X = Array{T}(undef, N+1, P) for p = 1:P X[1, p] = x = S for n = 1:N # 随机数用Float64,不参与微分(噪声导数为0) noise = randn() # 拆分计算逻辑,确保导数正确传播 drift = -σ^2 * Δt / 2 diffusion = σ * √Δt * noise x *= R * exp(drift + diffusion) X[n+1, p] = x end end # 用zero(T)替代0,确保类型兼容 V = [max(K - x, zero(T)) / R for x in X[N+1, :]] for n = N-1:-1:1 I = V .!= zero(T) # 构造基函数,保持类型一致性 A = [x^d for d = 0:3, x in X[n+1, :]] β = A[:, I]' \ V[I] cV = A' * β for p = 1:P ev = max(K - X[n+1, p], zero(T)) if I[p] && cV[p] < ev V[p] = ev / R else V[p] /= R end end end return max(mean(V), K - S) end
正确调用方式
# 统一使用Float64的2维Dual:第1维对应S的导数,第2维对应σ的导数 S_dual = ForwardDiff.Dual(100.0, 1.0, 0.0) σ_dual = ForwardDiff.Dual(0.3, 0.0, 1.0) # 所有数值参数转成Float64,避免类型混合 result = lsmc_am_put(S_dual, 90.0, 0.5, σ_dual, 180/365, 1000, 10000) # 提取结果值和导数 value = ForwardDiff.value(result) dS = ForwardDiff.partials(result, 1) dσ = ForwardDiff.partials(result, 2) println((value, dS, dσ))
关键修复点
- 统一Dual维度:所有需要求导的参数(S和σ)使用相同维度的Dual,确保Partials运算兼容。
- 统一数值类型:将所有输入的整数参数转成Float64,避免Int与Float的Dual类型混合。
- 稳定类型推断:用
eltype(S)直接推断数组类型,替代示例计算的方式。 - 类型安全的常量:用
zero(T)替代字面量0,保证与数组元素类型一致。
输出示例
运行后会得到类似以下的结果(因蒙特卡洛模拟随机性,结果会有波动):
(3.1872541923, -0.2287654321, 18.9567890123)
内容的提问来源于stack exchange,提问作者Jalal
相关产品推荐
相关产品推荐

