Skip to content

Commit 11a4eef

Browse files
authored
Mc speed up (#17)
* Updated roadmap * first draft of my EnsembleSol with reuse of noiseproblem * first sketch of fast noise solvers * Benchmarking speed up for montecarlo exact * First quick solve with alloc * Best alloc free version so far * Working on Montecarlo using marginal law for exact simulation and euro payoffs * New way to do montecarlo sampling from Distributions.rand * Seeding montecarlo sampler * Added license badge * Documenting montecarlo * Consolidated montecarlo european solvers * Added docstrings * Moved heston to log space * Debugging heston exact * Correcting sampling of log_price for heston * Added heston montecarlo agreement tests and checks for seed numbers in SimulationConfig builder. * Testing LSM against binomial tree * Little corrections * more euler heston paths * Added montecarlo methods benchmark * Removed old working examples * Trigger CI * CI name change to allow PR checks succeeding
1 parent 394f6ac commit 11a4eef

16 files changed

+990
-307
lines changed

.github/workflows/CI.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
name: CI
2+
23
on:
34
push:
45
branches:
56
- master
67
tags: ['*']
78
pull_request:
89
workflow_dispatch:
10+
911
concurrency:
10-
# Skip intermediate builds: always.
11-
# Cancel intermediate builds: only if it is a pull request build.
1212
group: ${{ github.workflow }}-${{ github.ref }}
1313
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
14+
1415
jobs:
1516
test:
16-
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
17+
name: CI
1718
runs-on: ${{ matrix.os }}
1819
timeout-minutes: 60
19-
permissions: # needed to allow julia-actions/cache to proactively delete old caches that it has created
20+
permissions:
2021
actions: write
2122
contents: read
2223
strategy:

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
[![Build Status](https://github.com/aleCombi/Hedgehog.jl/actions/workflows/CI.yml/badge.svg?branch=master)](https://github.com/aleCombi/Hedgehog.jl/actions/workflows/CI.yml?query=branch%3Amaster)
66
[![Documentation](https://img.shields.io/badge/docs-stable-blue.svg)](https://alecombi.github.io/Hedgehog.jl/)
7+
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
78

89
## 📐 Design Overview
910

examples/Project.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
44
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
55
DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0"
66
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
7+
DiffEqNoiseProcess = "77a26b50-5914-5dd7-bc55-306e6241c503"
78
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
89
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
910
Hedgehog = "7f16798b-0e18-40de-98af-932948254698"
@@ -14,8 +15,10 @@ Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
1415
Pluto = "c3e4b0f8-55cb-11ea-2926-15256bba5781"
1516
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
1617
Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45"
18+
ProfileView = "c46f51b8-102a-5cf2-8d2c-8597cb0e0da7"
1719
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
1820
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
21+
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
1922
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
2023
StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0"
2124
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

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_benchmark.jl

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using Revise
2+
using Hedgehog
3+
using Dates
4+
using Printf
5+
using BenchmarkTools
6+
using Random
7+
8+
function test_eur()
9+
spot = 100.0
10+
strike = 100.0
11+
rate = 0.05
12+
sigma = 0.20
13+
reference_date = Date(2023, 1, 1)
14+
expiry = reference_date + Year(1)
15+
16+
# Create the payoff (European call option)
17+
payoff = VanillaOption(strike, expiry, European(), Call(), Spot())
18+
19+
# Create market inputs
20+
market_inputs = BlackScholesInputs(reference_date, rate, spot, sigma)
21+
22+
# Create pricing problem
23+
prob = PricingProblem(payoff, market_inputs)
24+
25+
trajectories = 5_000
26+
mc_exact_method =
27+
MonteCarlo(LognormalDynamics(), BlackScholesExact(), SimulationConfig(trajectories))
28+
mc_exact_solution = solve(prob, mc_exact_method)
29+
@show mc_exact_solution.price
30+
31+
display(@benchmark solve($prob, $mc_exact_method))
32+
end
33+
34+
function test_am()
35+
@show "american"
36+
# Define market inputs
37+
strike = 10.0
38+
reference_date = Date(2020, 1, 1)
39+
expiry = reference_date + Year(1)
40+
rate = 0.05
41+
spot = 10.0
42+
sigma = 0.2
43+
market_inputs = BlackScholesInputs(reference_date, rate, spot, sigma)
44+
45+
# Define payoff
46+
american_payoff = VanillaOption(strike, expiry, American(), Put(), Spot())
47+
48+
# -- Wrap everything into a pricing problem
49+
prob = PricingProblem(american_payoff, market_inputs)
50+
51+
# --- LSM using `solve(...)` style
52+
dynamics = LognormalDynamics()
53+
trajectories = 10_000
54+
steps_lsm = 100
55+
56+
strategy = BlackScholesExact()
57+
config = Hedgehog.SimulationConfig(trajectories; steps=steps_lsm, variance_reduction=Hedgehog.Antithetic()
58+
#variance_reduction=Hedgehog.NoVarianceReduction()
59+
)
60+
degree = 5
61+
lsm_method = LSM(dynamics, strategy, config, degree)
62+
63+
lsm_solution = Hedgehog.solve(prob, lsm_method)
64+
65+
@show lsm_solution.price
66+
67+
display(@benchmark Hedgehog.solve($prob, $lsm_method))
68+
end
69+
70+
test_eur()
71+
test_am()

examples/montecarlo_exact.jl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using Revise, Hedgehog, BenchmarkTools, Dates
2+
using Accessors
3+
import Accessors: @optic
4+
using StochasticDiffEq
5+
6+
# ------------------------------
7+
# Define payoff and pricing problem
8+
# ------------------------------
9+
strike = 1.0
10+
expiry = Date(2021, 1, 1)
11+
12+
euro_payoff = VanillaOption(strike, expiry, European(), Put(), Spot())
13+
14+
reference_date = Date(2020, 1, 1)
15+
rate = 0.03
16+
spot = 1.0
17+
sigma = 0.04
18+
19+
market_inputs = BlackScholesInputs(reference_date, rate, spot, sigma)
20+
euro_pricing_prob = PricingProblem(euro_payoff, market_inputs)
21+
22+
dynamics = LognormalDynamics()
23+
trajectories = 10000
24+
config = Hedgehog.SimulationConfig(trajectories; steps=100, variance_reduction=Hedgehog.Antithetic())
25+
strategy = EulerMaruyama()
26+
montecarlo_method = MonteCarlo(dynamics, strategy, config)
27+
28+
solution_analytic = Hedgehog.solve(euro_pricing_prob, BlackScholesAnalytic()).price
29+
solution = Hedgehog.solve(euro_pricing_prob, montecarlo_method).price
30+
31+
@show Hedgehog.solve(euro_pricing_prob, montecarlo_method).price
32+
@show Hedgehog.solve(euro_pricing_prob, BlackScholesAnalytic()).price
33+
34+
law = Hedgehog.marginal_law(euro_pricing_prob, LognormalDynamics(), expiry) #log-marginal law
35+
log_sample = rand(law, trajectories)
36+
final_sample = exp.(log_sample)
37+
payoff_sample = euro_payoff.(final_sample)
38+
discount = df(euro_pricing_prob.market_inputs.rate, euro_pricing_prob.payoff.expiry)
39+
price = discount * mean(payoff_sample)
40+
41+
antithetic_sample = exp.(2 * mean(law) .- log_sample)
42+
payoff_anti_sample = (euro_payoff.(final_sample) + euro_payoff.(antithetic_sample)) / 2
43+
price_anti = discount * mean(payoff_anti_sample)
44+
45+
montecarlo_method_exact = MonteCarlo(dynamics, BlackScholesExact(), config)
46+
solution_exact = Hedgehog.solve(euro_pricing_prob, montecarlo_method_exact).price
47+
@btime Hedgehog.solve($euro_pricing_prob, $montecarlo_method_exact).price
48+
@show solution_exact

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=1, 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

0 commit comments

Comments
 (0)