use stochastic_rs_distributions::special::norm_cdf;
use crate::traits::PricerExt;
use crate::traits::TimeExt;
#[derive(Debug, Clone)]
pub struct KirkSpreadPricer {
pub f1: f64,
pub f2: f64,
pub x: f64,
pub r: f64,
pub v1: f64,
pub v2: f64,
pub corr: f64,
pub tau: Option<f64>,
pub eval: Option<chrono::NaiveDate>,
pub expiration: Option<chrono::NaiveDate>,
}
impl KirkSpreadPricer {
pub fn new(
f1: f64,
f2: f64,
x: f64,
r: f64,
v1: f64,
v2: f64,
corr: f64,
tau: Option<f64>,
eval: Option<chrono::NaiveDate>,
expiration: Option<chrono::NaiveDate>,
) -> Self {
Self {
f1,
f2,
x,
r,
v1,
v2,
corr,
tau,
eval,
expiration,
}
}
pub fn builder(f1: f64, f2: f64, x: f64, r: f64) -> KirkSpreadPricerBuilder {
KirkSpreadPricerBuilder {
f1,
f2,
x,
r,
v1: 0.0,
v2: 0.0,
corr: 0.0,
tau: None,
eval: None,
expiration: None,
}
}
}
#[derive(Debug, Clone)]
pub struct KirkSpreadPricerBuilder {
f1: f64,
f2: f64,
x: f64,
r: f64,
v1: f64,
v2: f64,
corr: f64,
tau: Option<f64>,
eval: Option<chrono::NaiveDate>,
expiration: Option<chrono::NaiveDate>,
}
impl KirkSpreadPricerBuilder {
pub fn v1(mut self, v1: f64) -> Self {
self.v1 = v1;
self
}
pub fn v2(mut self, v2: f64) -> Self {
self.v2 = v2;
self
}
pub fn corr(mut self, corr: f64) -> Self {
self.corr = corr;
self
}
pub fn tau(mut self, tau: f64) -> Self {
self.tau = Some(tau);
self
}
pub fn eval(mut self, eval: chrono::NaiveDate) -> Self {
self.eval = Some(eval);
self
}
pub fn expiration(mut self, expiration: chrono::NaiveDate) -> Self {
self.expiration = Some(expiration);
self
}
pub fn build(self) -> KirkSpreadPricer {
KirkSpreadPricer {
f1: self.f1,
f2: self.f2,
x: self.x,
r: self.r,
v1: self.v1,
v2: self.v2,
corr: self.corr,
tau: self.tau,
eval: self.eval,
expiration: self.expiration,
}
}
}
impl PricerExt for KirkSpreadPricer {
fn calculate_call_put(&self) -> (f64, f64) {
let tau = self.tau_or_from_dates();
let denom = self.f2 + self.x;
let f = self.f1 / denom;
let f_temp = self.f2 / denom;
let v = (self.v1.powi(2) + (self.v2 * f_temp).powi(2)
- 2.0 * self.corr * self.v1 * self.v2 * f_temp)
.sqrt();
let d1 = (f.ln() + 0.5 * v.powi(2) * tau) / (v * tau.sqrt());
let d2 = d1 - v * tau.sqrt();
let df = (-self.r * tau).exp();
let call = denom * (f * df * norm_cdf(d1) - df * norm_cdf(d2));
let put = denom * (df * norm_cdf(-d2) - f * df * norm_cdf(-d1));
(call, put)
}
fn calculate_price(&self) -> f64 {
self.calculate_call_put().0
}
}
impl TimeExt for KirkSpreadPricer {
fn tau(&self) -> Option<f64> {
self.tau
}
fn eval(&self) -> Option<chrono::NaiveDate> {
self.eval
}
fn expiration(&self) -> Option<chrono::NaiveDate> {
self.expiration
}
}