use super::bsm::Option;
extern crate docify;
use crate::tools::tool::jacbin_one_row;
use anyhow::{bail, Result};
use nalgebra::ComplexField;
use std::f64::consts::E;
use std::f64::INFINITY;
impl Option {
pub fn sabr_iv(&self, alpha: f64, beta: f64, rho: f64, vv: f64) -> f64 {
let iv = hagan(self.K, self.S, self.T, alpha, beta, rho, vv);
iv
}
}
pub fn hagan(K: f64, S: f64, T: f64, alpha: f64, beta: f64, rho: f64, vv: f64) -> f64 {
let eps = 1e-07;
let lnfk = (S / K).log(E);
let b1 = 1. - beta;
let fkb = (S * K).powf(1. - beta);
let n1 = b1.powf(2.) * alpha.powf(2.) / (24. * fkb);
let n2 = 0.25 * (rho * beta * alpha * vv) / (fkb.sqrt());
let n3 = (2. - 3. * rho.powf(2.)) * vv.powf(2.) / 24.;
let N = 1. + (n1 + n2 + n3) * T;
if K == S {
return alpha * N / fkb.sqrt();
}
let d1 = fkb.sqrt();
let d2 = b1.powf(2.) * lnfk.powf(2.) / 24.;
let d3 = b1.powf(4.) * lnfk.powf(4.) / 1920.;
let D = d1 * (1. + d2 + d3);
let z = (vv / alpha) * fkb.sqrt() * lnfk;
if z.abs() > eps {
let mut not_neg = 1. - 2. * rho * z + z.powf(2.);
if not_neg < 0. {
not_neg = 0.;
}
let _xz = (not_neg).sqrt() + z - rho;
let x_z = (_xz / (1. - rho)).log(E);
let IV = alpha * (N / D) * (z / x_z);
return IV;
}
let IV = alpha * (N / D) * z;
IV
}
pub struct SabrCali {
pub Ks: Vec<f64>,
pub ivs: Vec<f64>,
pub S: f64,
pub T: f64,
}
impl SabrCali {
fn cost_func(&self, arg: &Vec<f64>) -> f64 {
let mut cost = Vec::new();
let n = self.Ks.len();
for i in 0..n {
let K = self.Ks[i];
let iv = self.ivs[i];
let pred = hagan(K, self.S, self.T, arg[0], 0.5, arg[1], arg[2]);
let v = (iv - pred).powf(2.);
cost.push(v);
}
cost.iter().sum()
}
pub fn jacbin_one_row(&self, x: &Vec<f64>) -> Vec<f64> {
let dt = 1e-8;
let mut jac = Vec::new();
let y0 = self.cost_func(x);
for col in 0..x.len() {
let x1: Vec<f64> = x
.iter()
.enumerate()
.map(|(i, v)| if i != col { *v } else { *v + dt })
.collect();
let res = (self.cost_func(&x1) - self.cost_func(x)) / dt;
jac.push(res);
}
jac
}
fn fit(&self) -> Vec<f64> {
let mut arg = vec![0.01, 0.00, 0.10];
let precise = 1e-3;
let ncol = arg.len();
let bounds = vec![(0.0001, INFINITY), (-0.9999, 0.9999), (0.0001, INFINITY)];
let mut cost = INFINITY;
for i in 0..5000 {
let jac = self.jacbin_one_row(&arg);
let cost = self.cost_func(&arg);
for col in 0..ncol {
let limit = bounds[col];
arg[col] = arg[col] - precise * cost / jac[col];
if arg[col] < limit.0 {
arg[col] = limit.0;
}
if arg[col] > limit.1 {
arg[col] = limit.1;
}
}
dbg!(cost, &jac);
dbg!(&arg);
}
arg
}
}
#[test]
#[docify::export]
fn test_sabr() {
let sabr = SabrCali {
Ks: vec![1., 2., 3., 4., 5.],
ivs: vec![0.4, 0.21, 0.18, 0.24, 0.35],
S: 3.,
T: 1.,
};
sabr.fit();
}