RustQuant_stochastics/arithmetic_brownian_motion.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// RustQuant: A Rust library for quantitative finance tools.
// Copyright (C) 2023 https://github.com/avhz
// Dual licensed under Apache 2.0 and MIT.
// See:
// - LICENSE-APACHE.md
// - LICENSE-MIT.md
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// use crate::models::arithmetic_brownian_motion::ArithmeticBrownianMotion;
use crate::process::StochasticProcess;
use crate::ModelParameter;
/// Struct containing the Arithmetic Brownian Motion parameters.
pub struct ArithmeticBrownianMotion {
/// The drift ($\mu$) in percentage.
pub mu: ModelParameter,
/// The volatility ($\sigma$) in percentage.
pub sigma: ModelParameter,
}
impl ArithmeticBrownianMotion {
/// Create a new Arithmetic Brownian Motion process.
pub fn new(mu: impl Into<ModelParameter>, sigma: impl Into<ModelParameter>) -> Self {
Self {
mu: mu.into(),
sigma: sigma.into(),
}
}
}
impl StochasticProcess for ArithmeticBrownianMotion {
fn drift(&self, _x: f64, t: f64) -> f64 {
// mu dt
self.mu.0(t)
}
fn diffusion(&self, _x: f64, t: f64) -> f64 {
assert!(self.sigma.0(t) >= 0.0);
// sigma dW_t
self.sigma.0(t)
}
fn jump(&self, _x: f64, _t: f64) -> Option<f64> {
None
}
fn parameters(&self) -> Vec<f64> {
vec![self.mu.0(0.0), self.sigma.0(0.0)]
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// TESTS
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#[cfg(test)]
mod tests_abm {
use super::*;
use crate::StochasticProcessConfig;
use RustQuant_math::*;
use RustQuant_utils::assert_approx_equal;
#[test]
fn test_arithmetic_brownian_motion() {
let abm = ArithmeticBrownianMotion::new(0.05, 0.9);
let config = StochasticProcessConfig::new(10.0, 0.0, 0.5, 125, 1000, false);
let output = abm.euler_maruyama(&config);
// let file1 = "./images/ABM1.png";
// plot_vector((&output.trajectories[0]).clone(), file1).unwrap();
// let file2 = "./images/ABM2.png";
// plot_vector((&output.trajectories[1]).clone(), file2)
// Test the distribution of the final values.
let X_T: Vec<f64> = output
.paths
.iter()
.filter_map(|v| v.last().copied())
.collect();
let E_XT = X_T.mean();
let V_XT = X_T.variance();
// E[X_T] = X_0 + mu * T
assert_approx_equal!(E_XT, 10.0 + 0.05 * 0.5, 0.1);
// V[X_T] = sigma^2 * T
assert_approx_equal!(V_XT, 0.9 * 0.9 * 0.5, 0.1);
}
}