use std::rc::Rc;
use crate::errors::QlResult;
use crate::math::array::Array;
use crate::math::optimization::constraint::{BoundaryConstraint, Constraint};
use crate::models::model::{CalibratedModel, CalibratedModelHolder, register_with_term_structure};
use crate::models::parameter::ConstantParameter;
use crate::patterns::observable::{AsObservable, Observer};
use crate::processes::BatesProcess;
use crate::shared::{Shared, SharedMut, shared, shared_mut};
use crate::types::Real;
pub struct BatesModel {
model: CalibratedModel,
process: Shared<BatesProcess>,
_observer: Option<SharedMut<dyn Observer>>,
}
struct FiniteDomain {
positive: bool,
}
impl Constraint for FiniteDomain {
fn test(&self, params: &Array) -> bool {
params
.iter()
.all(|&p| p.is_finite() && (!self.positive || p > 0.0))
}
fn lower_bound(&self, params: &Array) -> Array {
Array::filled(params.size(), if self.positive { 0.0 } else { -Real::MAX })
}
}
impl BatesModel {
pub fn new(process: Shared<BatesProcess>) -> QlResult<SharedMut<Self>> {
let params = Array::from([
process.theta(),
process.kappa(),
process.sigma(),
process.rho(),
process.v0(),
process.nu(),
process.delta(),
process.lambda(),
]);
BatesProcess::validate_parameters(¶ms)?;
let mut model = CalibratedModel::new(8);
for (index, &value) in params.iter().enumerate() {
let constraint: Rc<dyn Constraint> = match index {
3 => Rc::new(BoundaryConstraint::new(-1.0, 1.0)),
6 | 7 => Rc::new(BoundaryConstraint::new(0.0, Real::MAX)),
_ => Rc::new(FiniteDomain {
positive: index != 5,
}),
};
model.arguments_mut()[index] = ConstantParameter::new(value, constraint)?;
}
let risk_free = process.risk_free_rate();
let dividend = process.dividend_yield();
let spot = process.s0();
let mut bates = Self {
model,
process,
_observer: None,
};
bates.generate_arguments();
let model = shared_mut(bates);
let observer = register_with_term_structure(&model, &risk_free);
dividend.register_observer(&observer);
spot.register_observer(&observer);
model.borrow_mut()._observer = Some(observer);
Ok(model)
}
pub fn theta(&self) -> Real {
self.model.arguments()[0].value(0.0)
}
pub fn kappa(&self) -> Real {
self.model.arguments()[1].value(0.0)
}
pub fn sigma(&self) -> Real {
self.model.arguments()[2].value(0.0)
}
pub fn rho(&self) -> Real {
self.model.arguments()[3].value(0.0)
}
pub fn v0(&self) -> Real {
self.model.arguments()[4].value(0.0)
}
pub fn nu(&self) -> Real {
self.model.arguments()[5].value(0.0)
}
pub fn delta(&self) -> Real {
self.model.arguments()[6].value(0.0)
}
pub fn lambda(&self) -> Real {
self.model.arguments()[7].value(0.0)
}
pub fn process(&self) -> Shared<BatesProcess> {
self.process.clone()
}
}
impl CalibratedModelHolder for BatesModel {
fn calibrated_model(&self) -> &CalibratedModel {
&self.model
}
fn calibrated_model_mut(&mut self) -> &mut CalibratedModel {
&mut self.model
}
fn generate_arguments(&mut self) {
self.process = shared(self.process.with_parameters(&self.model.params()));
}
fn set_params(&mut self, params: &Array) -> QlResult<()> {
BatesProcess::validate_parameters(params)?;
self.model.write_params(params)?;
self.generate_arguments();
self.model.observable().notify_observers();
Ok(())
}
}