pub mod assets;
pub mod bonds;
pub mod options;
pub mod basket;
use instruments::assets::Currency;
use instruments::assets::CreditEntity;
use instruments::assets::Equity;
use instruments::bonds::ZeroCoupon;
use instruments::basket::Basket;
use instruments::options::SpotStartingEuropean;
use instruments::options::ForwardStartingEuropean;
use dates::Date;
use dates::datetime::TimeOfDay;
use dates::rules::RcDateRule;
use dates::datetime::DateTime;
use dates::datetime::DateDayFraction;
use data::curves::RcRateCurve;
use data::forward::Forward;
use data::volsurface::RcVolSurface;
use data::volsurface::VolTimeDynamics;
use data::volsurface::VolForwardDynamics;
use data::fixings::FixingTable;
use core::qm;
use core::factories::TypeId;
use core::factories::Registry;
use core::factories::Qrc;
use core::dedup::{Dedup, DedupControl, Drc, FromId, InstanceId};
use math::interpolation::Interpolate;
use std::sync::Arc;
use std::hash::Hash;
use std::collections::HashMap;
use std::cell::RefCell;
use std::cmp::Ordering;
use std::hash::Hasher;
use std::f64::NAN;
use std::fmt::Debug;
use std::fmt;
use ndarray::ArrayView2;
use erased_serde as esd;
use serde as sd;
use serde_tagged as sdt;
use serde_tagged::de::BoxFnSeed;
pub trait Instrument : esd::Serialize + TypeId + InstanceId + Sync + Send + Debug {
fn payoff_currency(&self) -> &Currency;
fn credit_id(&self) -> &str;
fn settlement(&self) -> &RcDateRule;
fn dependencies(&self, context: &mut DependencyContext) -> SpotRequirement;
fn is_driftless(&self) -> bool {
false
}
fn is_pure_rates(&self) -> bool {
false
}
fn time_to_day_fraction(&self, _date_time: DateTime)
-> Result<DateDayFraction, qm::Error> {
Err(qm::Error::new("Underlying does not support volatility"))
}
fn vol_time_dynamics(&self) -> VolTimeDynamics {
VolTimeDynamics::ConstantExpiry
}
fn vol_forward_dynamics(&self) -> VolForwardDynamics {
VolForwardDynamics::StickyStrike
}
fn fix(&self, _fixing_table: &FixingTable)
-> Result<Option<Vec<(f64, RcInstrument)>>, qm::Error> {
Ok(None)
}
fn as_priceable(&self) -> Option<&Priceable> {
None
}
fn as_mc_priceable(&self) -> Option<&MonteCarloPriceable> {
None
}
}
pub fn fix_all(instruments: &Vec<(f64, RcInstrument)>, fixing_table: &FixingTable)
-> Result<Option<Vec<(f64, RcInstrument)>>, qm::Error> {
let mut basket : Vec<(f64, RcInstrument)> = Vec::new();
let mut uncopied = 0;
let mut modified = false;
for &(weight, ref instrument) in instruments.iter() {
if let Some(decomposition) = instrument.fix(fixing_table)? {
if !modified {
for &(prev_weight, ref prev_instrument) in instruments.iter().take(uncopied) {
basket.push((prev_weight, prev_instrument.clone()));
}
uncopied = 0;
modified = true;
}
for &(weight2, ref instrument) in decomposition.iter() {
basket.push((weight * weight2, instrument.clone()));
}
} else if modified {
basket.push((weight, instrument.clone()));
} else {
uncopied += 1;
}
}
if modified {
Ok(Some(basket))
} else {
Ok(None)
}
}
pub type TypeRegistry = Registry<BoxFnSeed<Qrc<Instrument>>>;
impl<'de> sd::Deserialize<'de> for Qrc<Instrument> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: sd::Deserializer<'de>
{
sdt::de::external::deserialize(deserializer, get_registry())
}
}
pub fn get_registry() -> &'static TypeRegistry {
lazy_static! {
static ref REG: TypeRegistry = {
let mut reg = TypeRegistry::new();
reg.insert("Currency", BoxFnSeed::new(Currency::from_serial));
reg.insert("CreditEntity", BoxFnSeed::new(CreditEntity::from_serial));
reg.insert("Equity", BoxFnSeed::new(Equity::from_serial));
reg.insert("Equity", BoxFnSeed::new(Equity::from_serial));
reg.insert("ZeroCoupon", BoxFnSeed::new(ZeroCoupon::from_serial));
reg.insert("Basket", BoxFnSeed::new(Basket::from_serial));
reg.insert("SpotStartingEuropean", BoxFnSeed::new(SpotStartingEuropean::from_serial));
reg.insert("ForwardStartingEuropean", BoxFnSeed::new(ForwardStartingEuropean::from_serial));
reg
};
}
®
}
pub type RcInstrument = Drc<Instrument, Qrc<Instrument>>;
impl Ord for RcInstrument {
fn cmp(&self, other: &RcInstrument) -> Ordering {
self.id().cmp(other.id())
}
}
impl PartialOrd for RcInstrument {
fn partial_cmp(&self, other: &RcInstrument) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for RcInstrument {
fn eq(&self, other: &RcInstrument) -> bool {
self.id() == other.id()
}
}
impl Eq for RcInstrument {}
impl Hash for RcInstrument {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id().hash(state);
}
}
thread_local! {
pub static DEDUP_INSTRUMENT : RefCell<Dedup<Instrument, Qrc<Instrument>>>
= RefCell::new(Dedup::new(DedupControl::Inline, HashMap::new()));
}
impl FromId for RcInstrument {
fn from_id(id: &str) -> Option<Self> {
DEDUP_INSTRUMENT.with(|tls| tls.borrow().get(id).clone())
}
}
impl sd::Serialize for RcInstrument {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: sd::Serializer {
self.serialize_with_dedup(serializer, &DEDUP_INSTRUMENT, |s| {
let qrc : &Qrc<Instrument> = self.content();
qrc.serialize(s)
})
}
}
pub fn string_or_struct_polymorphic<'de, D>(deserializer: D) -> Result<RcInstrument, D::Error>
where
D: sd::de::Deserializer<'de>,
{
struct StringOrStruct();
impl<'de> sd::de::Visitor<'de> for StringOrStruct {
type Value = RcInstrument;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string or map")
}
fn visit_str<E>(self, value: &str) -> Result<RcInstrument, E>
where
E: sd::de::Error,
{
if let Some(result) = FromId::from_id(value) {
Ok(result)
} else {
Err(sd::de::Error::invalid_value(sd::de::Unexpected::Str(value), &self))
}
}
fn visit_map<M>(self, visitor: M) -> Result<RcInstrument, M::Error>
where
M: sd::de::MapAccess<'de>,
{
let obj: Qrc<Instrument> = sd::de::Deserialize::deserialize(sd::de::value::MapAccessDeserializer::new(visitor))?;
Ok(Drc::new(obj))
}
}
deserializer.deserialize_any(StringOrStruct())
}
impl<'de> sd::Deserialize<'de> for RcInstrument {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: sd::Deserializer<'de> {
Self::deserialize_with_dedup(deserializer, &DEDUP_INSTRUMENT, |d| {
string_or_struct_polymorphic(d)
})
}
}
pub trait DependencyContext {
fn spot_date(&self) -> Date;
fn yield_curve(&mut self, credit_id: &str, high_water_mark: Date);
fn spot(&mut self, instrument: &RcInstrument);
fn forward_curve(&mut self, instrument: &RcInstrument,
high_water_mark: Date);
fn vol_surface(&mut self, instrument: &RcInstrument,
high_water_mark: Date);
fn fixing(&mut self, id: &str, date: DateTime);
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum SpotRequirement {
NotRequired,
RequiredOnlyForValuation,
Required
}
pub trait Dateable {
fn new_dated(&self, date: Date) -> Dated;
}
pub trait Dated {
fn expiry_date(&self) -> Date;
}
pub trait Priceable : Instrument {
fn price(&self, context: &PricingContext, val_date: DateTime) -> Result<f64, qm::Error> {
let dates = [val_date];
let mut prices = [NAN];
self.prices(context, &dates, &mut prices)?;
Ok(prices[0])
}
fn prices(&self, context: &PricingContext, dates: &[DateTime], out: &mut [f64])
-> Result<(), qm::Error>;
fn as_instrument(&self) -> &Instrument;
}
pub struct ForwardFromPriceable<'a> {
priceable: &'a Priceable,
context: &'a PricingContext,
time_of_day: TimeOfDay
}
impl<'a> ForwardFromPriceable<'a> {
pub fn new(priceable: &'a Priceable, context: &'a PricingContext, time_of_day: TimeOfDay)
-> ForwardFromPriceable<'a> {
ForwardFromPriceable { priceable, context, time_of_day }
}
}
impl<'a> Forward for ForwardFromPriceable<'a> {
fn as_interp(&self) -> &Interpolate<Date> { self }
fn forward(&self, date: Date) -> Result<f64, qm::Error> {
self.priceable.price(self.context, DateTime::new(date, self.time_of_day))
}
}
pub trait PricingContext : Sync + Send {
fn spot_date(&self) -> Date;
fn yield_curve(&self, credit_id: &str, high_water_mark: Date)
-> Result<RcRateCurve, qm::Error>;
fn spot(&self, id: &str) -> Result<f64, qm::Error>;
fn forward_curve(&self, instrument: &Instrument, high_water_mark: Date)
-> Result<Arc<Forward>, qm::Error>;
fn vol_surface(&self, instrument: &Instrument, high_water_mark: Date,
forward_fn: &Fn() -> Result<Arc<Forward>, qm::Error>)
-> Result<RcVolSurface, qm::Error>;
fn correlation(&self, first: &Instrument, second: &Instrument)
-> Result<f64, qm::Error>;
}
pub trait MonteCarloPriceable : Instrument {
fn mc_dependencies(&self, dates: &[DateDayFraction],
output: &mut MonteCarloDependencies) -> Result<(), qm::Error>;
fn start_date(&self) -> Option<DateDayFraction>;
fn mc_price(&self, context: &MonteCarloContext) -> Result<f64, qm::Error>;
fn as_instrument(&self) -> &Instrument;
}
pub trait MonteCarloDependencies {
fn observation(&mut self, instrument: &RcInstrument,
date_time: DateDayFraction);
fn flow(&mut self, instrument: &RcInstrument);
}
pub trait MonteCarloContext {
fn paths(&self, instrument: &RcInstrument)
-> Result<ArrayView2<f64>, qm::Error>;
fn evaluate_flows(&self, quantities: ArrayView2<f64>)
-> Result<f64, qm::Error>;
fn pricing_context(&self) -> &PricingContext;
}