use ndarray::Array1;
use super::scenario::CurveShift;
use crate::curves::DiscountCurve;
use crate::traits::FloatExt;
#[derive(Debug, Clone)]
pub struct Sensitivities<T: FloatExt> {
pub bucket_dv01: Array1<T>,
pub parallel_dv01: T,
pub base_pv: T,
}
pub fn central_difference<T: FloatExt, F>(f: F, x0: T, h: T) -> T
where
F: Fn(T) -> T,
{
let two = T::from_f64_fast(2.0);
(f(x0 + h) - f(x0 - h)) / (two * h)
}
pub fn forward_difference<T: FloatExt, F>(f: F, x0: T, h: T) -> T
where
F: Fn(T) -> T,
{
(f(x0 + h) - f(x0)) / h
}
pub fn second_difference<T: FloatExt, F>(f: F, x0: T, h: T) -> T
where
F: Fn(T) -> T,
{
(f(x0 + h) - T::from_f64_fast(2.0) * f(x0) + f(x0 - h)) / (h * h)
}
pub fn finite_difference_greek<T: FloatExt, F>(f: F, x0: T, bump_size: T) -> T
where
F: Fn(T) -> T,
{
central_difference(f, x0, bump_size)
}
pub fn bucket_dv01<T: FloatExt, F>(
curve: &DiscountCurve<T>,
bump_size: T,
mut valuer: F,
) -> Sensitivities<T>
where
F: FnMut(&DiscountCurve<T>) -> T,
{
let base_pv = valuer(curve);
let n = curve.points().len();
let mut bucket = Array1::zeros(n);
let half = T::from_f64_fast(0.5);
for i in 0..n {
let pillar = curve.points()[i].time;
let up = CurveShift::KeyRate {
pillar,
amount: bump_size,
}
.apply(curve);
let down = CurveShift::KeyRate {
pillar,
amount: -bump_size,
}
.apply(curve);
let pv_up = valuer(&up);
let pv_down = valuer(&down);
bucket[i] = half * (pv_up - pv_down);
}
let parallel = bucket.iter().fold(T::zero(), |acc, &v| acc + v);
Sensitivities {
bucket_dv01: bucket,
parallel_dv01: parallel,
base_pv,
}
}