Skip to content

Commit 2c0a396

Browse files
committed
added antithetic variates for discretized processes
1 parent a8f648e commit 2c0a396

File tree

3 files changed

+101
-10
lines changed

3 files changed

+101
-10
lines changed

examples/antithetic_mc_heston.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using DifferentialEquations
2+
using DiffEqNoiseProcess
3+
using Plots
4+
using Random
5+
using Hedgehog2
6+
7+
8+
u0 = [100.0, 0.04] # Initial (Stock Price, Variance)
9+
tspan = (0.0, 1.0) # Simulation for 1 year
10+
# Create and solve the original problem with saved noise
11+
rng = MersenneTwister(123)
12+
prob = Hedgehog2.HestonProblem(0.05, 2.0, 0.04, 0.3, -0.5, u0, tspan) # Heston parameters
13+
sol = solve(prob, EM(), dt=1//250, save_noise=true)
14+
15+
# Extract the 2D noise and flip it for antithetic
16+
W_t = sol.W.t
17+
W_u = sol.W.W # This is a Matrix: each row is a Brownian component
18+
19+
# Flip both components for antithetic version
20+
antithetic_noise = NoiseGrid(W_t, -W_u)
21+
22+
# Create new problem with antithetic noise
23+
prob_antithetic = remake(prob; noise=antithetic_noise)
24+
sol_anti = solve(prob_antithetic, EM(), dt=1//250)
25+
26+
# Plot only the first component (stock price)
27+
plot(sol.t, sol.u .|> x -> x[1], label="Original", linewidth=2)
28+
plot!(sol_anti.t, sol_anti.u .|> x -> x[1], label="Antithetic", linestyle=:dash, linewidth=2)
29+
xlabel!("Time")
30+
ylabel!("Stock Price")
31+
title!("Heston Process: Original vs Antithetic Paths")

examples/antithetic_montecarlo_EM.jl

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using DifferentialEquations
2+
using DiffEqNoiseProcess
3+
using Random
4+
using Plots
5+
6+
# Parameters
7+
μ = 0.05
8+
σ = 0.2
9+
t0 = 0.0
10+
T = 1.0
11+
S0 = 100.0
12+
tspan = (t0, T)
13+
N = 250
14+
dt = (T - t0) / N
15+
16+
# Define GBM drift and diffusion
17+
function drift!(du, u, p, t)
18+
du[1] = μ * u[1]
19+
end
20+
21+
function diffusion!(du, u, p, t)
22+
du[1] = σ * u[1]
23+
end
24+
25+
# Create noise with fixed seed
26+
rng = MersenneTwister(123)
27+
wiener = WienerProcess(t0, 0.0)
28+
29+
# Define and solve the original GBM SDE
30+
u0 = [S0]
31+
sde = SDEProblem(drift!, diffusion!, u0, tspan, noise=wiener)
32+
sol = DifferentialEquations.solve(sde, dt=dt, save_noise=true)
33+
sol = DifferentialEquations.solve(sde, dt=dt, save_noise=true)
34+
plot(sol.W.t, sol.W.u)
35+
# Extract the used noise path
36+
# W_vals = sol.W[1, :] # Extract Brownian path for the first dimension
37+
38+
# Flip it for antithetic path
39+
wiener_antithetic = NoiseGrid(sol.W.t, -sol.W.W)
40+
41+
# # Define and solve the antithetic GBM SDE
42+
sde_antithetic = SDEProblem(drift!, diffusion!, u0, tspan, noise=wiener_antithetic)
43+
sol_antithetic = solve(sde_antithetic, dt=dt)
44+
45+
# # Plot
46+
plot(sol, label="Original", linewidth=2)
47+
plot!(sol_antithetic, label="Antithetic", linestyle=:dash, linewidth=2)
48+
# xlabel!("Time")
49+
# ylabel!("GBM Value")
50+
# title!("GBM with Antithetic Variates via Flipped Brownian Path")

src/pricing_methods/montecarlo.jl

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,26 +59,37 @@ function sde_problem(::LognormalDynamics, s::BlackScholesExact, market::BlackSch
5959
return NoiseProblem(noise, tspan; s.kwargs...)
6060
end
6161

62+
function antithetic_sde_problem(d::LognormalDynamics, s::SimulationStrategy, m::BlackScholesInputs, tspan, normal_sol)
63+
s_flipped = @set s.seeds = normal_sol.seeds
64+
m_flipped = @set m.sigma = -m.sigma
65+
return sde_problem(d, s_flipped, m_flipped, tspan)
66+
end
67+
6268
function sde_problem(::HestonDynamics, ::EulerMaruyama, m::HestonInputs, tspan)
6369
@assert is_flat(m.rate) "Heston simulation requires flat rate curve"
6470
rate = zero_rate(m.rate, 0.0)
6571
return HestonProblem(rate, m.κ, m.θ, m.σ, m.ρ, [m.spot, m.V0], tspan)
6672
end
6773

74+
function antithetic_sde_problem(d::PriceDynamics, s::EulerMaruyama, m::AbstractMarketInputs, tspan, normal_sol::CustomEnsembleSolution)
75+
base_prob = original_sol.solutions[1].prob
76+
77+
antithetic_modify = function (_base_prob, _seed, i)
78+
sol = original_sol.solutions[i]
79+
flipped_noise = NoiseGrid(sol.W.t, -sol.W.W)
80+
return remake(_base_prob; noise=flipped_noise)
81+
end
82+
83+
return CustomEnsembleProblem(base_prob, normal_sol.seeds, antithetic_modify)
84+
end
85+
6886
function sde_problem(::HestonDynamics, strategy::HestonBroadieKaya, m::HestonInputs, tspan)
6987
@assert is_flat(m.rate) "Heston simulation requires flat rate curve"
7088
rate = zero_rate(m.rate, 0.0)
7189
noise = HestonNoise(rate, m.κ, m.θ, m.σ, m.ρ, 0.0, [log(m.spot), m.V0], Z0=nothing; strategy.kwargs...)
7290
return NoiseProblem(noise, tspan)
7391
end
7492

75-
function antithetic_sde_problem(d::PriceDynamics, s::SimulationStrategy, m::AbstractMarketInputs, tspan, seeds)
76-
s_flipped = @set s.seeds = seeds
77-
m_flipped = @set m.sigma = -m.sigma
78-
return sde_problem(d, s_flipped, m_flipped, tspan)
79-
end
80-
81-
8293
# ------------------ Marginal Laws (optional) ------------------
8394

8495
function marginal_law(::LognormalDynamics, m::BlackScholesInputs, t)
@@ -134,15 +145,14 @@ function simulate_paths(method::MonteCarlo, market_inputs::I, T) where {I <: Abs
134145
end
135146

136147
# Step 2: simulate antithetic paths using same seeds, flipped sigma
137-
seeds = normal_sol.seeds
138-
antithetic_prob = antithetic_sde_problem(dynamics, strategy, market_inputs, tspan, seeds)
148+
antithetic_prob = antithetic_sde_problem(dynamics, strategy, market_inputs, tspan, normal_sol)
139149
antithetic_sol = montecarlo_solution(antithetic_prob, strategy)
140150

141151
# Step 3: combine both solutions
142152
combined_paths = vcat(normal_sol.solutions, antithetic_sol.solutions)
143153

144154
# Create new EnsembleSolution with merged paths
145-
return CustomEnsembleSolution(combined_paths, seeds)
155+
return CustomEnsembleSolution(combined_paths, normal_sol.seeds)
146156
end
147157

148158
function solve(prob::PricingProblem{VanillaOption{European, C, Spot}, I}, method::MonteCarlo) where {C, I <: AbstractMarketInputs}

0 commit comments

Comments
 (0)