pub use self::standard_stochastic_models::{
ArithmeticBrownianMotion, GeometricBrownianMotion, OrnsteinUhlenbeckProcess, BrownianBridge, FellerSquareRootProcess, FSRSimulationMethod,
};
pub use self::stochastic_volatility_models::{ConstantElasticityOfVariance, HestonStochasticVolatility, VarianceGammaProcess};
pub use self::jump_diffusion_models::{MertonJumpDiffusionProcess, KouJumpDiffusionProcess};
pub mod standard_stochastic_models;
pub mod stochastic_volatility_models;
pub mod jump_diffusion_models;
pub mod stochastic_process_generator;
use ndarray::Array1;
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};
#[cfg(feature = "plotly")]
use plotly::{Plot, Trace, Layout, Scatter, layout::Axis};
use crate::error::DigiFiError;
use crate::statistics::{skewness, kurtosis};
#[cfg(feature = "plotly")]
use crate::utilities::compare_len;
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct StochasticProcessResult {
pub paths: Vec<Array1<f64>>,
pub expectations_path: Option<Array1<f64>>,
pub variances_path: Option<Array1<f64>>,
pub mean: f64,
pub std: f64,
pub variance: f64,
pub skewness: f64,
pub kurtosis: f64,
}
pub trait StochasticProcess {
fn update_n_paths(&mut self, n_paths: usize) -> ();
fn get_n_steps(&self) -> usize;
fn get_t_f(&self) -> f64;
fn get_expectations(&self) -> Option<Array1<f64>>;
fn get_variances(&self) -> Option<Array1<f64>>;
fn get_paths(&self) -> Result<Vec<Array1<f64>>, DigiFiError>;
fn simulate(&self) -> Result<StochasticProcessResult, DigiFiError> {
let paths: Vec<Array1<f64>> = self.get_paths()?;
let final_steps: Vec<f64> = paths.iter().fold(Vec::with_capacity(paths.len()), |mut fs, path| {
fs.push(path[self.get_n_steps()]);
fs
} );
let final_steps: Array1<f64> = Array1::from_vec(final_steps);
let std: f64 = final_steps.std(0.0);
Ok(StochasticProcessResult {
paths,
expectations_path: self.get_expectations(),
variances_path: self.get_variances(),
mean: final_steps.mean().ok_or(DigiFiError::MeanCalculation { title: "Stochastic Process Simulation".to_owned(), series: "final_steps".to_owned() })?,
std,
variance: std.powi(2),
skewness: skewness(&final_steps)?,
kurtosis: kurtosis(&final_steps)?,
})
}
}
#[cfg(feature = "plotly")]
pub fn plot_stochastic_paths(paths: &Vec<Array1<f64>>, expected_path: Option<&Array1<f64>>) -> Result<Plot, DigiFiError> {
let t: Array1<f64> = Array1::range(0.0, paths[0].len() as f64, 1.0);
let mut traces: Vec<Box<dyn Trace>> = Vec::<Box<dyn Trace>>::new();
if let Some(p) = expected_path {
compare_len(&t.iter(), &p.iter(), "path", "expected_path")?;
traces.push(Scatter::new(t.to_vec(), p.to_vec()).name("Expected Path"));
}
for path in paths {
traces.push(Scatter::new(t.to_vec(), path.to_vec()));
}
if let Some(_) = expected_path { traces.rotate_left(1); }
let mut plot: Plot = Plot::new();
plot.add_traces(traces);
let x_axis: Axis = Axis::new().title("Time Step");
let y_axis: Axis = Axis::new().title("Stochastic Process Value");
let layout: Layout = Layout::new().title("<b>Stochastic Path Simulations</b>").x_axis(x_axis).y_axis(y_axis);
plot.set_layout(layout);
Ok(plot)
}
#[cfg(all(test, feature = "plotly"))]
mod tests {
use ndarray::Array1;
use plotly::Plot;
use crate::stochastic_processes::{StochasticProcess, plot_stochastic_paths};
#[test]
#[ignore]
fn unit_test_abm_plot() -> () {
use crate::stochastic_processes::standard_stochastic_models::ArithmeticBrownianMotion;
let abm: ArithmeticBrownianMotion = ArithmeticBrownianMotion::new(1.0, 0.4, 100, 200, 1.0, 100.0);
let paths: Vec<Array1<f64>> = abm.get_paths().unwrap();
let expected_path: Array1<f64> = abm.get_expectations().unwrap();
let plot: Plot = plot_stochastic_paths(&paths, Some(&expected_path)).unwrap();
plot.show()
}
#[test]
#[ignore]
fn unit_test_gbm_plot() -> () {
use crate::stochastic_processes::standard_stochastic_models::GeometricBrownianMotion;
let gbm: GeometricBrownianMotion = GeometricBrownianMotion::new(0.0, 0.2, 1_000, 200, 1.0, 100.0);
let paths: Vec<Array1<f64>> = gbm.get_paths().unwrap();
let expected_path: Array1<f64> = gbm.get_expectations().unwrap();
let plot: Plot = plot_stochastic_paths(&paths, Some(&expected_path)).unwrap();
plot.show()
}
#[test]
#[ignore]
fn unit_test_oup_plot() -> () {
use crate::stochastic_processes::standard_stochastic_models::OrnsteinUhlenbeckProcess;
let oup: OrnsteinUhlenbeckProcess = OrnsteinUhlenbeckProcess::new(
0.07, 0.1, 10.0, 100, 200, 1.0, 0.05, true
);
let paths: Vec<Array1<f64>> = oup.get_paths().unwrap();
let expected_path: Array1<f64> = oup.get_expectations().unwrap();
let plot: Plot = plot_stochastic_paths(&paths, Some(&expected_path)).unwrap();
plot.show()
}
#[test]
#[ignore]
fn unit_test_bb_plot() -> () {
use crate::stochastic_processes::standard_stochastic_models::BrownianBridge;
let bb: BrownianBridge = BrownianBridge::new(1.0, 2.0, 0.5, 100, 200, 1.0);
let paths: Vec<Array1<f64>> = bb.get_paths().unwrap();
let expected_path: Array1<f64> = bb.get_expectations().unwrap();
let plot: Plot = plot_stochastic_paths(&paths, Some(&expected_path)).unwrap();
plot.show()
}
#[test]
#[ignore]
fn unit_test_fsrp_plot() -> () {
use crate::stochastic_processes::standard_stochastic_models::{FellerSquareRootProcess, FSRSimulationMethod};
let fsrp: FellerSquareRootProcess = FellerSquareRootProcess::new(0.05, 0.265, 5.0, 100, 200,
1.0, 0.03, FSRSimulationMethod::EulerMaruyama);
let paths: Vec<Array1<f64>> = fsrp.get_paths().unwrap();
let expected_path: Array1<f64> = fsrp.get_expectations().unwrap();
let plot: Plot = plot_stochastic_paths(&paths, Some(&expected_path)).unwrap();
plot.show()
}
#[test]
#[ignore]
fn unit_test_cev_plot() -> () {
use crate::stochastic_processes::stochastic_volatility_models::ConstantElasticityOfVariance;
let cev: ConstantElasticityOfVariance = ConstantElasticityOfVariance::build(
1.0, 0.4, 0.5, 100, 200, 1.0, 100.0
).unwrap();
let paths: Vec<Array1<f64>> = cev.get_paths().unwrap();
let expected_path: Array1<f64> = cev.get_expectations().unwrap();
let plot: Plot = plot_stochastic_paths(&paths, Some(&expected_path)).unwrap();
plot.show()
}
#[test]
#[ignore]
fn unit_test_hsv_plot() -> () {
use crate::stochastic_processes::stochastic_volatility_models::HestonStochasticVolatility;
let hsv: HestonStochasticVolatility = HestonStochasticVolatility::new(
0.1, 5.0, 0.07, 0.2, 0.0, 100, 200, 1.0, 100.0, 0.03
);
let paths: Vec<Array1<f64>> = hsv.get_paths().unwrap();
let expected_path: Array1<f64> = hsv.get_expectations().unwrap();
let plot: Plot = plot_stochastic_paths(&paths, Some(&expected_path)).unwrap();
plot.show()
}
#[test]
#[ignore]
fn unit_test_mjd_plot() -> () {
use crate::stochastic_processes::jump_diffusion_models::MertonJumpDiffusionProcess;
let mjd: MertonJumpDiffusionProcess = MertonJumpDiffusionProcess::new(
0.03, 0.2, -0.03, 0.1, 1.5, 100, 200, 1.0, 100.0
);
let paths: Vec<Array1<f64>> = mjd.get_paths().unwrap();
let expected_path: Array1<f64> = mjd.get_expectations().unwrap();
let plot: Plot = plot_stochastic_paths(&paths, Some(&expected_path)).unwrap();
plot.show()
}
#[test]
#[ignore]
fn unit_test_kjd_plot() -> () {
use crate::stochastic_processes::jump_diffusion_models::KouJumpDiffusionProcess;
let kjd: KouJumpDiffusionProcess = KouJumpDiffusionProcess::build(
0.2, 0.3, 0.5, 9.0, 5.0, 0.5, 100, 200, 1.0, 100.0
).unwrap();
let paths: Vec<Array1<f64>> = kjd.get_paths().unwrap();
let expected_path: Array1<f64> = kjd.get_expectations().unwrap();
let plot: Plot = plot_stochastic_paths(&paths, Some(&expected_path)).unwrap();
plot.show()
}
#[test]
#[ignore]
fn unit_test_sde_abm_plot() -> () {
use crate::stochastic_processes::stochastic_process_generator::{SDE, sde_components::{SDEComponent, Noise, Jump}};
let t_f: f64 = 1.0;
let s_0: f64 = 100.0;
let drift_component: SDEComponent = SDEComponent::Linear { a: 10.0 };
let diffusion_component: SDEComponent = SDEComponent::Linear { a: 0.4 };
let noise: Noise = Noise::WeinerProcess;
let jump: Jump = Jump::NoJumps;
let sde: SDE = SDE::build(t_f, s_0, 200, 100, drift_component, diffusion_component, noise, jump).unwrap();
let paths: Vec<Array1<f64>> = sde.get_paths().unwrap();
let plot: Plot = plot_stochastic_paths(&paths, None).unwrap();
plot.show()
}
}