use super::TypeFlag;
use crate::instruments::BlackScholesMerton;
use crate::time::{today, DayCountConvention};
use time::Date;
#[allow(clippy::module_name_repetitions)]
#[derive(derive_builder::Builder, Debug)]
pub struct Merton1976 {
pub underlying_price: f64,
pub strike_price: f64,
pub risk_free_rate: f64,
pub volatility: f64,
pub lambda: f64,
pub gamma: f64,
pub type_flag: TypeFlag,
#[builder(default = "None")]
pub evaluation_date: Option<Date>,
pub expiration_date: Date,
}
impl Merton1976 {
#[must_use]
pub fn price(&self) -> f64 {
let mut bsm = BlackScholesMerton::new(
self.risk_free_rate,
self.underlying_price,
self.strike_price,
self.volatility,
self.risk_free_rate,
self.evaluation_date,
self.expiration_date,
self.type_flag,
);
let tau = DayCountConvention::default().day_count_factor(
self.evaluation_date.unwrap_or(today()),
self.expiration_date,
);
let mut price = 0_f64;
for i in 0..20 {
bsm.volatility = Self::sigma(self, i, tau);
let factorial: usize = (1..=i).product();
let numerator = f64::exp(-self.lambda * tau) * f64::powi(self.lambda * tau, i as i32);
price += bsm.price() * numerator / factorial as f64;
}
price
}
fn sigma(&self, i: usize, tau: f64) -> f64 {
f64::sqrt(f64::powi(Self::z(self), 2) + f64::powi(Self::delta(self), 2) * i as f64 / tau)
}
fn delta(&self) -> f64 {
f64::sqrt(f64::powi(self.volatility, 2) * self.gamma / self.lambda)
}
fn z(&self) -> f64 {
f64::sqrt(f64::powi(self.volatility, 2) - self.lambda * f64::powi(Self::delta(self), 2))
}
}
#[cfg(test)]
mod tests_merton_1976 {
use crate::{assert_approx_equal, instruments::TypeFlag};
use super::*;
#[test]
fn test_merton_1976() {
let merton76 = Merton1976 {
underlying_price: 100.,
strike_price: 80.,
risk_free_rate: 0.08,
volatility: 0.25,
lambda: 1.,
gamma: 0.25,
type_flag: TypeFlag::Call,
evaluation_date: None,
expiration_date: today() + time::Duration::days(36),
};
assert_approx_equal!(merton76.price(), 20.67, 0.1);
}
}