Skip to content

Commit 16bb332

Browse files
committed
Setup broadie kaya exact simulation
1 parent 873b18a commit 16bb332

File tree

3 files changed

+96
-15
lines changed

3 files changed

+96
-15
lines changed

Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
1212
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
1313
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
1414
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
15+
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
16+
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
1517
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1618
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
1719

@@ -24,6 +26,8 @@ Distributions = "0.25.117"
2426
ForwardDiff = "0.10.38"
2527
QuadGK = "2.11.2"
2628
Random = "1.11.0"
29+
Roots = "2.2.6"
30+
SpecialFunctions = "2.5.0"
2731
StaticArrays = "1.9.13"
2832
Statistics = "1.11.1"
2933
julia = "1.11"

examples/heston_noise.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ heston_noise = Hedgehog2.HestonNoise(0.0, heston_dist)
1919
# Define `NoiseProblem`
2020
problem = NoiseProblem(heston_noise, (0.0, T))
2121

22-
trajectories = 10000
22+
trajectories = 10
2323
# Solve with multiple trajectories
2424
solution_h = solve(EnsembleProblem(problem), dt=T, trajectories=trajectories)
2525

@@ -28,10 +28,10 @@ tspan = (0.0, 1.0) # Simulation for 1 year
2828
process = HestonProcess(r, κ, θ, σ, ρ) # Heston parameters
2929
prob = SDEProblem(get_sde_function(process), u0, tspan, process)
3030
ensemble_problem = EnsembleProblem(prob)
31-
solution_h2 = solve(ensemble_problem, dt=T, trajectories=trajectories)
31+
solution_h2 = solve(ensemble_problem, dt=T/1000.0, trajectories=trajectories)
3232

3333
final_prices_2 = [sol.u[end][1] for sol in solution_h2] # Extract S_T for each trajectory
3434
final_prices = [sol.u[end] for sol in solution_h] # Transform log-prices to prices
3535

36-
println(mean(final_prices_2.^3))
37-
println(mean(final_prices.^3))
36+
println(mean(final_prices_2))
37+
println(mean(final_prices))

src/distributions/heston.jl

Lines changed: 88 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Distributions, Random
1+
using Distributions, Random, SpecialFunctions, Roots
22

33
"""
44
HestonDistribution(S0, V0, κ, θ, σ, ρ, r, T)
@@ -30,22 +30,99 @@ end
3030
3131
Samples `log(S_T)` using the exact Broadie-Kaya method.
3232
"""
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)
3497
κ, θ, σ, ρ, 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(ν, ψ))
3998

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)
45121

46122
return exp(log_S_T)
47123
end
48124

125+
49126
"""
50127
characteristic_function(d::HestonDistribution, u)
51128

0 commit comments

Comments
 (0)