use time::Date;
use crate::{
math::distributions::{Distribution, Gaussian},
time::{today, DayCountConvention},
};
#[allow(clippy::module_name_repetitions)]
#[derive(derive_builder::Builder, Debug)]
pub struct ForwardStartOption {
pub initial_price: f64,
pub alpha: f64,
pub risk_free_rate: f64,
pub volatility: f64,
pub dividend_rate: f64,
#[builder(default = "None")]
pub valuation_date: Option<Date>,
pub start: Date,
pub end: Date,
}
impl ForwardStartOption {
#[must_use]
pub fn price(&self) -> (f64, f64) {
let S = self.initial_price;
let a = self.alpha;
let r = self.risk_free_rate;
let v = self.volatility;
let q = self.dividend_rate;
let T = DayCountConvention::default()
.day_count_factor(self.valuation_date.unwrap_or(today()), self.end);
let t = DayCountConvention::default()
.day_count_factor(self.valuation_date.unwrap_or(today()), self.start);
let b = r - q;
let d1 = ((1. / a).ln() + (b + v * v / 2.) * (T - t)) / (v * (T - t).sqrt());
let d2 = d1 - v * (T - t).sqrt();
let norm = Gaussian::default();
let Nd1: f64 = norm.cdf(d1);
let Nd2: f64 = norm.cdf(d2);
let Nd1_: f64 = norm.cdf(-d1);
let Nd2_: f64 = norm.cdf(-d2);
let c: f64 = S
* ((b - r) * t).exp()
* (((b - r) * (T - t)).exp() * Nd1 - a * (-r * (T - t)).exp() * Nd2);
let p: f64 = S
* ((b - r) * t).exp()
* (-((b - r) * (T - t)).exp() * Nd1_ + a * (-r * (T - t)).exp() * Nd2_);
(c, p)
}
}
#[cfg(test)]
mod tests_forward_start {
use super::*;
use crate::assert_approx_equal;
#[test]
fn TEST_forward_start_option() {
let start = today() + time::Duration::days(91);
let end = today() + time::Duration::days(365);
let ForwardStart = ForwardStartOption {
initial_price: 60.0,
alpha: 1.1,
risk_free_rate: 0.08,
volatility: 0.3,
dividend_rate: 0.04,
valuation_date: None,
start,
end,
};
let prices = ForwardStart.price();
assert_approx_equal!(prices.0, 4.402888269001168, 1e-2);
}
}