use std::cell::RefCell;
use std::rc::Rc;
use levenberg_marquardt::LevenbergMarquardt;
use nalgebra::DVector;
use ndarray::Array1;
use stochastic_rs_stats::heston_nml_cekf::HestonNMLECEKFConfig;
use super::params::HestonJacobianMethod;
use super::params::HestonMleSeedMethod;
use super::params::HestonParams;
use super::result::HestonCalibrationResult;
use crate::CalibrationLossScore;
use crate::LossMetric;
use crate::OptionType;
use crate::calibration::CalibrationHistory;
#[derive(Clone)]
pub struct HestonCalibrator {
pub params: Option<HestonParams>,
pub c_market: DVector<f64>,
pub s: DVector<f64>,
pub k: DVector<f64>,
pub r: f64,
pub q: Option<f64>,
pub flat_t: Vec<f64>,
pub option_type: OptionType,
pub mle_s: Option<Array1<f64>>,
pub mle_v: Option<Array1<f64>>,
pub mle_r: Option<f64>,
pub mle_seed_method: HestonMleSeedMethod,
pub mle_delta: Option<f64>,
pub nmle_cekf_config: Option<HestonNMLECEKFConfig>,
pub record_history: bool,
pub loss_metrics: &'static [LossMetric],
pub jacobian_method: HestonJacobianMethod,
pub(super) calibration_history: Rc<RefCell<Vec<CalibrationHistory<HestonParams>>>>,
}
impl HestonCalibrator {
pub fn new(
params: Option<HestonParams>,
c_market: DVector<f64>,
s: DVector<f64>,
k: DVector<f64>,
r: f64,
q: Option<f64>,
tau: f64,
option_type: OptionType,
mle_s: Option<Array1<f64>>,
mle_v: Option<Array1<f64>>,
mle_r: Option<f64>,
record_history: bool,
) -> Self {
let n = c_market.len();
assert_eq!(n, s.len(), "c_market and s must have the same length");
assert_eq!(n, k.len(), "c_market and k must have the same length");
assert!(
tau.is_finite() && tau > 0.0,
"tau must be a finite positive value"
);
Self {
params,
c_market,
s,
k,
r,
q,
flat_t: vec![tau; n],
option_type,
mle_s,
mle_v,
mle_r,
mle_seed_method: HestonMleSeedMethod::default(),
mle_delta: None,
nmle_cekf_config: None,
record_history,
loss_metrics: &LossMetric::ALL,
jacobian_method: HestonJacobianMethod::default(),
calibration_history: Rc::new(RefCell::new(Vec::new())),
}
}
pub fn from_slices(
params: Option<HestonParams>,
slices: &[super::super::levy::MarketSlice],
s: f64,
r: f64,
q: Option<f64>,
option_type: OptionType,
record_history: bool,
) -> Self {
let mut flat_prices = Vec::new();
let mut flat_strikes = Vec::new();
let mut flat_t = Vec::new();
let mut flat_s = Vec::new();
for slice in slices {
for i in 0..slice.strikes.len() {
flat_prices.push(slice.prices[i]);
flat_strikes.push(slice.strikes[i]);
flat_t.push(slice.t);
flat_s.push(s);
}
}
Self {
params,
c_market: DVector::from_vec(flat_prices),
s: DVector::from_vec(flat_s),
k: DVector::from_vec(flat_strikes),
r,
q,
flat_t,
option_type,
mle_s: None,
mle_v: None,
mle_r: None,
mle_seed_method: HestonMleSeedMethod::default(),
mle_delta: None,
nmle_cekf_config: None,
record_history,
loss_metrics: &LossMetric::ALL,
jacobian_method: HestonJacobianMethod::default(),
calibration_history: Rc::new(RefCell::new(Vec::new())),
}
}
}
impl HestonCalibrator {
pub(super) fn solve(&self) -> HestonCalibrationResult {
let mut problem = self.clone();
problem.ensure_initial_guess();
let (result, report) = LevenbergMarquardt::new().minimize(problem);
let converged = report.termination.was_successful();
let params = result.effective_params();
let c_model = result.compute_model_prices_for_numeric(¶ms);
let loss = CalibrationLossScore::compute_selected(
result.c_market.as_slice(),
c_model.as_slice(),
result.loss_metrics,
);
HestonCalibrationResult {
params,
loss,
converged,
}
}
pub fn set_initial_guess(&mut self, params: HestonParams) {
self.params = Some(params.projected());
}
pub fn set_record_history(&mut self, record: bool) {
self.record_history = record;
}
pub fn set_jacobian_method(&mut self, method: HestonJacobianMethod) {
self.jacobian_method = method;
}
pub fn set_mle_seed_method(&mut self, method: HestonMleSeedMethod) {
self.mle_seed_method = method;
}
pub fn set_mle_delta(&mut self, delta: Option<f64>) {
self.mle_delta = delta;
}
pub fn set_nmle_cekf_config(&mut self, cfg: HestonNMLECEKFConfig) {
self.nmle_cekf_config = Some(cfg);
}
pub fn history(&self) -> Vec<CalibrationHistory<HestonParams>> {
self.calibration_history.borrow().clone()
}
}
impl crate::traits::Calibrator for HestonCalibrator {
type InitialGuess = HestonParams;
type Params = HestonParams;
type Output = HestonCalibrationResult;
type Error = anyhow::Error;
fn calibrate(&self, initial: Option<Self::InitialGuess>) -> Result<Self::Output, Self::Error> {
let mut this = self.clone();
if let Some(p) = initial {
this.set_initial_guess(p);
}
Ok(this.solve())
}
}