use num_complex::Complex64;
use scilib::math::basic::gamma;
use crate::pricing::fourier::Cumulants;
use crate::pricing::fourier::FourierModelExt;
#[derive(Debug, Clone)]
pub struct CgmysvParams {
pub alpha: f64,
pub lambda_plus: f64,
pub lambda_minus: f64,
pub kappa: f64,
pub eta: f64,
pub zeta: f64,
pub rho: f64,
pub v0: f64,
}
#[derive(Debug, Clone)]
pub struct CgmysvModel {
pub params: CgmysvParams,
pub r: f64,
pub q: f64,
}
impl CgmysvParams {
pub fn norm_const(&self) -> f64 {
let g2a = gamma(2.0 - self.alpha);
1.0
/ (g2a * (self.lambda_plus.powf(self.alpha - 2.0) + self.lambda_minus.powf(self.alpha - 2.0)))
}
pub fn psi_std_cgmy(&self, u: Complex64) -> Complex64 {
let a = self.alpha;
let lp = self.lambda_plus;
let lm = self.lambda_minus;
let i = Complex64::i();
let denom_base = lp.powf(a - 2.0) + lm.powf(a - 2.0);
let drift = (lp.powf(a - 1.0) - lm.powf(a - 1.0)) / ((a - 1.0) * denom_base) * i * u;
let jumps = ((Complex64::from(lp) - i * u).powf(a) - lp.powf(a)
+ (Complex64::from(lm) + i * u).powf(a)
- lm.powf(a))
/ (a * (a - 1.0) * denom_base);
drift + jumps
}
pub fn cir_joint_chf(&self, t: f64, a: Complex64, b: Complex64, x: f64) -> Complex64 {
let kappa = self.kappa;
let eta = self.eta;
let zeta = self.zeta;
let zeta2 = zeta * zeta;
let i = Complex64::i();
let gamma = (Complex64::from(kappa * kappa) - 2.0 * zeta2 * i * a).sqrt();
let half_gt = gamma * t / 2.0;
let ch = half_gt.cosh();
let sh = half_gt.sinh();
let base = ch + (Complex64::from(kappa) - i * b * zeta2) / gamma * sh;
let exp_power = 2.0 * kappa * eta / zeta2;
let a_val = (kappa * kappa * eta * t / zeta2).exp() * base.powf(-exp_power);
let b_num = i * b * (gamma * ch - Complex64::from(kappa) * sh) + 2.0 * i * a * sh;
let b_denom = gamma * ch + (Complex64::from(kappa) - i * b * zeta2) * sh;
let b_val = b_num / b_denom;
a_val * (b_val * x).exp()
}
pub fn chf_l(&self, t: f64, u: Complex64) -> Complex64 {
let psi = self.psi_std_cgmy(u);
let a = -Complex64::i() * psi;
let b = Complex64::from(self.rho) * u;
self.cir_joint_chf(t, a, b, self.v0)
}
pub fn omega(&self, t: f64) -> f64 {
self.chf_l(t, -Complex64::i()).ln().re
}
}
impl FourierModelExt for CgmysvModel {
fn chf(&self, t: f64, xi: Complex64) -> Complex64 {
let omega = self.params.omega(t);
let i = Complex64::i();
(i * xi * ((self.r - self.q) * t - omega)).exp() * self.params.chf_l(t, xi)
}
fn cumulants(&self, t: f64) -> Cumulants {
let h = 1e-4;
let f = |u: f64| self.chf(t, Complex64::new(u, 0.0)).ln();
let f0 = f(0.0);
let fp = f(h);
let fm = f(-h);
let f2p = f(2.0 * h);
let f2m = f(-2.0 * h);
Cumulants {
c1: (fp - fm).im / (2.0 * h),
c2: -(fp - 2.0 * f0 + fm).re / (h * h),
c4: (f2p - 4.0 * fp + 6.0 * f0 - 4.0 * fm + f2m).re / h.powi(4),
}
}
}