use crate::error::SimulationError;
use crate::simulation::steps::{Step, Ystep};
use crate::simulation::{WalkType, WalkTypeAble};
use positive::Positive;
use std::convert::TryInto;
use std::fmt::{Display, Formatter};
use std::ops::AddAssign;
#[derive(Debug, Clone)]
pub struct WalkParams<X, Y>
where
X: Copy + TryInto<Positive> + AddAssign + Display + Sized,
Y: TryInto<Positive> + Display + Sized + Clone,
{
pub size: usize,
pub init_step: Step<X, Y>,
pub walk_type: WalkType,
pub walker: Box<dyn WalkTypeAble<X, Y>>,
}
impl<X, Y> WalkParams<X, Y>
where
X: Copy + TryInto<Positive> + AddAssign + Display + Sized,
Y: TryInto<Positive> + Display + Sized + Clone,
{
pub fn y(&self) -> &Y {
self.init_step.y.value()
}
pub fn ystep_ref(&self) -> &Ystep<Y> {
&self.init_step.y
}
pub fn ystep(&self) -> Ystep<Y> {
self.init_step.y.clone()
}
pub fn ystep_as_positive(&self) -> Result<Positive, SimulationError> {
self.ystep_ref().positive()
}
}
impl<X, Y> Display for WalkParams<X, Y>
where
X: Copy + TryInto<Positive> + AddAssign + Display,
Y: TryInto<Positive> + Display + Clone,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"WalkParams {{ size: {}, init_step: {}, walk_type: {} }}",
self.size, self.init_step, self.walk_type
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ExpirationDate;
use crate::simulation::steps::{Xstep, Ystep};
use crate::utils::time::TimeFrame;
use positive::pos_or_panic;
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use std::fmt::Display;
use std::ops::AddAssign;
struct MockWalker;
impl<X, Y> WalkTypeAble<X, Y> for MockWalker
where
X: Copy + TryInto<Positive> + AddAssign + Display,
Y: Copy + TryInto<Positive> + Display,
{
}
#[test]
fn test_walk_params_creation_with_positive() {
let init_x = Xstep::new(
Positive::ONE,
TimeFrame::Day,
ExpirationDate::Days(pos_or_panic!(30.0)),
);
let init_y = Ystep::new(0, Positive::HUNDRED);
let init_step = Step {
x: init_x,
y: init_y,
};
let walk_params = WalkParams {
size: 100,
init_step,
walk_type: WalkType::Brownian {
dt: Positive::ONE,
drift: Decimal::ZERO,
volatility: pos_or_panic!(0.2),
},
walker: Box::new(MockWalker),
};
assert_eq!(walk_params.size, 100);
assert_eq!(
walk_params.init_step.x.step_size_in_time().value(),
Positive::ONE.value()
);
assert_eq!(
walk_params.init_step.y.value().value(),
Positive::HUNDRED.value()
);
}
#[test]
fn test_walk_params_clone_with_positive() {
let init_x = Xstep::new(
Positive::ONE,
TimeFrame::Day,
ExpirationDate::Days(pos_or_panic!(30.0)),
);
let init_y = Ystep::new(0, Positive::HUNDRED);
let init_step = Step {
x: init_x,
y: init_y,
};
let walk_params = WalkParams {
size: 100,
init_step,
walk_type: WalkType::Brownian {
dt: Positive::ONE,
drift: Decimal::ZERO,
volatility: pos_or_panic!(0.2),
},
walker: Box::new(MockWalker),
};
let cloned_params = &walk_params;
assert_eq!(cloned_params.size, walk_params.size);
assert_eq!(
cloned_params.init_step.x.step_size_in_time().value(),
walk_params.init_step.x.step_size_in_time().value()
);
assert_eq!(
cloned_params.init_step.y.value().value(),
walk_params.init_step.y.value().value()
);
}
#[test]
fn test_walk_params_display_with_positive() {
let init_x = Xstep::new(
pos_or_panic!(1.5),
TimeFrame::Day,
ExpirationDate::Days(pos_or_panic!(30.0)),
);
let init_y = Ystep::new(0, pos_or_panic!(200.0));
let init_step = Step {
x: init_x,
y: init_y,
};
let walk_params = WalkParams {
size: 50,
init_step: init_step.clone(),
walk_type: WalkType::GeometricBrownian {
dt: pos_or_panic!(1.0 / 252.0),
drift: dec!(0.0),
volatility: pos_or_panic!(0.2),
},
walker: Box::new(MockWalker),
};
let display_string = format!("{walk_params}");
assert!(display_string.contains("size: 50"));
assert!(display_string.contains(&format!("{init_step}")));
assert!(display_string.contains("walk_type: GeometricBrownian"));
}
#[test]
fn test_with_large_size_positive_types() {
let init_x = Xstep::new(
Positive::ONE,
TimeFrame::Day,
ExpirationDate::Days(pos_or_panic!(30.0)),
);
let init_y = Ystep::new(0, Positive::HUNDRED);
let init_step = Step {
x: init_x,
y: init_y,
};
let size = 1_000_000; let walk_params = WalkParams {
size,
init_step,
walk_type: WalkType::Brownian {
dt: pos_or_panic!(1.0 / 252.0),
drift: dec!(0.0),
volatility: pos_or_panic!(0.2),
},
walker: Box::new(MockWalker),
};
assert_eq!(walk_params.size, size);
let display_string = format!("{walk_params}");
assert!(display_string.contains(&format!("size: {size}")));
}
#[test]
fn test_with_different_positive_values() {
let init_x = Xstep::new(
pos_or_panic!(0.001), TimeFrame::Month,
ExpirationDate::Days(pos_or_panic!(90.0)),
);
let init_y = Ystep::new(0, pos_or_panic!(1000000.0)); let init_step = Step {
x: init_x,
y: init_y,
};
let walk_params = WalkParams {
size: 50,
init_step,
walk_type: WalkType::Brownian {
dt: Positive::ONE,
drift: Decimal::ZERO,
volatility: pos_or_panic!(0.2),
},
walker: Box::new(MockWalker),
};
assert_eq!(walk_params.size, 50);
assert_eq!(
walk_params.init_step.x.step_size_in_time().value(),
pos_or_panic!(0.001).value()
);
assert_eq!(
walk_params.init_step.y.value().value(),
pos_or_panic!(1000000.0).value()
);
assert_eq!(*walk_params.init_step.x.time_unit(), TimeFrame::Month);
}
}