RustQuant_stochastics/extended_vasicek.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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 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::model_parameter::ModelParameter;
use crate::process::StochasticProcess;
/// Struct containing the extended Vasicek process parameters.
pub struct ExtendedVasicek {
/// Mean function ($\mu(t)$)
pub alpha: ModelParameter,
/// Non-negative diffusion, or instantaneous time-varying volatility ($\sigma$).
pub sigma: ModelParameter,
/// Mean reversion function ($\theta(t)$)
pub theta: ModelParameter,
}
impl ExtendedVasicek {
/// Create a new Hull-White process.
pub fn new(
alpha: impl Into<ModelParameter>,
sigma: impl Into<ModelParameter>,
theta: impl Into<ModelParameter>,
) -> Self {
Self {
alpha: alpha.into(),
sigma: sigma.into(),
theta: theta.into(),
}
}
}
impl StochasticProcess for ExtendedVasicek {
fn drift(&self, x: f64, t: f64) -> f64 {
self.theta.0(t) - (self.alpha.0(t) * x)
}
fn diffusion(&self, _x: f64, t: f64) -> f64 {
self.sigma.0(t)
}
fn jump(&self, _x: f64, _t: f64) -> Option<f64> {
None
}
fn parameters(&self) -> Vec<f64> {
vec![self.alpha.0(0.0), self.sigma.0(0.0), self.theta.0(0.0)]
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// TESTS
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#[cfg(test)]
mod tests_extended_vasicek {
use super::*;
use crate::StochasticProcessConfig;
use RustQuant_math::*;
use RustQuant_utils::assert_approx_equal;
// fn alpha_t(_t: f64) -> f64 {
// 2.0
// }
// fn theta_t(_t: f64) -> f64 {
// 0.5
// }
#[test]
fn test_extended_vasicek() {
let sigma = 2.0;
let alpha = 2.0;
let theta = 0.5;
let ev = ExtendedVasicek::new(alpha, sigma, theta);
let config = StochasticProcessConfig::new(10.0, 0.0, 1.0, 150, 1000, false);
let output = ev.euler_maruyama(&config);
// 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();
// Note these tests are identical to the Hull-White
// E[X_T] = X_0*exp(-alpha_t)(t) T) X_0 + (theta/alpha_t)(t))(1- exp(-alpha_t)(t) * T))
// Expectation with constant reduces to Hull-White
assert_approx_equal!(
E_XT,
(-alpha * 1.0_f64).exp() * 10.0 + (theta / alpha) * (1.0 - alpha * 1.0_f64).exp(),
0.25
);
}
}