use super::super::heston::MultiHestonParams;
use super::payoff::MtPayoff;
use crate::traits::FloatExt;
#[derive(Debug, Clone)]
pub struct MtGreeks<T: FloatExt> {
pub params: MultiHestonParams<T>,
pub h: T,
pub n_paths: usize,
}
impl<T: FloatExt + ndarray_linalg::Lapack> MtGreeks<T> {
pub fn new(params: MultiHestonParams<T>, h: T, n_paths: usize) -> Self {
Self::try_new(params, h, n_paths).expect("invalid Malliavin–Thalmaier engine parameters")
}
pub fn try_new(params: MultiHestonParams<T>, h: T, n_paths: usize) -> anyhow::Result<Self> {
params.validate()?;
if !num_traits::Float::is_finite(h) || h <= T::zero() {
anyhow::bail!("h must be finite and positive");
}
if n_paths == 0 {
anyhow::bail!("n_paths must be positive");
}
Ok(Self { params, h, n_paths })
}
pub fn price(&self, payoff: &MtPayoff<T>) -> T {
self.price_from_sampler(payoff, || self.params.sample())
}
pub fn price_with_seed(&self, payoff: &MtPayoff<T>, seed: u64) -> T {
let mut seed_state = seed;
self.price_from_sampler(payoff, || {
self
.params
.sample_with_seed(crate::simd_rng::derive_seed(&mut seed_state))
})
}
fn price_from_sampler<F>(&self, payoff: &MtPayoff<T>, mut sample: F) -> T
where
F: FnMut() -> super::super::heston::MultiHestonPaths<T>,
{
let disc = <T as num_traits::Float>::exp(-self.params.r * self.params.tau);
let mut sum = T::zero();
for _ in 0..self.n_paths {
let terminal = sample().terminal_prices_array();
sum += disc
* payoff.evaluate(
terminal
.as_slice()
.expect("terminal-price array must be contiguous"),
);
}
sum / T::from_usize_(self.n_paths)
}
}