Skip to content

Commit 97a0986

Browse files
committed
Added heston montecarlo agreement tests and checks for seed numbers in SimulationConfig builder.
1 parent c783e05 commit 97a0986

File tree

4 files changed

+277
-53
lines changed

4 files changed

+277
-53
lines changed

examples/mc_heston_euler.jl

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Define the vanilla option payoff
2+
strike = 100.0
3+
expiry_date = Date(2025, 12, 31)
4+
payoff = VanillaOption(strike, expiry_date, European(), Call(), Spot())
5+
6+
# Define the Heston model market inputs using positional arguments
7+
reference_date = Date(2025, 1, 1)
8+
spot = 100.0
9+
rate = 0.05
10+
heston_inputs = HestonInputs(
11+
reference_date,
12+
rate,
13+
spot,
14+
1.5, # kappa
15+
0.04, # theta
16+
0.3, # sigma
17+
-0.6, # rho
18+
0.04 # v0
19+
)
20+
21+
# Create the pricing problem
22+
problem = PricingProblem(payoff, heston_inputs)
23+
24+
# Generate a vector of seeds for reproducibility
25+
num_paths = 10_000
26+
rng = MersenneTwister(42)
27+
seeds = rand(rng, UInt, num_paths)
28+
29+
# Pricing with Broadie-Kaya (Heston Exact) using Antithetic variance reduction
30+
mc_exact_method = MonteCarlo(
31+
HestonDynamics(),
32+
HestonBroadieKaya(),
33+
SimulationConfig(num_paths; seeds=seeds)
34+
)
35+
solution_exact = solve(problem, mc_exact_method)
36+
price_exact = solution_exact.price
37+
38+
# Pricing with Euler-Maruyama using Antithetic variance reduction
39+
mc_euler_method = MonteCarlo(
40+
HestonDynamics(),
41+
EulerMaruyama(),
42+
SimulationConfig(10*num_paths; steps=200, seeds=seeds, variance_reduction=Hedgehog.Antithetic())
43+
)
44+
solution_euler = solve(problem, mc_euler_method)
45+
sample_at_expiry = Hedgehog.get_final_samples(problem, mc_euler_method)
46+
sde_prob = Hedgehog.sde_problem(problem, mc_euler_method)
47+
ens = Hedgehog.simulate_paths(sde_prob, mc_euler_method, mc_euler_method.config.variance_reduction)
48+
49+
config = mc_euler_method.config
50+
dt = sde_prob.tspan[2] / config.steps
51+
ensemble_prob = Hedgehog.get_ensemble_problem(sde_prob, config)
52+
normal_sol = StochasticDiffEq.solve(ensemble_prob, EM(); dt = dt, trajectories=config.trajectories)
53+
54+
price_euler = solution_euler.price

examples/montecarlo_heston.jl

Lines changed: 107 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,108 @@
1-
using Revise, Hedgehog, BenchmarkTools, Dates
2-
3-
"""Example code with benchmarks"""
4-
5-
# Define market inputs
6-
reference_date = Date(2020, 1, 1)
7-
8-
# Define Heston model parameters
9-
S0 = 100 # Initial stock price
10-
V0 = 0.010201 # Initial variance
11-
κ = 6.21 # Mean reversion speed
12-
θ = 0.019 # Long-run variance
13-
σ = 0.61 # Volatility of variance
14-
ρ = -0.7 # Correlation
15-
r = 0.0319 # Risk-free rate
16-
T = 1.0 # Time to maturity
17-
market_inputs = Hedgehog.HestonInputs(reference_date, r, S0, V0, κ, θ, σ, ρ)
18-
bs_market_inputs = BlackScholesInputs(reference_date, r, S0, sqrt(V0))
19-
# Define payoff
20-
expiry = reference_date + Day(365)
21-
strike = 100
22-
payoff =
23-
VanillaOption(strike, expiry, Hedgehog.European(), Hedgehog.Call(), Hedgehog.Spot())
24-
25-
# Define carr madan method
26-
boundary = 32
27-
α = 1
28-
method_heston = Hedgehog.CarrMadan(α, boundary, HestonDynamics())
29-
30-
# Define pricer
31-
pricing_problem = PricingProblem(payoff, market_inputs)
32-
analytic_sol = Hedgehog.solve(pricing_problem, method_heston)
33-
34-
dynamics = HestonDynamics()
35-
trajectories = 10000
36-
config = Hedgehog.SimulationConfig(trajectories; steps=100, variance_reduction=Hedgehog.NoVarianceReduction())
37-
config_exact = Hedgehog.SimulationConfig(trajectories; steps=2, variance_reduction=Hedgehog.NoVarianceReduction())
38-
39-
montecarlo_method = MonteCarlo(dynamics, EulerMaruyama(), config)
40-
montecarlo_method_exact = MonteCarlo(dynamics, HestonBroadieKaya(), config_exact)
41-
42-
solution = Hedgehog.solve(pricing_problem, montecarlo_method)
43-
solution_exact = Hedgehog.solve(pricing_problem, montecarlo_method_exact)
44-
45-
@show solution.price
46-
@show analytic_sol.price
47-
@show solution_exact.price
48-
49-
@btime Hedgehog.solve($pricing_problem, $montecarlo_method_exact).price
50-
@btime Hedgehog.solve($pricing_problem, $montecarlo_method).price
1+
using Test
2+
using Hedgehog
3+
using Dates
4+
using Random
515

6+
@testset "Heston Exact Simulation vs Euler Maruyama" begin
7+
# Define the vanilla option payoff
8+
strike = 100.0
9+
expiry_date = Date(2025, 12, 31)
10+
payoff = VanillaOption(strike, expiry_date, European(), Call(), Spot())
11+
12+
# Define the Heston model market inputs using positional arguments
13+
reference_date = Date(2025, 1, 1)
14+
spot = 100.0
15+
rate = 0.05
16+
heston_inputs = HestonInputs(
17+
reference_date,
18+
rate,
19+
spot,
20+
1.5, # kappa
21+
0.04, # theta
22+
0.3, # sigma
23+
-0.6, # rho
24+
0.04 # v0
25+
)
26+
27+
# Create the pricing problem
28+
problem = PricingProblem(payoff, heston_inputs)
29+
30+
# Generate a vector of seeds for reproducibility
31+
num_paths = 10_000
32+
rng = MersenneTwister(42)
33+
seeds = rand(rng, UInt, 10*num_paths)
34+
35+
# Pricing with Broadie-Kaya (Heston Exact) using Antithetic variance reduction
36+
mc_exact_method = MonteCarlo(
37+
HestonDynamics(),
38+
HestonBroadieKaya(),
39+
SimulationConfig(num_paths; seeds=seeds)
40+
)
41+
solution_exact = solve(problem, mc_exact_method)
42+
price_exact = solution_exact.price
43+
44+
# Pricing with Euler-Maruyama using Antithetic variance reduction
45+
mc_euler_method = MonteCarlo(
46+
HestonDynamics(),
47+
EulerMaruyama(),
48+
SimulationConfig(num_paths; steps=200, seeds=seeds, variance_reduction=Hedgehog.Antithetic())
49+
)
50+
solution_euler = solve(problem, mc_euler_method)
51+
price_euler = solution_euler.price
52+
53+
# Pricing with Carr-Madan
54+
carr_madan_method = CarrMadan(1.0, 32.0, HestonDynamics())
55+
solution_carr_madan = solve(problem, carr_madan_method)
56+
price_carr_madan = solution_carr_madan.price
57+
58+
# Compare the prices with a lower tolerance
59+
@test isapprox(price_exact, price_euler, rtol=5e-2)
60+
@test isapprox(price_exact, price_carr_madan, rtol=2e-2)
61+
end
62+
63+
@testset "Heston Exact Simulation vs Carr-Madan" begin
64+
# Define the vanilla option payoff
65+
strike = 100.0
66+
expiry_date = Date(2025, 12, 31)
67+
payoff = VanillaOption(strike, expiry_date, European(), Call(), Spot())
68+
69+
# Define the Heston model market inputs using positional arguments
70+
reference_date = Date(2025, 1, 1)
71+
spot = 100.0
72+
rate = 0.05
73+
heston_inputs = HestonInputs(
74+
reference_date,
75+
rate,
76+
spot,
77+
1.5, # kappa
78+
0.04, # theta
79+
0.3, # sigma
80+
-0.6, # rho
81+
0.04 # v0
82+
)
83+
84+
# Create the pricing problem
85+
problem = PricingProblem(payoff, heston_inputs)
86+
87+
# Generate a vector of seeds for reproducibility
88+
num_paths = 10_000
89+
rng = MersenneTwister(42)
90+
seeds = rand(rng, UInt, num_paths)
91+
92+
# Pricing with Broadie-Kaya (Heston Exact) using Antithetic variance reduction
93+
mc_exact_method = MonteCarlo(
94+
HestonDynamics(),
95+
HestonBroadieKaya(),
96+
SimulationConfig(num_paths; seeds=seeds)
97+
)
98+
solution_exact = solve(problem, mc_exact_method)
99+
price_exact = solution_exact.price
100+
101+
# Pricing with Carr-Madan
102+
carr_madan_method = CarrMadan(1.0, 32.0, HestonDynamics())
103+
solution_carr_madan = solve(problem, carr_madan_method)
104+
price_carr_madan = solution_carr_madan.price
105+
106+
# Compare the prices with a lower tolerance
107+
@test isapprox(price_exact, price_carr_madan, rtol=2e-2)
108+
end

src/pricing_methods/montecarlo.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,20 @@ struct SimulationConfig{I, S, V<:VarianceReductionStrategy, TSeeds}
6060
steps::S
6161
variance_reduction::V
6262
seeds::Vector{TSeeds}
63+
64+
function SimulationConfig(trajectories::I, steps::S, variance_reduction::V, seeds::Vector{TSeeds}) where {I, S, V<:VarianceReductionStrategy, TSeeds}
65+
length(seeds) < trajectories &&
66+
throw(ArgumentError("Number of seeds ($(length(seeds))) must be ≥ number of trajectories ($trajectories)."))
67+
new{I, S, V, TSeeds}(trajectories, steps, variance_reduction, seeds)
68+
end
6369
end
6470

6571
"""
6672
SimulationConfig(trajectories; steps=1, seeds=nothing, variance_reduction=Antithetic())
6773
6874
Constructor for `SimulationConfig`. If `seeds` is not provided, random seeds are generated.
6975
"""
70-
SimulationConfig(trajectories; steps = 1, seeds = nothing, variance_reduction=Antithetic()) = begin
76+
SimulationConfig(trajectories; steps = 1, seeds = nothing, variance_reduction=NoVarianceReduction()) = begin
7177
seeds === nothing && (seeds = Base.rand(UInt64, trajectories))
7278
SimulationConfig(trajectories, steps, variance_reduction, seeds)
7379
end
@@ -478,10 +484,8 @@ function solve(
478484

479485
# Get samples using the dispatched helper
480486
sample_at_expiry = get_final_samples(prob, method)
481-
@show typeof(sample_at_expiry)
482487
# Common logic for pricing
483488
payoffs = reduce_payoffs(sample_at_expiry, prob.payoff, config.variance_reduction)
484-
@show typeof(payoffs)
485489
discount = df(prob.market_inputs.rate, prob.payoff.expiry)
486490
price = discount * mean(payoffs)
487491

test/agreement/montecarlo_heston.jl

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,112 @@ using Printf
142142
)
143143
end
144144
end
145+
146+
using Test
147+
using Hedgehog
148+
using Dates
149+
using Random
150+
151+
@testset "Heston Exact Simulation vs Euler Maruyama" begin
152+
# Define the vanilla option payoff
153+
strike = 100.0
154+
expiry_date = Date(2025, 12, 31)
155+
payoff = VanillaOption(strike, expiry_date, European(), Call(), Spot())
156+
157+
# Define the Heston model market inputs using positional arguments
158+
reference_date = Date(2025, 1, 1)
159+
spot = 100.0
160+
rate = 0.05
161+
heston_inputs = HestonInputs(
162+
reference_date,
163+
rate,
164+
spot,
165+
1.5, # kappa
166+
0.04, # theta
167+
0.3, # sigma
168+
-0.6, # rho
169+
0.04 # v0
170+
)
171+
172+
# Create the pricing problem
173+
problem = PricingProblem(payoff, heston_inputs)
174+
175+
# Generate a vector of seeds for reproducibility
176+
num_paths = 10_000
177+
rng = MersenneTwister(42)
178+
seeds = rand(rng, UInt, 10*num_paths)
179+
180+
# Pricing with Broadie-Kaya (Heston Exact) using Antithetic variance reduction
181+
mc_exact_method = MonteCarlo(
182+
HestonDynamics(),
183+
HestonBroadieKaya(),
184+
SimulationConfig(num_paths; seeds=seeds)
185+
)
186+
solution_exact = solve(problem, mc_exact_method)
187+
price_exact = solution_exact.price
188+
189+
# Pricing with Euler-Maruyama using Antithetic variance reduction
190+
mc_euler_method = MonteCarlo(
191+
HestonDynamics(),
192+
EulerMaruyama(),
193+
SimulationConfig(num_paths; steps=200, seeds=seeds, variance_reduction=Hedgehog.Antithetic())
194+
)
195+
solution_euler = solve(problem, mc_euler_method)
196+
price_euler = solution_euler.price
197+
198+
# Pricing with Carr-Madan
199+
carr_madan_method = CarrMadan(1.0, 32.0, HestonDynamics())
200+
solution_carr_madan = solve(problem, carr_madan_method)
201+
price_carr_madan = solution_carr_madan.price
202+
203+
# Compare the prices with a lower tolerance
204+
@test isapprox(price_exact, price_euler, rtol=5e-2)
205+
@test isapprox(price_exact, price_carr_madan, rtol=2e-2)
206+
end
207+
208+
@testset "Heston Exact Simulation vs Carr-Madan" begin
209+
# Define the vanilla option payoff
210+
strike = 100.0
211+
expiry_date = Date(2025, 12, 31)
212+
payoff = VanillaOption(strike, expiry_date, European(), Call(), Spot())
213+
214+
# Define the Heston model market inputs using positional arguments
215+
reference_date = Date(2025, 1, 1)
216+
spot = 100.0
217+
rate = 0.05
218+
heston_inputs = HestonInputs(
219+
reference_date,
220+
rate,
221+
spot,
222+
1.5, # kappa
223+
0.04, # theta
224+
0.3, # sigma
225+
-0.6, # rho
226+
0.04 # v0
227+
)
228+
229+
# Create the pricing problem
230+
problem = PricingProblem(payoff, heston_inputs)
231+
232+
# Generate a vector of seeds for reproducibility
233+
num_paths = 10_000
234+
rng = MersenneTwister(42)
235+
seeds = rand(rng, UInt, num_paths)
236+
237+
# Pricing with Broadie-Kaya (Heston Exact) using Antithetic variance reduction
238+
mc_exact_method = MonteCarlo(
239+
HestonDynamics(),
240+
HestonBroadieKaya(),
241+
SimulationConfig(num_paths; seeds=seeds)
242+
)
243+
solution_exact = solve(problem, mc_exact_method)
244+
price_exact = solution_exact.price
245+
246+
# Pricing with Carr-Madan
247+
carr_madan_method = CarrMadan(1.0, 32.0, HestonDynamics())
248+
solution_carr_madan = solve(problem, carr_madan_method)
249+
price_carr_madan = solution_carr_madan.price
250+
251+
# Compare the prices with a lower tolerance
252+
@test isapprox(price_exact, price_carr_madan, rtol=2e-2)
253+
end

0 commit comments

Comments
 (0)