use crate::math::distributions::{gaussian::Gaussian, Distribution};
#[derive(Debug, Clone, Copy)]
pub struct GapOption {
pub initial_price: f64,
pub strike_1: f64,
pub strike_2: f64,
pub risk_free_rate: f64,
pub volatility: f64,
pub cost_of_carry: f64,
pub time_to_maturity: f64,
}
#[derive(Debug, Clone, Copy)]
pub struct CashOrNothingOption {
pub initial_price: f64,
pub strike_price: f64,
pub payout_value: f64,
pub risk_free_rate: f64,
pub volatility: f64,
pub cost_of_carry: f64,
pub time_to_maturity: f64,
}
impl GapOption {
#[must_use]
pub fn price(&self) -> (f64, f64) {
let S = self.initial_price;
let K_1 = self.strike_1;
let K_2 = self.strike_2;
let T = self.time_to_maturity;
let r = self.risk_free_rate;
let v = self.volatility;
let b = self.cost_of_carry;
let d1 = ((S / K_1).ln() + (b + 0.5 * v * v) * T) / (v * (T).sqrt());
let d2 = d1 - v * (T).sqrt();
let N = Gaussian::default();
let c = S * ((b - r) * T).exp() * N.cdf(d1) - K_2 * (-r * T).exp() * N.cdf(d2);
let p = -S * ((b - r) * T).exp() * N.cdf(-d1) + K_2 * (-r * T).exp() * N.cdf(-d2);
(c, p)
}
}
impl CashOrNothingOption {
#[must_use]
pub fn price(&self) -> (f64, f64) {
let S = self.initial_price;
let X = self.strike_price;
let K = self.payout_value;
let T = self.time_to_maturity;
let r = self.risk_free_rate;
let v = self.volatility;
let b = self.cost_of_carry;
let d = ((S / X).ln() + (b - 0.5 * v * v) * T) / (v * (T).sqrt());
let N = Gaussian::default();
let c = K * (-r * T).exp() * N.cdf(d);
let p = K * (-r * T).exp() * N.cdf(-d);
(c, p)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::assert_approx_equal;
use crate::RUSTQUANT_EPSILON;
#[test]
fn test_gap_option() {
let gap = GapOption {
initial_price: 50.0,
strike_1: 50.0,
strike_2: 57.0,
risk_free_rate: 0.09,
volatility: 0.2,
time_to_maturity: 0.5,
cost_of_carry: 0.09,
};
let prices = gap.price();
assert_approx_equal!(prices.0, -0.005_252_489_258_779_747, RUSTQUANT_EPSILON);
}
#[test]
fn test_cash_or_nothing_option() {
let CON = CashOrNothingOption {
initial_price: 100.0,
payout_value: 10.0,
strike_price: 80.0,
risk_free_rate: 0.06,
volatility: 0.35,
time_to_maturity: 0.75,
cost_of_carry: 0.0,
};
let prices = CON.price();
assert_approx_equal!(prices.1, 2.671_045_684_461_347, RUSTQUANT_EPSILON);
}
}