Skip to content

Commit ef00517

Browse files
committed
Merged drif and diffusion functions into SDEFunction, combining SDEFunctions, also with analytical definitions, SDEProblems given a set of processes and a correlation matrix.
1 parent 4b62943 commit ef00517

File tree

5 files changed

+143
-72
lines changed

5 files changed

+143
-72
lines changed

examples/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
55
DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa"
66
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
77
Hedgehog2 = "7f16798b-0e18-40de-98af-932948254698"
8+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
89
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
910
Pluto = "c3e4b0f8-55cb-11ea-2926-15256bba5781"
1011
PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee"

examples/correlated_gbms.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Revise, Hedgehog2, DifferentialEquations, Plots
2+
3+
process = GBMProcess(0.05, 0.2) # μ = 5%, σ = 20%
4+
sde_function_1 = get_sde_function(process)
5+
sde_function_2 = get_sde_function(process)
6+
combined_sde_function = combine_sde_functions((sde_function_1, sde_function_2))
7+
8+
ρ = -0.8
9+
# Correlation matrix
10+
Γ = [1.0 ρ;
11+
ρ 1.0]
12+
13+
u0 = [1.0, 1.0] # Initial conditions
14+
tspan = (0.0, 2.0) # Time span
15+
16+
noise = CorrelatedWienerProcess(Γ, tspan[1], [0.0, 0.0])
17+
# Create multi-dimensional SDE problem
18+
prob = SDEProblem(combined_sde_function, u0, tspan, "p", noise=noise)
19+
prob2 = get_sde_problem((process, process), Γ, u0, tspan, "p")
20+
21+
sol = solve(prob)
22+
plot(sol, plot_analytic=true)
23+
24+
sol2 = solve(prob2)
25+
plot!(sol, plot_analytic=true)

examples/time_dependend_correlated_gbms.jl

Whitespace-only changes.

examples/time_dependent_gbm.jl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using Revise, Hedgehog2, DifferentialEquations, Plots
2+
3+
# Define a constant volatility function and its integral
4+
σ_const = t -> 0.2 # Constant volatility (σ_0 = 0.2)
5+
function mean_σ²_const(t)
6+
σ1² = 0.2^2 # σ_1^2
7+
σ2² = 0.4^2 # σ_2^2
8+
9+
if t < 0.5
10+
return σ1²
11+
else
12+
return (σ1² * 0.5 + σ2² * (t - 0.5)) / t
13+
end
14+
end
15+
16+
17+
# Create the GBMTimeDependent process with constant volatility
18+
process_const = GBMTimeDependent(
19+
0.05, # Drift
20+
σ_const, # Constant volatility function
21+
mean_σ²_const # Integral of σ²(t)
22+
)
23+
24+
# Get SDE function
25+
sde_func_const = get_sde_function(process_const)
26+
27+
# Initial value and time span
28+
u0 = [1.0]
29+
tspan = (0.0, 2.0)
30+
31+
32+
sde_problem_td = SDEProblem(sde_func_const, u0, tspan, seed=12)
33+
sol_td = solve(sde_problem_td)
34+
plot(sol_td)
35+
36+
process_gbm = GBMProcess(0.05, 0.2)
37+
sde_func = get_sde_function(process_gbm)
38+
sde_problem = SDEProblem(sde_func, u0, tspan, seed=12)
39+
sol = solve(sde_problem)
40+
plot!(sol)

src/stochastic_processes/stochastic_processes.jl

Lines changed: 77 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,41 @@
11
using DifferentialEquations
22

3-
export AbstractStochasticProcess, GBMProcess, HestonProcess, get_drift, get_diffusion, get_analytic_solution, get_sde_function
3+
export AbstractStochasticProcess, GBMProcess, HestonProcess, get_sde_function, combine_sde_functions, GBMTimeDependent, get_sde_problem
4+
5+
function combine_sde_functions(sde_functions::Tuple{Vararg{SDEFunction}})
6+
n = length(sde_functions) # Number of 1D SDEs
7+
8+
# Construct multi-dimensional drift function
9+
f = (u, p, t) -> begin
10+
return map(i -> sde_functions[i].f([u[i]], p, t)[1], 1:n)
11+
end
12+
13+
# Construct multi-dimensional diffusion function
14+
g = (u, p, t) -> begin
15+
return map(i -> sde_functions[i].g([u[i]], p, t)[1], 1:n)
16+
end
17+
18+
# Check if all SDEFunctions have an analytic solution
19+
if all(sf -> sf.analytic !== nothing, sde_functions)
20+
# Construct a multi-dimensional analytical solution
21+
analytic = (u0, p, t, W) -> begin
22+
return map(i -> sde_functions[i].analytic([u0[i]], p, t, W[i])[1], 1:n)
23+
end
24+
else
25+
analytic = nothing # No combined analytic solution if any function lacks it
26+
end
27+
28+
return SDEFunction(f, g, analytic=analytic)
29+
end
30+
31+
function get_sde_problem(processes, Γ, u0, tspan, p; kwargs...)
32+
sde_functions = get_sde_function.(processes)
33+
combined_sde_function = combine_sde_functions(sde_functions)
34+
noise = CorrelatedWienerProcess(Γ, tspan[1], zeros(length(processes)))
35+
36+
return SDEProblem(combined_sde_function, u0, tspan, p; noise=noise, kwargs...)
37+
end
38+
439

540
# Abstract type for stochastic processes
641
"""
@@ -21,6 +56,16 @@ struct GBMProcess <: AbstractStochasticProcess
2156
σ
2257
end
2358

59+
function get_sde_function(process::GBMProcess)
60+
drift(u, p, t) = process.μ .* u
61+
62+
diffusion(u, p, t) = process.σ .* u
63+
64+
analytic_solution(u₀, p, t, W) = u₀ .* exp.((process.μ - (process.σ^2) / 2) .* t .+ process.σ * W)
65+
66+
return SDEFunction(drift, diffusion, analytic = analytic_solution)
67+
end
68+
2469
# Define the Heston process
2570
"""
2671
HestonProcess <: AbstractStochasticProcess
@@ -40,84 +85,44 @@ struct HestonProcess <: AbstractStochasticProcess
4085
ρ
4186
end
4287

43-
# Drift function for GBM
44-
"""
45-
drift(process::GBMProcess, u, t)
46-
47-
Computes the drift term of the GBM process at time `t` for state `u`.
48-
Drift equation: `du/dt = μ * u`
49-
"""
50-
function drift(process::GBMProcess, u, t)
51-
return process.μ .* u # Element-wise broadcasting for array compatibility
52-
end
53-
54-
# Drift function for Heston
55-
"""
56-
drift(process::HestonProcess, u, t)
57-
58-
Computes the drift term of the Heston process at time `t` for state `u = (S, V)`.
59-
Drift equations:
60-
- `dS/dt = μ * S`
61-
- `dV/dt = κ * (θ - V)`
62-
"""
63-
function drift(process::HestonProcess, u, t)
64-
S, V = u
65-
return [process.μ * S, process.κ * (process.θ - V)] # Drift for (Stock price, Variance)
66-
end
67-
68-
# Diffusion function for GBM
69-
"""
70-
diffusion(process::GBMProcess, u, t)
71-
72-
Computes the diffusion term of the GBM process at time `t` for state `u`.
73-
Diffusion equation: `dW_t = σ * u * dB_t`
74-
"""
75-
function diffusion(process::GBMProcess, u, t)
76-
return process.σ .* u # Element-wise broadcasting for array compatibility
77-
end
78-
79-
# Diffusion function for Heston
80-
"""
81-
diffusion(process::HestonProcess, u, t)
82-
83-
Computes the diffusion term of the Heston process at time `t` for state `u = (S, V)`.
84-
Diffusion equations:
85-
- `dS_t = σ * sqrt(V) * dB_t`
86-
- `dV_t = ξ * sqrt(V) * dW_t`, with correlation `ρ`.
87-
"""
88-
function diffusion(process::HestonProcess, u, t)
89-
S, V = u
90-
return [process.σ * S * sqrt(V), process.σ * sqrt(V)] # Diffusion for (Stock price, Variance)
88+
function get_sde_function(process::HestonProcess)
89+
function drift(u, p, t)
90+
S, V = u
91+
return [process.μ * S, process.κ * (process.θ - V)]
92+
end
93+
94+
function diffusion(u, p, t)
95+
S, V = u
96+
return [process.σ * S * sqrt(V), process.σ * sqrt(V)]
97+
end
98+
99+
return SDEFunction(drift, diffusion)
91100
end
92101

93-
# Higher-order functions to return drift! and diffusion!
94-
"""
95-
get_drift!(process::AbstractStochasticProcess)
96-
97-
Returns the in-place drift function `drift!` for the given process.
98-
"""
99-
function get_drift(process::P) where P<:AbstractStochasticProcess
100-
return (u, p, t) -> drift(process, u, t)
102+
struct GBMTimeDependent
103+
μ
104+
σ
105+
mean_σ²
101106
end
102107

103-
"""
104-
get_diffusion!(process::AbstractStochasticProcess)
108+
function get_sde_function(process::GBMTimeDependent)
109+
drift(u, p, t) = process.μ .* u
105110

106-
Returns the in-place diffusion function `diffusion!` for the given process.
107-
"""
108-
function get_diffusion(process::P) where P<:AbstractStochasticProcess
109-
return (u, p, t) -> diffusion(process, u, t)
110-
end
111+
diffusion(u, p, t) = sqrt(process.mean_σ²(t)) .* u
111112

112-
function get_analytic_solution(process::P) where P <: AbstractStochasticProcess
113-
return Nothing()
114-
end
113+
# observe that the noise here is integrated.
114+
# That is, W is not such that dX = mu dt + sigma_t dW_t but it is W' such that W'_t = t (int_0^t sigma_s dW_t) / (int_0^t sigma_s^2)
115+
function analytic_solution(u₀, p, t, W)
116+
I_t = sqrt(process.mean_σ²(t)) ./ t ./ t
117+
return u₀ .* exp.((process.μ .- 0.5 * I_t^2) .* t .+ I_t .* W)
118+
end
115119

116-
function get_analytic_solution(::GBMProcess)
117-
return (u₀, p, t, W) -> u₀ * exp((0.05 - (0.2^2) / 2) * t + 0.2 * W)
120+
return SDEFunction(drift, diffusion, analytic = analytic_solution)
118121
end
119122

120-
function get_sde_function(process::P) where P<:AbstractStochasticProcess
121-
return SDEFunction(get_drift(process), get_diffusion(process), analytic=get_analytic_solution(process))
123+
# dX_t = (θ_t - a t) dt + σ_t dW_t
124+
struct OUTimeDependent
125+
θ
126+
a
127+
σ
122128
end
123-

0 commit comments

Comments
 (0)