From f7f2db26dee31880bdd0a20e04d916c8df1731ab Mon Sep 17 00:00:00 2001 From: Daniel Boros Date: Thu, 12 Sep 2024 02:03:43 +0200 Subject: [PATCH 1/4] fix: index errors --- stochastic-rs-core/src/lib.rs | 2 +- stochastic-rs-core/src/main.rs | 43 ++++++++++++++++----------- stochastic-rs-core/src/noise/fgn.rs | 4 +-- stochastic-rs-core/src/process/fbm.rs | 10 +++---- 4 files changed, 33 insertions(+), 26 deletions(-) diff --git a/stochastic-rs-core/src/lib.rs b/stochastic-rs-core/src/lib.rs index 2920945..c4f9b96 100644 --- a/stochastic-rs-core/src/lib.rs +++ b/stochastic-rs-core/src/lib.rs @@ -49,7 +49,7 @@ pub trait Sampling: Send + Sync { panic!("m must be specified for parallel sampling"); } - let mut xs = Array2::zeros((self.m().unwrap(), self.n())); + let mut xs = Array2::zeros((self.m().unwrap(), self.n() + 1)); xs.axis_iter_mut(Axis(0)).into_par_iter().for_each(|mut x| { x.assign(&self.sample()); diff --git a/stochastic-rs-core/src/main.rs b/stochastic-rs-core/src/main.rs index 64b364f..547baf6 100644 --- a/stochastic-rs-core/src/main.rs +++ b/stochastic-rs-core/src/main.rs @@ -1,24 +1,31 @@ -use stochastic_rs::process::fbm::Fbm; +use stochastic_rs::{process::fbm::Fbm, Sampling}; fn main() { - // let fbm = Fbm::new(0.75, 10000, Some(1.0), None); - // let mut runs = Vec::new(); + let fbm = Fbm::new(&Fbm { + hurst: 0.9, + n: 10000, + t: None, + m: Some(100), + ..Default::default() + }); + println!("{:?}", fbm.fgn.hurst); + let mut runs = Vec::new(); - // for _ in 0..20 { - // let start = std::time::Instant::now(); - // for _ in 0..1000 { - // let _ = fbm.sample(); - // } + for _ in 0..20 { + let start = std::time::Instant::now(); + for _ in 0..1000 { + let _ = fbm.sample_par(); + } - // let duration = start.elapsed(); - // println!( - // "Time elapsed in expensive_function() is: {:?}", - // duration.as_secs_f32() - // ); - // runs.push(duration.as_secs_f32()); - // } + let duration = start.elapsed(); + println!( + "Time elapsed in expensive_function() is: {:?}", + duration.as_secs_f32() + ); + runs.push(duration.as_secs_f32()); + } - // let sum: f32 = runs.iter().sum(); - // let average = sum / runs.len() as f32; - // println!("Average time: {}", average); + let sum: f32 = runs.iter().sum(); + let average = sum / runs.len() as f32; + println!("Average time: {}", average); } diff --git a/stochastic-rs-core/src/noise/fgn.rs b/stochastic-rs-core/src/noise/fgn.rs index df52728..d89824a 100644 --- a/stochastic-rs-core/src/noise/fgn.rs +++ b/stochastic-rs-core/src/noise/fgn.rs @@ -21,7 +21,7 @@ pub struct Fgn { impl Default for Fgn { fn default() -> Self { - Self::new(0.5, 1024, None, None) + Self::new(0.7, 1024, None, None) } } @@ -112,7 +112,7 @@ mod tests { #[test] fn plot() { - let fgn = Fgn::new(0.7, 1024, Some(1.0), None); + let fgn = Fgn::new(0.7, 1024, Some(1.0), Some(1)); let mut plot = Plot::new(); let d = fgn.sample_par(); for data in d.axis_iter(Axis(0)) { diff --git a/stochastic-rs-core/src/process/fbm.rs b/stochastic-rs-core/src/process/fbm.rs index e2423b9..6ce0118 100644 --- a/stochastic-rs-core/src/process/fbm.rs +++ b/stochastic-rs-core/src/process/fbm.rs @@ -17,7 +17,7 @@ impl Fbm { panic!("Hurst parameter must be in (0, 1)") } - let fgn = Fgn::new(params.hurst, params.n, params.t, params.m); + let fgn = Fgn::new(params.hurst, params.n, params.t, None); Self { hurst: params.hurst, @@ -33,10 +33,10 @@ impl Sampling for Fbm { fn sample(&self) -> Array1 { let fgn = self.fgn.sample(); - let mut fbm = Array1::::zeros(self.n); + let mut fbm = Array1::::zeros(self.n + 1); fbm.slice_mut(s![1..]).assign(&fgn); - for i in 1..self.n { + for i in 1..(self.n + 1) { fbm[i] += fbm[i - 1]; } @@ -62,10 +62,10 @@ mod tests { #[test] fn plot() { let fbm = Fbm::new(&Fbm { - hurst: 0.5, + hurst: 0.9, n: 1000, t: Some(1.0), - m: None, + m: Some(1), ..Default::default() }); let mut plot = Plot::new(); From c78bafa6255bb231117d24946b40168fa645e358 Mon Sep 17 00:00:00 2001 From: Daniel Boros Date: Thu, 12 Sep 2024 21:16:52 +0200 Subject: [PATCH 2/4] feat: remove unused files --- stochastic-rs-quant/src/diffusions.rs | 4 - stochastic-rs-quant/src/diffusions/bm.rs | 27 ------- stochastic-rs-quant/src/diffusions/fbm.rs | 51 ------------- stochastic-rs-quant/src/diffusions/fou.rs | 62 ---------------- stochastic-rs-quant/src/diffusions/ou.rs | 25 ------- stochastic-rs-quant/src/lib.rs | 5 -- stochastic-rs-quant/src/noises.rs | 2 - stochastic-rs-quant/src/noises/fgn.rs | 89 ----------------------- stochastic-rs-quant/src/noises/gn.rs | 42 ----------- stochastic-rs-quant/src/traits.rs | 43 ----------- stochastic-rs-quant/src/traits_f.rs | 46 ------------ 11 files changed, 396 deletions(-) delete mode 100644 stochastic-rs-quant/src/diffusions.rs delete mode 100644 stochastic-rs-quant/src/diffusions/bm.rs delete mode 100644 stochastic-rs-quant/src/diffusions/fbm.rs delete mode 100644 stochastic-rs-quant/src/diffusions/fou.rs delete mode 100644 stochastic-rs-quant/src/diffusions/ou.rs delete mode 100644 stochastic-rs-quant/src/noises.rs delete mode 100644 stochastic-rs-quant/src/noises/fgn.rs delete mode 100644 stochastic-rs-quant/src/noises/gn.rs delete mode 100644 stochastic-rs-quant/src/traits.rs delete mode 100644 stochastic-rs-quant/src/traits_f.rs diff --git a/stochastic-rs-quant/src/diffusions.rs b/stochastic-rs-quant/src/diffusions.rs deleted file mode 100644 index 390ffb1..0000000 --- a/stochastic-rs-quant/src/diffusions.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod bm; -pub mod fbm; -pub mod fou; -pub mod ou; diff --git a/stochastic-rs-quant/src/diffusions/bm.rs b/stochastic-rs-quant/src/diffusions/bm.rs deleted file mode 100644 index 81f7752..0000000 --- a/stochastic-rs-quant/src/diffusions/bm.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::quant::traits::Process; - -pub struct BM { - mu: T, - sigma: T, -} - -impl BM { - #[must_use] - #[inline(always)] - pub fn new() -> Self { - Self { - mu: 0.0, - sigma: 1.0, - } - } -} - -impl Process for BM { - fn drift(&self, _x: f64, _t: f64) -> f64 { - self.mu - } - - fn diffusion(&self, _x: f64, _t: f64) -> f64 { - self.sigma - } -} diff --git a/stochastic-rs-quant/src/diffusions/fbm.rs b/stochastic-rs-quant/src/diffusions/fbm.rs deleted file mode 100644 index c8d84c6..0000000 --- a/stochastic-rs-quant/src/diffusions/fbm.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::quant::{noises::fgn::FGN, traits_f::FractionalProcess}; - -pub struct FBM { - mu: T, - sigma: T, - pub hurst: T, - n: usize, - x_0: T, - t_0: T, - t: T, - fgn: FGN, -} - -impl FBM { - #[must_use] - #[inline(always)] - pub fn new(hurst: f64, n: usize, x_0: f64, t_0: f64, t: f64) -> Self { - Self { - mu: 0.0, - sigma: 1.0, - hurst, - n, - x_0, - t_0, - t, - fgn: FGN::new(hurst, n - 1, t), - } - } -} - -impl FractionalProcess for FBM { - fn drift(&self, _x: f64, _t: f64) -> f64 { - self.mu - } - - fn diffusion(&self, _x: f64, _t: f64) -> f64 { - self.sigma - } - - fn hurst(&self) -> f64 { - self.hurst - } - - fn fgn(&self) -> FGN { - self.fgn.clone() - } - - fn params(&self) -> (usize, f64, f64, f64) { - (self.n, self.x_0, self.t_0, self.t) - } -} diff --git a/stochastic-rs-quant/src/diffusions/fou.rs b/stochastic-rs-quant/src/diffusions/fou.rs deleted file mode 100644 index 2d9b434..0000000 --- a/stochastic-rs-quant/src/diffusions/fou.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::quant::{noises::fgn::FGN, traits_f::FractionalProcess}; - -pub struct FOU { - pub theta: T, - pub mu: T, - pub sigma: T, - pub hurst: T, - n: usize, - x_0: T, - t_0: T, - t: T, - fgn: FGN, -} - -impl FOU { - #[must_use] - #[inline(always)] - pub fn new( - theta: f64, - mu: f64, - sigma: f64, - hurst: f64, - n: usize, - x_0: f64, - t_0: f64, - t: f64, - ) -> Self { - Self { - theta, - mu, - sigma, - hurst, - n, - x_0, - t_0, - t, - fgn: FGN::new(hurst, n, t), - } - } -} - -impl FractionalProcess for FOU { - fn drift(&self, x: f64, _t: f64) -> f64 { - self.theta * (self.mu - x) - } - - fn diffusion(&self, _x: f64, _t: f64) -> f64 { - self.sigma - } - - fn hurst(&self) -> f64 { - self.hurst - } - - fn fgn(&self) -> FGN { - self.fgn.clone() - } - - fn params(&self) -> (usize, f64, f64, f64) { - (self.n, self.x_0, self.t_0, self.t) - } -} diff --git a/stochastic-rs-quant/src/diffusions/ou.rs b/stochastic-rs-quant/src/diffusions/ou.rs deleted file mode 100644 index 7555d80..0000000 --- a/stochastic-rs-quant/src/diffusions/ou.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::quant::traits::Process; - -pub struct OU { - pub theta: T, - pub mu: T, - pub sigma: T, -} - -impl OU { - #[must_use] - #[inline(always)] - pub fn new(theta: f64, mu: f64, sigma: f64) -> Self { - Self { theta, mu, sigma } - } -} - -impl Process for OU { - fn drift(&self, x: f64, _t: f64) -> f64 { - self.theta * (self.mu - x) - } - - fn diffusion(&self, _x: f64, _t: f64) -> f64 { - self.sigma - } -} diff --git a/stochastic-rs-quant/src/lib.rs b/stochastic-rs-quant/src/lib.rs index 39a0a66..9fb759b 100644 --- a/stochastic-rs-quant/src/lib.rs +++ b/stochastic-rs-quant/src/lib.rs @@ -5,8 +5,3 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; #[cfg(feature = "jemalloc")] #[global_allocator] static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; - -pub mod diffusions; -pub mod noises; -pub mod traits; -pub mod traits_f; diff --git a/stochastic-rs-quant/src/noises.rs b/stochastic-rs-quant/src/noises.rs deleted file mode 100644 index 25a9976..0000000 --- a/stochastic-rs-quant/src/noises.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod fgn; -pub mod gn; diff --git a/stochastic-rs-quant/src/noises/fgn.rs b/stochastic-rs-quant/src/noises/fgn.rs deleted file mode 100644 index f3a06af..0000000 --- a/stochastic-rs-quant/src/noises/fgn.rs +++ /dev/null @@ -1,89 +0,0 @@ -use ndarray::{concatenate, prelude::*}; -use ndarray_rand::rand_distr::StandardNormal; -use ndarray_rand::RandomExt; -use ndrustfft::{ndfft_par, FftHandler}; -use num_complex::{Complex, ComplexDistribution}; -use rayon::prelude::*; - -use crate::quant::traits_f::SamplingF; - -#[derive(Clone)] -pub struct FGN { - hurst: T, - n: usize, - offset: usize, - t: T, - sqrt_eigenvalues: Array1>, - fft_handler: FftHandler, - fft_fgn: Array1>, -} - -impl FGN { - #[must_use] - #[inline(always)] - pub fn new(hurst: f64, n: usize, t: f64) -> Self { - if !(0.0..=1.0).contains(&hurst) { - panic!("Hurst parameter must be between 0 and 1"); - } - let n_ = n.next_power_of_two(); - let offset = n_ - n; - let n = n_; - let mut r = Array1::linspace(0.0, n as f64, n + 1); - r.par_mapv_inplace(|x| { - if x == 0.0 { - 1.0 - } else { - 0.5 - * ((x + 1.0).powf(2.0 * hurst) - 2.0 * x.powf(2.0 * hurst) + (x - 1.0).powf(2.0 * hurst)) - } - }); - let r = concatenate( - Axis(0), - #[allow(clippy::reversed_empty_ranges)] - &[r.view(), r.slice(s![..;-1]).slice(s![1..-1]).view()], - ) - .unwrap(); - let data = r.mapv(|v| Complex::new(v, 0.0)); - let r_fft = FftHandler::new(r.len()); - let mut sqrt_eigenvalues = Array1::>::zeros(r.len()); - ndfft_par(&data, &mut sqrt_eigenvalues, &r_fft, 0); - sqrt_eigenvalues.par_mapv_inplace(|x| Complex::new((x.re / (2.0 * n as f64)).sqrt(), x.im)); - - Self { - hurst, - n, - offset, - t, - sqrt_eigenvalues, - fft_handler: FftHandler::new(2 * n), - fft_fgn: Array1::>::zeros(2 * n), - } - } -} - -impl SamplingF for FGN { - fn sample(&self) -> Array1 { - let rnd = Array1::>::random( - 2 * self.n, - ComplexDistribution::new(StandardNormal, StandardNormal), - ); - let fgn = &self.sqrt_eigenvalues * &rnd; - let fft_handler = self.fft_handler.clone(); - let mut fgn_fft = self.fft_fgn.clone(); - ndfft_par(&fgn, &mut fgn_fft, &fft_handler, 0); - let fgn = fgn_fft - .slice(s![1..self.n - self.offset + 1]) - .mapv(|x: Complex| (x.re * (self.n as f64).powf(-self.hurst)) * self.t.powf(self.hurst)); - fgn - } - - fn sample_parallel(&self, m: usize) -> Array2 { - let mut xs = Array2::::zeros((m, self.n)); - - xs.axis_iter_mut(Axis(0)).into_par_iter().for_each(|mut x| { - x.assign(&self.sample()); - }); - - xs - } -} diff --git a/stochastic-rs-quant/src/noises/gn.rs b/stochastic-rs-quant/src/noises/gn.rs deleted file mode 100644 index c379ced..0000000 --- a/stochastic-rs-quant/src/noises/gn.rs +++ /dev/null @@ -1,42 +0,0 @@ -use ndarray::Array1; -use ndarray_rand::RandomExt; -use rand_distr::Normal; -use rayon::prelude::*; - -use crate::quant::traits::Sampling; - -pub struct GN; - -impl GN { - #[must_use] - #[inline(always)] - pub fn new() -> Self { - Self - } -} - -impl Sampling for GN { - fn sample(&self, n: usize, _x_0: f64, t_0: f64, t: f64) -> Array1 { - let sqrt_dt = ((t - t_0) / n as f64).sqrt(); - Array1::random(n, Normal::new(0.0, sqrt_dt).unwrap()) - } - - fn sample_parallel( - &self, - m: usize, - n: usize, - x_0: f64, - t_0: f64, - t: f64, - ) -> ndarray::Array2 { - let mut xs = ndarray::Array2::::zeros((m, n)); - - xs.axis_iter_mut(ndarray::Axis(0)) - .into_par_iter() - .for_each(|mut x| { - x.assign(&self.sample(n, x_0, t_0, t)); - }); - - xs - } -} diff --git a/stochastic-rs-quant/src/traits.rs b/stochastic-rs-quant/src/traits.rs deleted file mode 100644 index ebcd22a..0000000 --- a/stochastic-rs-quant/src/traits.rs +++ /dev/null @@ -1,43 +0,0 @@ -use ndarray::{Array1, Array2, Axis}; -use ndarray_rand::rand_distr::Normal; -use ndarray_rand::RandomExt; -use rayon::prelude::*; - -pub trait Process: Send + Sync { - fn drift(&self, x: T, t: T) -> T; - fn diffusion(&self, x: T, t: T) -> T; - fn jump() {} -} - -pub trait Sampling { - fn sample(&self, n: usize, x_0: T, t_0: T, t: T) -> Array1; - fn sample_parallel(&self, m: usize, n: usize, x_0: T, t_0: T, t: T) -> Array2; -} - -impl> Sampling for T { - fn sample(&self, n: usize, x_0: f64, t_0: f64, t: f64) -> Array1 { - let dt = (t - t_0) / n as f64; - let mut x = Array1::zeros(n); - x[0] = x_0; - let noise = Array1::random(n - 1, Normal::new(0.0, dt.sqrt()).unwrap()); - let times = Array1::linspace(t_0, t, n); - - // TODO: test idx - noise.into_iter().enumerate().for_each(|(idx, dw)| { - x[idx + 1] = - x[idx] + self.drift(x[idx], times[idx]) * dt + self.diffusion(x[idx], times[idx]) * dw; - }); - - x - } - - fn sample_parallel(&self, m: usize, n: usize, x_0: f64, t_0: f64, t: f64) -> Array2 { - let mut xs = Array2::::zeros((m, n)); - - xs.axis_iter_mut(Axis(0)).into_par_iter().for_each(|mut x| { - x.assign(&self.sample(n, x_0, t_0, t)); - }); - - xs - } -} diff --git a/stochastic-rs-quant/src/traits_f.rs b/stochastic-rs-quant/src/traits_f.rs deleted file mode 100644 index 2ae7388..0000000 --- a/stochastic-rs-quant/src/traits_f.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::quant::noises::fgn::FGN; -use ndarray::{Array1, Array2, Axis}; -use rayon::prelude::*; - -pub trait FractionalProcess: Send + Sync { - fn drift(&self, x: T, t: T) -> T; - fn diffusion(&self, x: T, t: T) -> T; - fn hurst(&self) -> T; - fn fgn(&self) -> FGN; - fn params(&self) -> (usize, T, T, T); -} - -pub trait SamplingF { - fn sample(&self) -> Array1; - fn sample_parallel(&self, m: usize) -> Array2; -} - -impl> SamplingF for T { - fn sample(&self) -> Array1 { - let (n, x_0, t_0, t) = self.params(); - let dt = (t - t_0) / n as f64; - let mut x = Array1::zeros(n); - x[0] = x_0; - let noise = self.fgn().sample(); - let times = Array1::linspace(t_0, t, n); - - // TODO: test idx - noise.into_iter().enumerate().for_each(|(idx, dw)| { - x[idx + 1] = - x[idx] + self.drift(x[idx], times[idx]) * dt + self.diffusion(x[idx], times[idx]) * dw; - }); - - x - } - - fn sample_parallel(&self, m: usize) -> Array2 { - let (n, ..) = self.params(); - let mut xs = Array2::::zeros((m, n)); - - xs.axis_iter_mut(Axis(0)).into_par_iter().for_each(|mut x| { - x.assign(&self.sample()); - }); - - xs - } -} From 3897d63f38bb05eb37685a8e60b2e2c4a2d369ab Mon Sep 17 00:00:00 2001 From: Daniel Boros Date: Thu, 12 Sep 2024 21:18:22 +0200 Subject: [PATCH 3/4] feat: init stochastis-rs-ml --- Cargo.toml | 7 ++++++- stochastic-rs-ml/Cargo.toml | 6 ++++++ stochastic-rs-ml/src/lib.rs | 14 ++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 stochastic-rs-ml/Cargo.toml create mode 100644 stochastic-rs-ml/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 4f7e6e5..4bd9c05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,8 @@ [workspace] -members = ["stochastic-rs-core", "stochastic-rs-quant", "stochastic-rs-stats"] +members = [ + "stochastic-rs-core", + "stochastic-rs-ml", + "stochastic-rs-quant", + "stochastic-rs-stats", +] resolver = "2" diff --git a/stochastic-rs-ml/Cargo.toml b/stochastic-rs-ml/Cargo.toml new file mode 100644 index 0000000..8ee56da --- /dev/null +++ b/stochastic-rs-ml/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "stochastic-rs-ml" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/stochastic-rs-ml/src/lib.rs b/stochastic-rs-ml/src/lib.rs new file mode 100644 index 0000000..78ba4fe --- /dev/null +++ b/stochastic-rs-ml/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From 58ae1086e9ed7537a28d3c2a11b1092bf6edd8bd Mon Sep 17 00:00:00 2001 From: Daniel Boros Date: Thu, 12 Sep 2024 22:14:26 +0200 Subject: [PATCH 4/4] feat: add rbergomi model --- stochastic-rs-core/src/volatility.rs | 33 +-------- stochastic-rs-core/src/volatility/rbergomi.rs | 71 +++++++++++++++++++ 2 files changed, 73 insertions(+), 31 deletions(-) create mode 100644 stochastic-rs-core/src/volatility/rbergomi.rs diff --git a/stochastic-rs-core/src/volatility.rs b/stochastic-rs-core/src/volatility.rs index 55131f3..09dfe61 100644 --- a/stochastic-rs-core/src/volatility.rs +++ b/stochastic-rs-core/src/volatility.rs @@ -1,33 +1,4 @@ -//! This module contains the implementations of various diffusion processes. -//! -//! The following diffusion processes are implemented: -//! -//! - Duffie-Kan Model -//! - The Duffie-Kan model is a multifactor interest rate model incorporating correlated Brownian motions. -//! - SDE: `dr(t) = (a1 * r(t) + b1 * x(t) + c1) * dt + sigma1 * (alpha * r(t) + beta * x(t) + gamma) * dW_r(t)` -//! - SDE: `dx(t) = (a2 * r(t) + b2 * x(t) + c2) * dt + sigma2 * (alpha * r(t) + beta * x(t) + gamma) * dW_x(t)` -//! - where Corr(W_r(t), W_x(t)) = rho -//! -//! - **Heston Model** -//! - A stochastic volatility model used to describe the evolution of the volatility of an underlying asset. -//! - SDE: `dS(t) = mu * S(t) * dt + S(t) * sqrt(V(t)) * dW_1(t)` -//! - SDE: `dV(t) = kappa * (theta - V(t)) * dt + eta * sqrt(V(t)) * dW_2(t)` -//! -//! - SABR (Stochastic Alpha, Beta, Rho) Model -//! - Widely used in financial mathematics for modeling stochastic volatility. -//! - SDE: `dF(t) = V(t) * F(t)^beta * dW_F(t)` -//! - SDE: `dV(t) = alpha * V(t) * dW_V(t)` -//! - where Corr(W_F(t), W_V(t)) = rho -//! -//! - **Vasicek Model** -//! - An Ornstein-Uhlenbeck process used to model interest rates. -//! - SDE: `dX(t) = theta * (mu - X(t)) * dt + sigma * dW(t)` -//! -//! - **Fractional Vasicek (fVasicek) Model** -//! - Incorporates fractional Brownian motion into the Vasicek model. -//! - SDE: `dX(t) = theta * (mu - X(t)) * dt + sigma * dW^H(t)` -//! -//! Each process has its own module and functions to generate sample paths. - +pub mod fheston; pub mod heston; +pub mod rbergomi; pub mod sabr; diff --git a/stochastic-rs-core/src/volatility/rbergomi.rs b/stochastic-rs-core/src/volatility/rbergomi.rs new file mode 100644 index 0000000..330533f --- /dev/null +++ b/stochastic-rs-core/src/volatility/rbergomi.rs @@ -0,0 +1,71 @@ +use ndarray::{s, Array1}; + +use crate::{noise::cgns::Cgns, Sampling2D}; + +#[derive(Default)] +pub struct RoughBergomi { + pub hurst: f64, + pub nu: f64, + pub sigma_0: f64, + pub r: f64, + pub rho: f64, + pub n: usize, + pub t: Option, + pub m: Option, + pub cgns: Cgns, +} + +impl RoughBergomi { + #[must_use] + pub fn new(params: &Self) -> Self { + let cgns = Cgns::new(&Cgns { + rho: params.rho, + n: params.n, + t: params.t, + m: params.m, + }); + + Self { + hurst: params.hurst, + nu: params.nu, + sigma_0: params.sigma_0, + r: params.r, + rho: params.rho, + n: params.n, + t: params.t, + m: params.m, + cgns, + } + } +} + +impl Sampling2D for RoughBergomi { + fn sample(&self) -> [Array1; 2] { + let dt = self.t.unwrap_or(1.0) / self.n as f64; + let [cgn1, z] = self.cgns.sample(); + + let mut s = Array1::::zeros(self.n + 1); + let mut sigma2 = Array1::::zeros(self.n + 1); + + for i in 1..(self.n + 1) { + s[i] = s[i - 1] + self.r * s[i - 1] + sigma2[i - 1] * cgn1[i - 1]; + + let sum_z = z.slice(s![..i]).sum(); + let t = i as f64 * dt; + sigma2[i] = self.sigma_0.powi(2) + * (self.nu * (2.0 * self.hurst).sqrt() * t.powf(self.hurst - 0.5) * sum_z + - 0.5 * self.nu.powi(2) * t.powf(2.0 * self.hurst)) + .exp(); + } + + [s, sigma2] + } + + fn n(&self) -> usize { + self.n + } + + fn m(&self) -> Option { + self.m + } +}