use std::cell::RefCell;
use crate::errors::QlResult;
use crate::handle::Handle;
use crate::interestrate::Compounding;
use crate::math::array::Array;
use crate::methods::finitedifferences::BoundaryCondition;
use crate::methods::finitedifferences::meshers::{FdmMesher, UniformGridMesher};
use crate::methods::finitedifferences::operators::{
FdmBlackScholesOp, FdmLinearOp, FdmLinearOpComposite, FdmLinearOpLayout,
};
use crate::methods::finitedifferences::utilities::FdmBoundaryConditionSet;
use crate::processes::GeneralizedBlackScholesProcess;
use crate::quotes::make_quote_handle;
use crate::shared::{Shared, SharedMut, shared, shared_mut};
use crate::termstructures::volatility::{BlackConstantVol, BlackVolTermStructure};
use crate::termstructures::yields::FlatForward;
use crate::termstructures::yieldtermstructure::YieldTermStructure;
use crate::time::date::{Date, Month};
use crate::time::daycounter::DayCounter;
use crate::time::daycounters::actual365fixed::Actual365Fixed;
use crate::time::frequency::Frequency;
use crate::types::{Rate, Real, Size, Time, Volatility};
pub const WHOLE: Real = 0.7;
pub const GRID: Size = 5;
const DIRECTION: Size = 0;
const R: Rate = 0.05;
const Q: Rate = 0.02;
const VOL: Volatility = 0.2;
const STRIKE: Real = 100.0;
const TOL: Real = 1e-13;
pub struct ScaledComposite {
coefficients: Vec<Real>,
pub last_set_time: Option<(Time, Time)>,
}
impl ScaledComposite {
fn new(coefficients: &[Real]) -> Self {
ScaledComposite {
coefficients: coefficients.to_vec(),
last_set_time: None,
}
}
}
impl FdmLinearOp for ScaledComposite {
fn apply(&self, r: &Array) -> Array {
WHOLE * r
}
}
impl FdmLinearOpComposite for ScaledComposite {
fn size(&self) -> Size {
self.coefficients.len()
}
fn set_time(&mut self, t1: Time, t2: Time) -> QlResult<()> {
self.last_set_time = Some((t1, t2));
Ok(())
}
fn apply_mixed(&self, r: &Array) -> Array {
Array::with_size(r.size())
}
fn apply_direction(&self, direction: Size, r: &Array) -> Array {
self.coefficients[direction] * r
}
fn solve_splitting(&self, direction: Size, r: &Array, s: Real) -> QlResult<Array> {
Ok(r / (1.0 + s * self.coefficients[direction]))
}
fn preconditioner(&self, r: &Array, s: Real) -> QlResult<Array> {
self.solve_splitting(0, r, s)
}
}
pub fn scaled_composite(coefficients: &[Real]) -> SharedMut<ScaledComposite> {
shared_mut(ScaledComposite::new(coefficients))
}
pub struct CallLog {
log: Shared<RefCell<Vec<String>>>,
}
impl BoundaryCondition for CallLog {
fn apply_before_applying(&self, _op: &mut dyn FdmLinearOp) {
self.log.borrow_mut().push("before_applying".to_string());
}
fn apply_after_applying(&self, _a: &mut Array) {
self.log.borrow_mut().push("after_applying".to_string());
}
fn apply_before_solving(&self, _op: &mut dyn FdmLinearOp, _rhs: &mut Array) {
self.log.borrow_mut().push("before_solving".to_string());
}
fn apply_after_solving(&self, _a: &mut Array) {
self.log.borrow_mut().push("after_solving".to_string());
}
fn set_time(&self, t: Time) {
self.log.borrow_mut().push(format!("set_time:{t}"));
}
}
pub fn call_log() -> (Shared<RefCell<Vec<String>>>, FdmBoundaryConditionSet) {
let log = shared(RefCell::new(Vec::new()));
let bc_set: FdmBoundaryConditionSet = vec![shared(CallLog {
log: Shared::clone(&log),
})];
(log, bc_set)
}
pub fn mesher() -> Shared<dyn FdmMesher> {
let layout = shared(FdmLinearOpLayout::new(vec![GRID]));
shared(UniformGridMesher::new(layout, &[(4.0, 5.0)]).unwrap())
}
pub fn black_scholes_op(mesher: &Shared<dyn FdmMesher>) -> FdmBlackScholesOp {
let dc = Actual365Fixed::new();
let today = Date::new(11, Month::February, 2018);
let process = GeneralizedBlackScholesProcess::new(
make_quote_handle(100.0).handle(),
flat_rate(today, Q, dc.clone()),
flat_rate(today, R, dc.clone()),
flat_vol(today, VOL, dc),
);
FdmBlackScholesOp::new(Shared::clone(mesher), &process, STRIKE, DIRECTION).unwrap()
}
fn flat_rate(reference: Date, rate: Rate, dc: DayCounter) -> Handle<dyn YieldTermStructure> {
Handle::new(shared(FlatForward::with_rate(
reference,
rate,
dc,
Compounding::Continuous,
Frequency::Annual,
)) as Shared<dyn YieldTermStructure>)
}
fn flat_vol(reference: Date, vol: Volatility, dc: DayCounter) -> Handle<dyn BlackVolTermStructure> {
Handle::new(shared(BlackConstantVol::new(reference, None, vol, dc))
as Shared<dyn BlackVolTermStructure>)
}
pub fn probe(n: Size) -> Array {
(0..n)
.map(|i| {
let i = i as Real;
1.0 + 0.5 * i + 0.05 * i * i
})
.collect()
}
pub fn assert_close(actual: &Array, expected: &Array) {
assert_eq!(actual.size(), expected.size());
for i in 0..actual.size() {
assert!(
(actual[i] - expected[i]).abs() <= TOL,
"element {i}: {} != {}",
actual[i],
expected[i]
);
}
}