|
1 |
| -using Distributions, Random |
| 1 | +using Distributions, Random, SpecialFunctions, Roots |
2 | 2 |
|
3 | 3 | """
|
4 | 4 | HestonDistribution(S0, V0, κ, θ, σ, ρ, r, T)
|
|
30 | 30 |
|
31 | 31 | Samples `log(S_T)` using the exact Broadie-Kaya method.
|
32 | 32 | """
|
33 |
| -function rand(rng, d::HestonDistribution) |
| 33 | +function sample_V_T(rng::AbstractRNG, d::HestonDistribution) |
| 34 | + κ, θ, σ, V0, T = d.κ, d.θ, d.σ, d.V0, d.T |
| 35 | + |
| 36 | + ν = 4κ*θ / σ^2 # Degrees of freedom |
| 37 | + ψ = 4κ * exp(-κ * T) * V0 / (σ^2 * (1 - exp(-κ * T))) # Noncentrality parameter |
| 38 | + c = σ^2 * (1 - exp(-κ * T)) / (4κ) # Scaling factor |
| 39 | + |
| 40 | + V_T = c * Distributions.rand(rng, NoncentralChisq(ν, ψ)) |
| 41 | + return V_T |
| 42 | +end |
| 43 | + |
| 44 | +function sample_integral_V(VT, rng, dist::HestonDistribution) |
| 45 | + Φ(u) = integral_var_char(u, VT, dist) |
| 46 | + integrand(x) = u -> sin(u * x) / u * real(Φ(u)) |
| 47 | + F(x) = 2 / π * quadgk(integrand(x), 0, Inf)[1] # specify like in paper (trapz) |
| 48 | + lower_bound = 0.0 |
| 49 | + upper_bound = 1000 |
| 50 | + # Generate samples |
| 51 | + unif = Uniform(0,1) # Define uniform distribution |
| 52 | + u = Distributions.rand(rng, unif) |
| 53 | + return inverse_cdf_rootfinding(F, u, lower_bound, upper_bound) |
| 54 | +end |
| 55 | + |
| 56 | +function inverse_cdf_rootfinding(cdf_func, u, y_min, y_max) |
| 57 | + return find_zero(y -> cdf_func(y) - u, (y_min, y_max); atol=1E-2, maxiters=5) # Solve F(y) = u like in paper (newton 2nd order) |
| 58 | +end |
| 59 | + |
| 60 | +using SpecialFunctions |
| 61 | + |
| 62 | +""" Adjusts the argument of z to lie in (-π, π] by shifting it appropriately. """ |
| 63 | +function adjust_argument(z) |
| 64 | + θ = angle(z) # Compute current argument |
| 65 | + m = round(Int, θ / π) # Find the nearest integer multiple of π |
| 66 | + z_adjusted = z * exp(-im * m * π) # Shift argument back into (-π, π] |
| 67 | + return z_adjusted |
| 68 | +end |
| 69 | + |
| 70 | +""" Compute the modified Bessel function I_{-ν}(z) with argument correction. """ |
| 71 | +function besseli_corrected(nu, z) |
| 72 | + z_adj = adjust_argument(z) # Ensure argument is in (-π, π] |
| 73 | + return besseli(nu, z_adj) # Compute Bessel function with corrected input |
| 74 | +end |
| 75 | + |
| 76 | +function integral_var_char(a, VT, dist::HestonDistribution) |
| 77 | + κ, θ, σ, ρ, V0, T, S0, r = dist.κ, dist.θ, dist.σ, dist.ρ, dist.V0, dist.T, dist.S0, dist.r |
| 78 | + γ(a) = √(κ^2 - 2 * σ^2 * a * im) |
| 79 | + d = 4 * θ * κ / σ^2 |
| 80 | + |
| 81 | + ζ(x) = (1 - exp(- x * T)) / x |
| 82 | + first_term = exp(-0.5 * (γ(a) - κ) * T) * ζ(κ) / ζ(γ(a)) |
| 83 | + |
| 84 | + η(x) = x * (1 + exp(- x * T)) / (1 - exp(- x * T)) |
| 85 | + second_term = exp((V0 + VT) / σ^2 * ( η(κ) - η(γ(a)) )) |
| 86 | + |
| 87 | + ν(x) = √(V0 * VT) * 4 * x * exp(-0.5 * x * T) / σ^2 / (1 - exp(- x * T)) |
| 88 | + numerator = besseli_corrected(0.5*d - 1, ν(γ(a))) |
| 89 | + denominator = besseli_corrected(0.5*d - 1, ν(κ)) |
| 90 | + third_term = numerator / denominator |
| 91 | + |
| 92 | + return first_term * second_term * third_term |
| 93 | +end |
| 94 | + |
| 95 | +""" Sample log(S_T) given V_T and integral_V. """ |
| 96 | +function sample_log_S_T(V_T, integral_V, rng::AbstractRNG, d::HestonDistribution) |
34 | 97 | κ, θ, σ, ρ, V0, T, S0, r = d.κ, d.θ, d.σ, d.ρ, d.V0, d.T, d.S0, d.r
|
35 |
| - # 1. Sample V_T using the noncentral chi-squared distribution |
36 |
| - ν = (4κ * θ) / σ^2 |
37 |
| - ψ = (4κ * exp(-κ * T) * V0) / (σ^2 * (1 - exp(-κ * T))) |
38 |
| - V_T = (σ^2 * (1 - exp(-κ * T)) / (4κ)) * Distributions.rand(rng, NoncentralChisq(ν, ψ)) |
39 | 98 |
|
40 |
| - # 2. Sample log(S_T) given V_T |
41 |
| - μ_XT = log(S0) + (r - 0.5 * V0) * T + (1 - exp(-κ * T)) * (θ - V0) / (2κ) |
42 |
| - var_XT = (ρ^2 * V0 * (1 - exp(-κ * T))^2) / (2κ) + (1 - ρ^2) * (V0 * T + θ * (T - (1 - exp(-κ * T)) / κ)) |
43 |
| - |
44 |
| - log_S_T = μ_XT + sqrt(var_XT) * randn(rng) # Sample from Normal(μ_XT, sqrt(var_XT)) |
| 99 | + # Compute conditional mean |
| 100 | + mu = log(S0) + r*T - 0.5*integral_V + (ρ/σ)*(V_T - V0 - κ*θ*T + κ*integral_V) |
| 101 | + |
| 102 | + # Compute conditional variance |
| 103 | + sigma2 = (1 - ρ^2) * integral_V |
| 104 | + |
| 105 | + # Sample log(S_T) |
| 106 | + log_S_T = mu + sqrt(sigma2) * randn(rng) |
| 107 | + |
| 108 | + return log_S_T |
| 109 | +end |
| 110 | + |
| 111 | +""" Full sampling process for S_T """ |
| 112 | +function rand(rng::AbstractRNG, d::HestonDistribution) |
| 113 | + # Step 1: Sample V_T |
| 114 | + V_T = sample_V_T(rng, d) |
| 115 | + |
| 116 | + # Step 2: Sample ∫ V_t dt |
| 117 | + integral_V = sample_integral_V(V_T, rng, d) |
| 118 | + |
| 119 | + # Step 3: Sample log(S_T) |
| 120 | + log_S_T = sample_log_S_T(V_T, integral_V, rng, d) |
45 | 121 |
|
46 | 122 | return exp(log_S_T)
|
47 | 123 | end
|
48 | 124 |
|
| 125 | + |
49 | 126 | """
|
50 | 127 | characteristic_function(d::HestonDistribution, u)
|
51 | 128 |
|
|
0 commit comments