diff --git a/Cargo.toml b/Cargo.toml index 4050104..84dbf87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stochastic-rs" -version = "0.6.1" +version = "0.6.2" edition = "2021" license = "MIT" description = "A Rust library for stochastic processes" @@ -16,14 +16,14 @@ ndarray = { version = "0.15.6", features = [ "rayon", "matrixmultiply-threading", ] } -num-complex = { version = "0.4.4", features = ["rand"] } +num-complex = { version = "0.4.6", features = ["rand"] } rand = "0.8.5" rand_distr = "0.4.3" -rayon = "1.8.0" +rayon = "1.10.0" indicatif = "0.17.7" plotly = "0.8.4" ndarray-rand = "0.14.0" -ndrustfft = "0.4.2" +ndrustfft = "0.4.5" [dev-dependencies] diff --git a/README.md b/README.md index 38ec635..6daed8b 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Documentation is available at [stochastic-rs](https://docs.rs/stochastic-rs/). # Stochastic processes - [x] Gaussian noise +- [x] Correlated Gaussian noise - [x] Brownian motion - [x] Correlated Brownian motion - [x] Geometric Brownian motion @@ -43,6 +44,7 @@ Documentation is available at [stochastic-rs](https://docs.rs/stochastic-rs/). # Fractional Stochastic processes - [x] Fractional Gaussian noise +- [x] Correlated Gaussian noise - [x] Fractional Brownian motion - [x] Correlated Fractional Brownian motion - [x] Fractional Geometric Brownian motion diff --git a/src/jumps/bates.rs b/src/jumps/bates.rs index 84a8020..d8f2372 100644 --- a/src/jumps/bates.rs +++ b/src/jumps/bates.rs @@ -1,23 +1,28 @@ use ndarray::Array1; use rand_distr::Distribution; -use crate::prelude::{ - cbms::{correlated_bms, CorrelatedBms}, - cpoisson::{compound_poisson, CompoundPoisson}, +use crate::{ + noises::cgns::{cgns, Cgns}, + prelude::cpoisson::{compound_poisson, CompoundPoisson}, }; /// Generates paths for the Bates (1996) model. /// -/// The Bates model combines a stochastic volatility model with jump diffusion, commonly used in financial mathematics to model asset prices. +/// The Bates model combines a stochastic volatility model with jump diffusion, +/// commonly used in financial mathematics to model asset prices. /// /// # Parameters /// /// - `mu`: Drift parameter of the asset price. -/// - `kappa`: Rate of mean reversion of the volatility. -/// - `theta`: Long-term mean level of the volatility. -/// - `eta`: Volatility of the volatility (vol of vol). -/// - `rho`: Correlation between the asset price and its volatility. +/// - `b`: The continuously compounded domestic/foreign interest rate differential. +/// - `r`: The continuously compounded risk-free interest rate. +/// - `r_f`: The continuously compounded foreign interest rate. /// - `lambda`: Jump intensity. +/// - `k`: Mean jump size. +/// - `alpha`: Rate of mean reversion of the volatility. +/// - `beta`: Long-term mean level of the volatility. +/// - `sigma`: Volatility of the volatility (vol of vol). +/// - `rho`: Correlation between the asset price and its volatility. /// - `n`: Number of time steps. /// - `s0`: Initial value of the asset price (optional, defaults to 0.0). /// - `v0`: Initial value of the volatility (optional, defaults to 0.0). @@ -31,7 +36,23 @@ use crate::prelude::{ /// # Example /// /// ``` -/// let paths = bates_1996(0.05, 1.5, 0.04, 0.3, -0.7, 0.1, 1000, Some(100.0), Some(0.04), Some(1.0), Some(false)); +/// let params = Bates1996 { +/// mu: 0.05, +/// lambda: 0.1, +/// k: 0.2, +/// alpha: 1.5, +/// beta: 0.04, +/// sigma: 0.3, +/// rho: -0.7, +/// n: 1000, +/// s0: Some(100.0), +/// v0: Some(0.04), +/// t: Some(1.0), +/// use_sym: Some(false), +/// }; +/// +/// let jump_distr = Normal::new(0.0, 1.0); // Example jump distribution +/// let paths = bates_1996(¶ms, jump_distr); /// let asset_prices = paths[0]; /// let volatilities = paths[1]; /// ``` @@ -40,14 +61,18 @@ use crate::prelude::{ /// /// This function will panic if the `correlated_bms` or `compound_poisson` functions return invalid lengths or if there are issues with array indexing. -#[derive(Default)] +#[derive(Default, Debug)] pub struct Bates1996 { - pub mu: f64, - pub kappa: f64, - pub theta: f64, - pub eta: f64, - pub rho: f64, + pub mu: Option, + pub b: Option, + pub r: Option, + pub r_f: Option, pub lambda: f64, + pub k: f64, + pub alpha: f64, + pub beta: f64, + pub sigma: f64, + pub rho: f64, pub n: usize, pub s0: Option, pub v0: Option, @@ -61,11 +86,15 @@ pub fn bates_1996( ) -> [Array1; 2] { let Bates1996 { mu, - kappa, - theta, - eta, - rho, + b, + r, + r_f, lambda, + k, + alpha, + beta, + sigma, + rho, n, s0, v0, @@ -73,7 +102,7 @@ pub fn bates_1996( use_sym, } = *params; - let correlated_bms = correlated_bms(&CorrelatedBms { rho, n, t }); + let [cgn1, cgn2] = cgns(&Cgns { rho, n, t }); let dt = t.unwrap_or(1.0) / n as f64; let mut s = Array1::::zeros(n); @@ -82,6 +111,12 @@ pub fn bates_1996( s[0] = s0.unwrap_or(0.0); v[0] = v0.unwrap_or(0.0); + let drift = match (mu, b, r, r_f) { + (Some(r), Some(r_f), ..) => r - r_f, + (Some(b), ..) => b, + _ => mu.unwrap(), + }; + for i in 1..n { let [.., jumps] = compound_poisson( &CompoundPoisson { @@ -92,17 +127,18 @@ pub fn bates_1996( jump_distr, ); + let sqrt_v = use_sym + .unwrap_or(false) + .then(|| v[i - 1].abs()) + .unwrap_or(v[i - 1].max(0.0)) + .sqrt(); + s[i] = s[i - 1] - + mu * s[i - 1] * dt - + s[i - 1] * v[i - 1].sqrt() * correlated_bms[0][i - 1] + + (drift - lambda * k) * s[i - 1] * dt + + s[i - 1] * sqrt_v * cgn1[i - 1] + jumps.sum(); - let random: f64 = match use_sym.unwrap_or(false) { - true => eta * (v[i]).abs().sqrt() * correlated_bms[1][i - 1], - false => eta * (v[i]).max(0.0).sqrt() * correlated_bms[1][i - 1], - }; - - v[i] = v[i - 1] + kappa * (theta - v[i - 1]) * dt + random; + v[i] = v[i - 1] + (alpha - beta * v[i - 1]) * dt + sigma * v[i - 1] * cgn2[i - 1]; } [s, v] diff --git a/src/main.rs b/src/main.rs index 594979e..c503c3d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,30 +1,45 @@ use std::time::Instant; use plotly::{common::Line, Plot, Scatter}; -use rand_distr::{Gamma, Normal}; -use stochastic_rs::{ - jumps::{bates::bates_1996, jump_fou::jump_fou, levy_diffusion::levy_diffusion, merton::merton}, - processes::{cpoisson::compound_poisson, fbm::Fbm, poisson::poisson}, - utils::Generator, -}; +use rand_distr::Normal; +use stochastic_rs::jumps::bates::{bates_1996, Bates1996}; fn main() { let start = Instant::now(); let mut plot = Plot::new(); - let fbm = Fbm::new(0.9, 1000, Some(1.0), Some(10)); - for i in 0..1 { - // let d = poisson(10.0, Some(50), None); - // let trace = Scatter::new((0..d.len()).collect::>(), d.to_vec()) - // .mode(plotly::common::Mode::Lines) - // .line( - // Line::new() - // .color("orange") - // .shape(plotly::common::LineShape::Hv), - // ) - // .name("Poisson"); - //plot.add_trace(trace); + let jump_distr = Normal::new(0.0, 0.07).unwrap(); + let params = Bates1996 { + mu: Some(0.05), + b: Some(0.05), + r: None, + r_f: None, + lambda: 0.01, + k: 0., + alpha: 0.0225, + beta: 4.0, + sigma: 0.1, + rho: 0.1, + n: 100, + s0: Some(40.), + v0: Some(0.025), + t: Some(25.0), + use_sym: Some(false), + }; + println!("{:?}", params); + for _ in 0..10 { + let [s, _v] = bates_1996(¶ms, jump_distr); + + let trace = Scatter::new((0..s.len()).collect::>(), s.to_vec()) + .mode(plotly::common::Mode::Lines) + .line( + Line::new() + .color("orange") + .shape(plotly::common::LineShape::Hv), + ) + .name("Bates"); + plot.add_trace(trace); } plot.show(); diff --git a/src/models/duffie_kan.rs b/src/models/duffie_kan.rs index 4d9b4e0..9b27050 100644 --- a/src/models/duffie_kan.rs +++ b/src/models/duffie_kan.rs @@ -1,6 +1,6 @@ use ndarray::Array1; -use crate::processes::cbms::{correlated_bms, CorrelatedBms}; +use crate::noises::cgns::{cgns, Cgns}; /// Generates paths for the Duffie-Kan multifactor interest rate model. /// @@ -76,7 +76,7 @@ pub fn duffie_kan(params: &DuffieKan) -> [Array1; 2] { t, } = *params; - let correlated_bms = correlated_bms(&CorrelatedBms { rho, n, t }); + let [cgn1, cgn2] = cgns(&Cgns { rho, n, t }); let dt = t.unwrap_or(1.0) / n as f64; let mut r = Array1::::zeros(n); @@ -88,10 +88,10 @@ pub fn duffie_kan(params: &DuffieKan) -> [Array1; 2] { for i in 1..n { r[i] = r[i - 1] + (a1 * r[i - 1] + b1 * x[i - 1] + c1) * dt - + sigma1 * (alpha * r[i - 1] + beta * x[i - 1] + gamma) * correlated_bms[0][i - 1]; + + sigma1 * (alpha * r[i - 1] + beta * x[i - 1] + gamma) * cgn1[i - 1]; x[i] = x[i - 1] + (a2 * r[i - 1] + b2 * x[i - 1] + c2) * dt - + sigma2 * (alpha * r[i - 1] + beta * x[i - 1] + gamma) * correlated_bms[1][i - 1]; + + sigma2 * (alpha * r[i - 1] + beta * x[i - 1] + gamma) * cgn2[i - 1]; } [r, x] diff --git a/src/models/heston.rs b/src/models/heston.rs index b3dd21b..12f6423 100644 --- a/src/models/heston.rs +++ b/src/models/heston.rs @@ -1,6 +1,6 @@ use ndarray::Array1; -use crate::prelude::cbms::{correlated_bms, CorrelatedBms}; +use crate::noises::cgns::{cgns, Cgns}; /// Generates paths for the Heston model. /// @@ -59,7 +59,7 @@ pub fn heston(params: &Heston) -> [Array1; 2] { use_sym, } = *params; - let correlated_bms = correlated_bms(&CorrelatedBms { rho, n, t }); + let [cgn1, cgn2] = cgns(&Cgns { rho, n, t }); let dt = t.unwrap_or(1.0) / n as f64; let mut s = Array1::::zeros(n); @@ -69,11 +69,11 @@ pub fn heston(params: &Heston) -> [Array1; 2] { v[0] = v0.unwrap_or(0.0); for i in 1..n { - s[i] = s[i - 1] + mu * s[i - 1] * dt + s[i - 1] * v[i - 1].sqrt() * correlated_bms[0][i - 1]; + s[i] = s[i - 1] + mu * s[i - 1] * dt + s[i - 1] * v[i - 1].sqrt() * cgn1[i - 1]; let random: f64 = match use_sym.unwrap_or(false) { - true => eta * (v[i - 1]).abs().sqrt() * correlated_bms[1][i - 1], - false => eta * (v[i - 1]).max(0.0).sqrt() * correlated_bms[1][i - 1], + true => eta * (v[i - 1]).abs().sqrt() * cgn2[i - 1], + false => eta * (v[i - 1]).max(0.0).sqrt() * cgn2[i - 1], }; v[i] = v[i - 1] + kappa * (theta - v[i - 1]) * dt + random; } diff --git a/src/models/sabr.rs b/src/models/sabr.rs index d344c23..4f53551 100644 --- a/src/models/sabr.rs +++ b/src/models/sabr.rs @@ -1,6 +1,6 @@ use ndarray::Array1; -use crate::prelude::cbms::{correlated_bms, CorrelatedBms}; +use crate::noises::cgns::{cgns, Cgns}; /// Generates a path of the SABR (Stochastic Alpha, Beta, Rho) model. /// @@ -53,7 +53,7 @@ pub fn sabr(params: &Sabr) -> [Array1; 2] { assert!(-1.0 < rho && rho < 1.0, "Rho parameter must be in (-1, 1)"); assert!(alpha > 0.0, "Alpha parameter must be positive"); - let correlated_bms = correlated_bms(&CorrelatedBms { rho, n, t }); + let [cgn1, cgn2] = cgns(&Cgns { rho, n, t }); let mut f = Array1::::zeros(n); let mut v = Array1::::zeros(n); @@ -62,8 +62,8 @@ pub fn sabr(params: &Sabr) -> [Array1; 2] { v[0] = v0.unwrap_or(0.0); for i in 0..n { - f[i] = f[i - 1] + v[i - 1] * f[i - 1].powf(beta) * correlated_bms[0][i - 1]; - v[i] = v[i - 1] + alpha * v[i - 1] * correlated_bms[1][i - 1]; + f[i] = f[i - 1] + v[i - 1] * f[i - 1].powf(beta) * cgn1[i - 1]; + v[i] = v[i - 1] + alpha * v[i - 1] * cgn2[i - 1]; } [f, v] diff --git a/src/noises/cfgns.rs b/src/noises/cfgns.rs new file mode 100644 index 0000000..e7daadf --- /dev/null +++ b/src/noises/cfgns.rs @@ -0,0 +1,67 @@ +use ndarray::{Array1, Array2}; + +use crate::{noises::fgn::FgnFft, utils::Generator}; + +/// Generates two correlated fractional Gaussian noise (fGn) paths. +/// +/// # Parameters +/// +/// - `hurst`: Hurst parameter for the fGn, must be in (0, 1). +/// - `rho`: Correlation coefficient between the two fGns, must be in [-1, 1]. +/// - `n`: Number of time steps. +/// - `t`: Total time (optional, defaults to 1.0). +/// +/// # Returns +/// +/// A `[Array1; 2]` where each vector represents a generated fGn path. +/// +/// # Panics +/// +/// Panics if `hurst` is not in the range (0, 1). +/// Panics if `rho` is not in the range [-1, 1]. +/// +/// # Example +/// +/// ``` +/// let params = Cfgns { +/// hurst: 0.7, +/// rho: 0.5, +/// n: 1000, +/// t: Some(1.0), +/// }; +/// let correlated_fg_paths = cfgns(¶ms); +/// let fgn1 = correlated_fg_paths[0].clone(); +/// let fgn2 = correlated_fg_paths[1].clone(); +/// ``` + +#[derive(Default)] +pub struct Cfgns { + pub hurst: f64, + pub rho: f64, + pub n: usize, + pub t: Option, +} + +pub fn cfgns(params: &Cfgns) -> [Array1; 2] { + let Cfgns { hurst, rho, n, t } = *params; + assert!( + !(0.0..=1.0).contains(&hurst), + "Hurst parameter must be in (0, 1)" + ); + assert!( + !(-1.0..=1.0).contains(&rho), + "Correlation coefficient must be in [-1, 1]" + ); + + let mut cfgns = Array2::::zeros((2, n)); + let fgn = FgnFft::new(hurst, n - 1, t, None); + let fgn1 = fgn.sample(); + let fgn2 = fgn.sample(); + + for i in 1..n { + cfgns[[0, i]] = fgn1[i - 1]; + cfgns[[1, i]] = rho * fgn1[i - 1] + (1.0 - rho.powi(2)).sqrt() * fgn2[i - 1]; + } + + [cfgns.row(0).into_owned(), cfgns.row(1).into_owned()] +} diff --git a/src/noises/cgns.rs b/src/noises/cgns.rs new file mode 100644 index 0000000..ed1c034 --- /dev/null +++ b/src/noises/cgns.rs @@ -0,0 +1,58 @@ +use ndarray::{Array1, Array2}; + +use crate::noises::gn; + +/// Generates two correlated Gaussian noise (GN) paths. +/// +/// # Parameters +/// +/// - `rho`: Correlation coefficient between the two GNs, must be in [-1, 1]. +/// - `n`: Number of time steps. +/// - `t`: Total time (optional, defaults to 1.0). +/// +/// # Returns +/// +/// A `[Array1; 2]` where each vector represents a generated GN path. +/// +/// # Panics +/// +/// Panics if `rho` is not in the range [-1, 1]. +/// +/// # Example +/// +/// ``` +/// let params = Cgns { +/// rho: 0.5, +/// n: 1000, +/// t: Some(1.0), +/// }; +/// let correlated_gn_paths = cgns(¶ms); +/// let gn1 = correlated_gn_paths[0].clone(); +/// let gn2 = correlated_gn_paths[1].clone(); +/// ``` + +#[derive(Default)] +pub struct Cgns { + pub rho: f64, + pub n: usize, + pub t: Option, +} + +pub fn cgns(params: &Cgns) -> [Array1; 2] { + let Cgns { rho, n, t } = *params; + assert!( + (-1.0..=1.0).contains(&rho), + "Correlation coefficient must be in [-1, 1]" + ); + + let mut cgns = Array2::::zeros((2, n)); + let gn1 = gn::gn(n, Some(t.unwrap_or(1.0))); + let gn2 = gn::gn(n, Some(t.unwrap_or(1.0))); + + for i in 1..n { + cgns[[0, i]] = gn1[i - 1]; + cgns[[1, i]] = rho * gn1[i - 1] + (1.0 - rho.powi(2)).sqrt() * gn2[i - 1]; + } + + [cgns.row(0).into_owned(), cgns.row(1).into_owned()] +} diff --git a/src/noises/gn.rs b/src/noises/gn.rs index 4bd6971..65c705e 100644 --- a/src/noises/gn.rs +++ b/src/noises/gn.rs @@ -24,16 +24,3 @@ pub fn gn(n: usize, t: Option) -> Array1 { let sqrt_dt = (t.unwrap_or(1.0) / n as f64).sqrt(); Array1::random(n, Normal::new(0.0, sqrt_dt).unwrap()) } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_gn() { - let n = 10; - let t = 1.0; - let gn = gn(n, Some(t)); - assert_eq!(gn.len(), n); - } -} diff --git a/src/noises/mod.rs b/src/noises/mod.rs index d6cf615..5422ed3 100644 --- a/src/noises/mod.rs +++ b/src/noises/mod.rs @@ -8,5 +8,7 @@ //! - **Gaussian Noise (GN)** //! - Generates Gaussian noise, commonly used in simulations requiring white noise or random perturbations. +pub mod cfgns; +pub mod cgns; pub mod fgn; pub mod gn; diff --git a/src/processes/cbms.rs b/src/processes/cbms.rs index 051a212..de4b37a 100644 --- a/src/processes/cbms.rs +++ b/src/processes/cbms.rs @@ -1,4 +1,4 @@ -use crate::noises::gn; +use crate::noises::cgns::{cgns, Cgns}; use ndarray::{Array1, Array2}; /// Generates two correlated Brownian motion (BM) paths. @@ -20,34 +20,44 @@ use ndarray::{Array1, Array2}; /// # Example /// /// ``` -/// let correlated_paths = correlated_bms(0.5, 1000, Some(1.0)); -/// let bm1 = correlated_paths[0]; -/// let bm2 = correlated_paths[1]; +/// let params = Cbms { +/// rho: 0.5, +/// n: 1000, +/// t: Some(1.0), +/// }; +/// let correlated_paths = cbms(¶ms); +/// let bm1 = correlated_paths[0].clone(); +/// let bm2 = correlated_paths[1].clone(); /// ``` +/// +/// # Details +/// +/// This function generates two correlated Brownian motion paths using the provided correlation coefficient `rho` +/// and the number of time steps `n`. The total time `t` is optional and defaults to 1.0 if not provided. The function +/// ensures that `rho` is within the valid range of [-1, 1] and panics otherwise. The generated paths are stored in a +/// 2D array and returned as a tuple of two 1D arrays. #[derive(Default)] -pub struct CorrelatedBms { +pub struct Cbms { pub rho: f64, pub n: usize, pub t: Option, } -pub fn correlated_bms(params: &CorrelatedBms) -> [Array1; 2] { - let CorrelatedBms { rho, n, t } = *params; +pub fn cbms(params: &Cbms) -> [Array1; 2] { + let Cbms { rho, n, t } = *params; assert!( - rho >= -1.0 && rho <= 1.0, + !(-1.0..=1.0).contains(&rho), "Correlation coefficient must be in [-1, 1]" ); - let mut bms = Array2::::zeros((n, 2)); - - let gn1 = gn::gn(n, Some(t.unwrap_or(1.0))); - let gn2 = gn::gn(n, Some(t.unwrap_or(1.0))); + let mut bms = Array2::::zeros((2, n)); + let [cgn1, cgn2] = cgns(&Cgns { rho, n, t }); for i in 1..n { - bms[[i, 0]] = bms[[i - 1, 0]] + gn1[i - 1]; - bms[[i, 1]] = rho * gn1[i - 1] + (1.0 - rho.powi(2)).sqrt() * gn2[i - 1]; + bms[[0, i]] = bms[[0, i - 1]] + cgn1[i - 1]; + bms[[1, i]] = bms[[1, i - 1]] + rho * cgn1[i - 1] + (1.0 - rho.powi(2)).sqrt() * cgn2[i - 1]; } - [bms.column(0).into_owned(), bms.column(1).into_owned()] + [bms.row(0).into_owned(), bms.row(1).into_owned()] } diff --git a/src/processes/cfbms.rs b/src/processes/cfbms.rs index 4735afd..d613671 100644 --- a/src/processes/cfbms.rs +++ b/src/processes/cfbms.rs @@ -30,16 +30,16 @@ use crate::{noises::fgn::FgnFft, utils::Generator}; /// ``` #[derive(Default)] -pub struct CorrelatedFbms { +pub struct Cfbms { pub hurst1: f64, - pub hurst2: f64, + pub hurst2: Option, pub rho: f64, pub n: usize, pub t: Option, } -pub fn correlated_fbms(params: &CorrelatedFbms) -> [Array1; 2] { - let CorrelatedFbms { +pub fn correlated_fbms(params: &Cfbms) -> [Array1; 2] { + let Cfbms { hurst1, hurst2, rho, @@ -48,27 +48,30 @@ pub fn correlated_fbms(params: &CorrelatedFbms) -> [Array1; 2] { } = *params; assert!( - hurst1 > 0.0 && hurst1 < 1.0, + !(0.0..=1.0).contains(&hurst1), "Hurst parameter for the first fBM must be in (0, 1)" ); + + if let Some(hurst2) = hurst2 { + assert!( + !(0.0..=1.0).contains(&hurst2), + "Hurst parameter for the second fBM must be in (0, 1)" + ); + } assert!( - hurst2 > 0.0 && hurst2 < 1.0, - "Hurst parameter for the second fBM must be in (0, 1)" - ); - assert!( - rho >= -1.0 && rho <= 1.0, + !(-1.0..=1.0).contains(&rho), "Correlation coefficient must be in [-1, 1]" ); let mut fbms = Array2::::zeros((n, 2)); let fgn1 = FgnFft::new(hurst1, n - 1, t, None).sample(); - let fgn2 = FgnFft::new(hurst2, n - 1, t, None).sample(); + let fgn2 = FgnFft::new(hurst2.unwrap_or(hurst1), n - 1, t, None).sample(); for i in 1..n { - fbms[[i, 0]] = fbms[[i - 1, 0]] + fgn1[i - 1]; - fbms[[i, 1]] = rho * fgn2[i - 1] + (1.0 - rho.powi(2)).sqrt() * fgn2[i - 1]; + fbms[[0, i]] = fbms[[0, i - 1]] + fgn1[i - 1]; + fbms[[1, i]] = fbms[[1, i - 1]] + rho * fgn2[i - 1] + (1.0 - rho.powi(2)).sqrt() * fgn2[i - 1]; } - [fbms.column(0).to_owned(), fbms.column(1).to_owned()] + [fbms.row(0).to_owned(), fbms.row(1).to_owned()] } diff --git a/src/processes/cpoisson.rs b/src/processes/cpoisson.rs index 20f7993..6cd5585 100644 --- a/src/processes/cpoisson.rs +++ b/src/processes/cpoisson.rs @@ -48,7 +48,6 @@ pub fn compound_poisson( let p = poisson(&Poisson { lambda, n, t_max }); let mut jumps = Array1::::zeros(n.unwrap_or(p.len())); - for i in 1..p.len() { jumps[i] = distribution.sample(&mut thread_rng()); } diff --git a/src/processes/fbm.rs b/src/processes/fbm.rs index 655ac38..b470d24 100644 --- a/src/processes/fbm.rs +++ b/src/processes/fbm.rs @@ -39,7 +39,7 @@ impl Fbm { /// ``` /// pub fn new(hurst: f64, n: usize, t: Option, m: Option) -> Self { - if !(0.0..1.0).contains(&hurst) { + if !(0.0..=1.0).contains(&hurst) { panic!("Hurst parameter must be in (0, 1)") } diff --git a/src/processes/poisson.rs b/src/processes/poisson.rs index e4a21ea..0647a57 100644 --- a/src/processes/poisson.rs +++ b/src/processes/poisson.rs @@ -39,7 +39,6 @@ pub fn poisson(params: &Poisson) -> Array1 { if let Some(n) = n { let exponentials = Array1::random(n - 1, Exp::new(1.0 / lambda).unwrap()); let mut poisson = Array1::::zeros(n); - for i in 1..n { poisson[i] = poisson[i - 1] + exponentials[i - 1]; }