use std::sync::Arc;
use std::collections::HashMap;
use std::any::Any;
use std::ops::Deref;
use data::volsurface::RcVolSurface;
use data::forward::Forward;
use data::curves::RcRateCurve;
use data::bump::Bump;
use dates::Date;
use instruments::Instrument;
use instruments::PricingContext;
use risk::dependencies::DependencyCollector;
use risk::marketdata::MarketData;
use risk::marketdata::SavedData;
use risk::marketdata::copy_from_saved;
use risk::Bumpable;
use risk::Saveable;
use risk::BumpablePricingContext;
use core::qm;
#[derive(Clone)]
pub struct PricingContextPrefetch {
context: MarketData,
dependencies: Arc<DependencyCollector>,
forward_curves: HashMap<String, Arc<Forward>>,
vol_surfaces: HashMap<String, RcVolSurface>,
}
impl PricingContextPrefetch {
pub fn new(
context: &MarketData,
dependencies: Arc<DependencyCollector>)
-> Result<PricingContextPrefetch, qm::Error> {
let mut forward_curves = HashMap::new();
let mut vol_surfaces = HashMap::new();
walk_dependencies(
&context, &dependencies, &mut forward_curves, &mut vol_surfaces)?;
Ok(PricingContextPrefetch {
context: context.clone(),
dependencies: dependencies,
forward_curves: forward_curves,
vol_surfaces: vol_surfaces
})
}
pub fn refetch_all(&mut self) -> Result<(), qm::Error> {
self.forward_curves.clear();
self.vol_surfaces.clear();
walk_dependencies(
&self.context, &self.dependencies,
&mut self.forward_curves, &mut self.vol_surfaces)
}
pub fn refetch(&mut self, id: &str,
bumped_forward: bool,
bumped_vol: bool,
saved_forward_curves: Option<&mut HashMap<String, Arc<Forward>>>,
saved_vol_surfaces: Option<&mut HashMap<String, RcVolSurface>>)
-> Result<bool, qm::Error> {
if !bumped_forward && !bumped_vol {
return Ok(false)
}
let id_string = id.to_string();
if let Some(fwd) = self.forward_curves.get_mut(&id_string) {
if let Some(inst) = self.dependencies.instrument_by_id(id) {
let instrument: &Instrument = &*inst.clone();
if bumped_forward {
if let Some(s) = saved_forward_curves {
s.insert(id.to_string(), fwd.clone());
}
if let Some(hwm)
= self.dependencies.forward_curve_hwm(inst) {
*fwd = self.context.forward_curve(instrument, hwm)?;
} else {
return Err(qm::Error::new("Cannot find forward"))
}
}
if bumped_vol {
if let Some(vol) = self.vol_surfaces.get_mut(&id_string) {
if let Some(s) = saved_vol_surfaces {
s.insert(id_string, vol.clone());
}
if let Some(vol_hwm) =
self.dependencies.vol_surface_hwm(inst) {
*vol = self.context.vol_surface(instrument, vol_hwm,
&|| Ok(fwd.clone()))?;
} else {
return Err(qm::Error::new("Cannot find vol"))
}
}
}
} else {
return Err(qm::Error::new("Cannot find instrument"))
}
} else {
return Err(qm::Error::new("Cannot find prefetched forward"))
}
Ok(true)
}
}
fn walk_dependencies(
context: &MarketData,
dependencies: &Arc<DependencyCollector>,
forward_curves: &mut HashMap<String, Arc<Forward>>,
vol_surfaces: &mut HashMap<String, RcVolSurface>)
-> Result<(), qm::Error> {
let forward_dependencies = dependencies.forward_curves();
let vol_dependencies = dependencies.vol_surfaces();
for (rc_instrument, high_water_mark) in &*forward_dependencies {
let instrument : &Instrument = rc_instrument.deref();
let id = instrument.id().to_string();
let forward = context.forward_curve(instrument, *high_water_mark)?;
if let Some(vol_hwd) = vol_dependencies.get(rc_instrument) {
let vol = context.vol_surface(instrument, *vol_hwd, &|| Ok(forward.clone()))?;
vol_surfaces.insert(id.clone(), vol);
}
forward_curves.insert(id, forward);
}
Ok(())
}
impl PricingContext for PricingContextPrefetch {
fn spot_date(&self) -> Date {
self.context.spot_date()
}
fn yield_curve(&self, credit_id: &str, high_water_mark: Date)
-> Result<RcRateCurve, qm::Error> {
self.context.yield_curve(credit_id, high_water_mark)
}
fn spot(&self, id: &str) -> Result<f64, qm::Error> {
self.context.spot(id)
}
fn forward_curve(&self, instrument: &Instrument, _high_water_mark: Date)
-> Result<Arc<Forward>, qm::Error> {
find_cached_data(instrument.id(), &self.forward_curves, "Forward")
}
fn vol_surface(&self, instrument: &Instrument, _high_water_mark: Date,
_forward_fn: &Fn() -> Result<Arc<Forward>, qm::Error>)
-> Result<RcVolSurface, qm::Error> {
find_cached_data(instrument.id(), &self.vol_surfaces, "Vol Surface")
}
fn correlation(&self, first: &Instrument, second: &Instrument)
-> Result<f64, qm::Error> {
self.context.correlation(first, second)
}
}
fn find_cached_data<T: Clone>(id: &str, collection: &HashMap<String, T>,
item: &str) -> Result<T, qm::Error> {
match collection.get(id) {
None => Err(qm::Error::new(&format!(
"{} not found (incorrect dependencies?): '{}'", item, id))),
Some(x) => Ok(x.clone())
}
}
impl Bumpable for PricingContextPrefetch {
fn bump(&mut self, bump: &Bump, any_saved: Option<&mut Saveable>)
-> Result<bool, qm::Error> {
let saved = to_saved(any_saved)?;
let (saved_data, saved_forward_curves, saved_vol_surfaces)
: (Option<&mut Saveable>
, Option<&mut HashMap<String, Arc<Forward>>>
, Option<&mut HashMap<String, RcVolSurface>>)
= if let Some(s) = saved {
(Some(&mut s.saved_data), Some(&mut s.forward_curves), Some(&mut s.vol_surfaces))
} else {
(None, None, None)
};
let bumped = if let &Bump::SpotDate(ref bump) = bump {
bump.spot_date() != self.spot_date()
} else {
self.context.bump(bump, saved_data)?
};
match bump {
&Bump::Spot(ref id, _) => self.refetch(&id, bumped, false, saved_forward_curves, saved_vol_surfaces),
&Bump::Divs(ref id, _) => self.refetch(&id, bumped, false, saved_forward_curves, saved_vol_surfaces),
&Bump::Vol(ref id, _) => self.refetch(&id, false, bumped, saved_forward_curves, saved_vol_surfaces),
&Bump::Borrow(ref id, _) => self.refetch(&id, bumped, false, saved_forward_curves, saved_vol_surfaces),
&Bump::Yield(ref credit_id, _) => {
let v = self.dependencies
.forward_id_by_credit_id(&credit_id).to_vec();
let mut done = false;
if let Some(sfc) = saved_forward_curves {
if let Some(svs) = saved_vol_surfaces {
done = true;
for id in v.iter() {
self.refetch(&id, bumped, false, Some(sfc), Some(svs))?;
}
}
}
if !done {
for id in v.iter() {
self.refetch(&id, bumped, false, None, None)?;
}
}
Ok(bumped) },
&Bump::SpotDate(ref bump) => {
if bumped {
self.context.bump_spot_date(bump, &self.dependencies)?;
self.refetch_all()?;
}
Ok(bumped) }
}
}
fn dependencies(&self) -> Result<&DependencyCollector, qm::Error> {
Ok(&*self.dependencies)
}
fn context(&self) -> &PricingContext {
self.as_pricing_context()
}
fn new_saveable(&self) -> Box<Saveable> {
Box::new(SavedPrefetch::new())
}
fn restore(&mut self, any_saved: &Saveable) -> Result<(), qm::Error> {
if let Some(saved)
= any_saved.as_any().downcast_ref::<SavedPrefetch>() {
self.context.restore(&saved.saved_data)?;
copy_from_saved(&mut self.forward_curves, &saved.forward_curves);
copy_from_saved(&mut self.vol_surfaces, &saved.vol_surfaces);
Ok(())
} else {
Err(qm::Error::new("Mismatching save space for restore"))
}
}
}
impl BumpablePricingContext for PricingContextPrefetch {
fn as_bumpable(&self) -> &Bumpable { self }
fn as_mut_bumpable(&mut self) -> &mut Bumpable { self }
fn as_pricing_context(&self) -> &PricingContext { self }
fn raw_market_data(&self) -> &MarketData { &self.context }
}
fn to_saved(opt_any_saved: Option<&mut Saveable>)
-> Result<Option<&mut SavedPrefetch>, qm::Error> {
if let Some(any_saved) = opt_any_saved {
if let Some(saved) = any_saved.as_mut_any().downcast_mut::<SavedPrefetch>() {
Ok(Some(saved))
} else {
Err(qm::Error::new("Mismatching save space for bumped prefetch"))
}
} else {
Ok(None)
}
}
pub struct SavedPrefetch {
saved_data: SavedData,
forward_curves: HashMap<String, Arc<Forward>>,
vol_surfaces: HashMap<String, RcVolSurface>
}
impl SavedPrefetch {
pub fn new() -> SavedPrefetch {
SavedPrefetch {
saved_data: SavedData::new(),
forward_curves: HashMap::new(),
vol_surfaces: HashMap::new() }
}
}
impl Saveable for SavedPrefetch {
fn as_any(&self) -> &Any { self }
fn as_mut_any(&mut self) -> &mut Any { self }
fn clear(&mut self) {
self.saved_data.clear();
self.forward_curves.clear();
self.vol_surfaces.clear();
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use std::sync::Arc;
use instruments::DependencyContext;
use instruments::Priceable;
use instruments::RcInstrument;
use math::numerics::approx_eq;
use risk::marketdata::tests::sample_market_data;
use risk::marketdata::tests::sample_european;
use data::bumpspot::BumpSpot;
use data::bumpdivs::BumpDivs;
use data::bumpvol::BumpVol;
use data::bumpyield::BumpYield;
use dates::datetime::DateTime;
use dates::datetime::TimeOfDay;
use core::factories::Qrc;
pub fn create_dependencies(instrument: &RcInstrument, spot_date: Date)
-> Arc<DependencyCollector> {
let mut collector = DependencyCollector::new(spot_date);
collector.spot(instrument);
Arc::new(collector)
}
#[test]
fn european_bumped_price_with_prefetch() {
let market_data = sample_market_data();
let european = sample_european();
let val_date = DateTime::new(Date::from_ymd(2017, 01, 02), TimeOfDay::Open);
let unbumped_price = european.price(&market_data, val_date).unwrap();
let spot_date = Date::from_ymd(2017, 01, 02);
let instrument = RcInstrument::new(Qrc::new(european.clone()));
let dependencies = create_dependencies(&instrument, spot_date);
let mut mut_data = PricingContextPrefetch::new(&market_data,
dependencies).unwrap();
let mut save = SavedPrefetch::new();
let bump = Bump::new_spot("BP.L", BumpSpot::new_relative(0.01));
let bumped = mut_data.bump(&bump, Some(&mut save)).unwrap();
assert!(bumped);
let bumped_price = european.price(&mut_data, val_date).unwrap();
assert_approx(bumped_price, 17.343905306334765, 1e-12);
mut_data.restore(&save).unwrap();
save.clear();
let price = european.price(&mut_data, val_date).unwrap();
assert_approx(price, unbumped_price, 1e-12);
let bump = Bump::new_vol("BP.L", BumpVol::new_flat_additive(0.01));
let bumped = mut_data.bump(&bump, Some(&mut save)).unwrap();
assert!(bumped);
let bumped_price = european.price(&mut_data, val_date).unwrap();
assert_approx(bumped_price, 17.13982242072566, 1e-12);
mut_data.restore(&save).unwrap();
save.clear();
let price = european.price(&mut_data, val_date).unwrap();
assert_approx(price, unbumped_price, 1e-12);
let bump = Bump::new_divs("BP.L", BumpDivs::new_all_relative(0.01));
let bumped = mut_data.bump(&bump, Some(&mut save)).unwrap();
assert!(bumped);
let bumped_price = european.price(&mut_data, val_date).unwrap();
assert_approx(bumped_price, 16.691032323609356, 1e-12);
mut_data.restore(&save).unwrap();
save.clear();
let price = european.price(&mut_data, val_date).unwrap();
assert_approx(price, unbumped_price, 1e-12);
let bump = Bump::new_yield("LSE", BumpYield::new_flat_annualised(0.01));
let bumped = mut_data.bump(&bump, Some(&mut save)).unwrap();
assert!(bumped);
let bumped_price = european.price(&mut_data, val_date).unwrap();
assert_approx(bumped_price, 17.299620299229513, 1e-12);
mut_data.restore(&save).unwrap();
save.clear();
let price = european.price(&mut_data, val_date).unwrap();
assert_approx(price, unbumped_price, 1e-12);
let bump = Bump::new_yield("OPT", BumpYield::new_flat_annualised(0.01));
let bumped = mut_data.bump(&bump, Some(&mut save)).unwrap();
assert!(bumped);
let bumped_price = european.price(&mut_data, val_date).unwrap();
assert_approx(bumped_price, 16.710717400832973, 1e-12);
mut_data.restore(&save).unwrap();
save.clear();
let price = european.price(&mut_data, val_date).unwrap();
assert_approx(price, unbumped_price, 1e-12);
}
fn assert_approx(value: f64, expected: f64, tolerance: f64) {
assert!(approx_eq(value, expected, tolerance),
"value={} expected={}", value, expected);
}
}