use std::collections::HashMap;
use std::sync::Arc;
use std::any::Any;
use std::ops::Deref;
use core::qm;
use dates::Date;
use data::curves::RcRateCurve;
use data::divstream::RcDividendStream;
use data::volsurface::RcVolSurface;
use data::forward::Forward;
use data::forward::EquityForward;
use data::bump::Bump;
use data::bumpspot::BumpSpot;
use data::bumpdivs::BumpDivs;
use data::bumpyield::BumpYield;
use data::bumpvol::BumpVol;
use data::bumpspotdate::BumpSpotDate;
use data::bumpspotdate::SpotDynamics;
use data::bump::Bumper;
use instruments::Instrument;
use instruments::PricingContext;
use risk::Bumpable;
use risk::Saveable;
use risk::BumpablePricingContext;
use risk::dependencies::DependencyCollector;
use serde as sd;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MarketData {
spot_date: Date,
spots: HashMap<String, f64>,
yield_curves: HashMap<String, RcRateCurve>,
borrow_curves: HashMap<String, RcRateCurve>,
dividends: HashMap<String, RcDividendStream>,
vol_surfaces: HashMap<String, RcVolSurface>
}
impl MarketData {
pub fn new(
spot_date: Date,
spots: HashMap<String, f64>,
yield_curves: HashMap<String, RcRateCurve>,
borrow_curves: HashMap<String, RcRateCurve>,
dividends: HashMap<String, RcDividendStream>,
vol_surfaces: HashMap<String, RcVolSurface>) -> MarketData {
MarketData {
spot_date: spot_date,
spots: spots,
yield_curves: yield_curves,
borrow_curves: borrow_curves,
dividends: dividends,
vol_surfaces: vol_surfaces }
}
pub fn bump_spot_date(&mut self, bump: &BumpSpotDate, dependencies: &DependencyCollector)
-> Result<(), qm::Error> {
let new_spot_date = bump.spot_date();
match bump.spot_dynamics() {
SpotDynamics::StickyForward => self.sticky_forward_bump(new_spot_date, dependencies)?,
SpotDynamics::StickySpot => self.sticky_spot_bump(new_spot_date, dependencies)?
}
self.spot_date = new_spot_date;
Ok(())
}
fn sticky_forward_bump(&mut self, new_spot_date: Date, dependencies: &DependencyCollector)
-> Result<(), qm::Error> {
let mut new_spots = HashMap::<String, f64>::new();
for (id, spot) in self.spots.iter() {
if let Some(instrument) = dependencies.instrument_by_id(id) {
let instr: &Instrument = &*instrument.clone();
let curve = self.forward_curve(instr, new_spot_date)?;
let new_spot = curve.forward(new_spot_date)?;
new_spots.insert(id.to_string(), new_spot);
} else {
new_spots.insert(id.to_string(), *spot);
}
}
self.spots = new_spots;
Ok(())
}
fn sticky_spot_bump(&mut self, _new_spot_date: Date, _dependencies: &DependencyCollector)
-> Result<(), qm::Error> {
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct RcMarketData(Arc<MarketData>);
impl RcMarketData {
pub fn new(table: Arc<MarketData>) -> RcMarketData {
RcMarketData(table)
}
}
impl Deref for RcMarketData {
type Target = MarketData;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl sd::Serialize for RcMarketData {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: sd::Serializer
{
self.0.serialize(serializer)
}
}
impl<'de> sd::Deserialize<'de> for RcMarketData {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: sd::Deserializer<'de> {
let md = MarketData::deserialize(deserializer)?;
Ok(RcMarketData::new(Arc::new(md)))
}
}
impl PricingContext for MarketData {
fn spot_date(&self) -> Date {
self.spot_date
}
fn yield_curve(&self, credit_id: &str, _high_water_mark: Date)
-> Result<RcRateCurve, qm::Error> {
find_market_data(credit_id, &self.yield_curves, "Yield curve")
}
fn spot(&self, id: &str) -> Result<f64, qm::Error> {
find_market_data(id, &self.spots, "Spot")
}
fn forward_curve(&self, instrument: &Instrument, high_water_mark: Date)
-> Result<Arc<Forward>, qm::Error> {
let id = instrument.id();
let spot = find_market_data(id, &self.spots, "Spot")?;
let divs = find_market_data(id, &self.dividends, "Dividends")?;
let borrow = find_market_data(id, &self.borrow_curves, "Borrow curve")?;
let credit_id = instrument.credit_id();
let yield_curve = find_market_data(&credit_id, &self.yield_curves,
"Yield curve for forward")?;
let settlement = instrument.settlement().clone();
let forward = EquityForward::new(self.spot_date, spot, settlement,
yield_curve, borrow, &*divs, high_water_mark)?;
Ok(Arc::new(forward))
}
fn vol_surface(&self, instrument: &Instrument, _high_water_mark: Date,
forward_fn: &Fn() -> Result<Arc<Forward>, qm::Error>)
-> Result<RcVolSurface, qm::Error> {
let id = instrument.id();
let mut vol = find_market_data(id, &self.vol_surfaces, "Vol surface")?;
instrument.vol_time_dynamics().modify(&mut vol, self.spot_date)?;
instrument.vol_forward_dynamics().modify(&mut vol, forward_fn)?;
Ok(vol)
}
fn correlation(&self, _first: &Instrument, _second: &Instrument)
-> Result<f64, qm::Error> {
Err(qm::Error::new("Correlation not implemented"))
}
}
fn find_market_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: '{}'", item, id))),
Some(x) => Ok(x.clone())
}
}
impl Bumpable for MarketData {
fn bump(&mut self, bump: &Bump, save: Option<&mut Saveable>)
-> Result<bool, qm::Error> {
let saved = to_saved_data(save)?;
match bump {
&Bump::Spot(ref id, ref bump) => apply_bump(&id, bump as &BumpSpot,
&mut self.spots, saved.map_or(None, |s| Some(&mut s.spots))),
&Bump::Divs(ref id, ref bump) => apply_bump(&id, bump as &BumpDivs,
&mut self.dividends, saved.map_or(None, |s| Some(&mut s.dividends))),
&Bump::Borrow(ref id, ref bump) => apply_bump(&id,
bump as &BumpYield, &mut self.borrow_curves,
saved.map_or(None, |s| Some(&mut s.borrow_curves))),
&Bump::Vol(ref id, ref bump) => apply_bump(&id, bump as &BumpVol,
&mut self.vol_surfaces, saved.map_or(None, |s| Some(&mut s.vol_surfaces))),
&Bump::Yield(ref credit_id, ref bump) => apply_bump(&credit_id,
bump as &BumpYield, &mut self.yield_curves,
saved.map_or(None, |s| Some(&mut s.yield_curves))),
&Bump::SpotDate(_) => Err(qm::Error::new("MarketData does not have \
enough information to handle spot date bumping on its own. It needs \
to be handled by a containing PricingContextPrefetch."))
}
}
fn dependencies(&self) -> Result<&DependencyCollector, qm::Error> {
Err(qm::Error::new("Dependency information not available"))
}
fn new_saveable(&self) -> Box<Saveable> {
Box::new(SavedData::new())
}
fn context(&self) -> &PricingContext {
self.as_pricing_context()
}
fn restore(&mut self, any_saved: &Saveable) -> Result<(), qm::Error> {
if let Some(saved) = any_saved.as_any().downcast_ref::<SavedData>() {
copy_from_saved(&mut self.spots, &saved.spots);
copy_from_saved(&mut self.yield_curves, &saved.yield_curves);
copy_from_saved(&mut self.borrow_curves, &saved.borrow_curves);
copy_from_saved(&mut self.dividends, &saved.dividends);
copy_from_saved(&mut self.vol_surfaces, &saved.vol_surfaces);
Ok(())
} else {
Err(qm::Error::new("Mismatching save space for restore"))
}
}
}
impl BumpablePricingContext for MarketData {
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 }
}
fn to_saved_data(opt_save: Option<&mut Saveable>) -> Result<Option<&mut SavedData>, qm::Error> {
if let Some(save) = opt_save {
if let Some(as_self) = save.as_mut_any().downcast_mut::<SavedData>() {
Ok(Some(as_self))
} else {
Err(qm::Error::new("Mismatching save space for bump market data"))
}
} else {
Ok(None)
}
}
fn apply_bump<T: Clone>(id: &str, bump: &Bumper<T>,
to_bump: &mut HashMap<String, T>,
to_save: Option<&mut HashMap<String, T>>) -> Result<bool, qm::Error> {
let key = id.to_string();
if let Some(entry) = to_bump.get_mut(&key) {
if let Some(save) = to_save {
save.insert(key, entry.clone());
}
*entry = bump.apply(entry.clone());
Ok(true)
} else {
Ok(false)
}
}
pub fn copy_from_saved<T: Clone>(to_restore: &mut HashMap<String, T>,
saved: &HashMap<String, T>) {
for (key, value) in saved.iter() {
to_restore.insert(key.to_string(), value.clone());
}
}
pub struct SavedData {
spots: HashMap<String, f64>,
yield_curves: HashMap<String, RcRateCurve>,
borrow_curves: HashMap<String, RcRateCurve>,
dividends: HashMap<String, RcDividendStream>,
vol_surfaces: HashMap<String, RcVolSurface>
}
impl SavedData {
pub fn new() -> SavedData {
SavedData {
spots: HashMap::new(),
yield_curves: HashMap::new(),
borrow_curves: HashMap::new(),
dividends: HashMap::new(),
vol_surfaces: HashMap::new() }
}
}
impl Saveable for SavedData {
fn as_any(&self) -> &Any { self }
fn as_mut_any(&mut self) -> &mut Any { self }
fn clear(&mut self) {
self.spots.clear();
self.yield_curves.clear();
self.borrow_curves.clear();
self.dividends.clear();
self.vol_surfaces.clear();
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use instruments::assets::Currency;
use instruments::assets::RcCurrency;
use std::sync::Arc;
use dates::datetime::DateTime;
use dates::datetime::TimeOfDay;
use dates::datetime::DateDayFraction;
use dates::rules::RcDateRule;
use dates::rules::BusinessDays;
use instruments::assets::Equity;
use instruments::options::SpotStartingEuropean;
use instruments::options::ForwardStartingEuropean;
use instruments::options::PutOrCall;
use instruments::options::OptionSettlement;
use instruments::Priceable;
use instruments::RcInstrument;
use data::divstream::DividendStream;
use data::divstream::Dividend;
use data::curves::RateCurveAct365;
use data::volsurface::RcVolSurface;
use data::volsurface::FlatVolSurface;
use data::bumpspot::BumpSpot;
use data::bumpdivs::BumpDivs;
use data::bumpvol::BumpVol;
use data::bumpyield::BumpYield;
use dates::calendar::WeekdayCalendar;
use dates::calendar::RcCalendar;
use math::numerics::approx_eq;
use math::interpolation::Extrap;
use core::factories::Qrc;
use serde_json;
pub fn sample_currency(step: u32) -> Currency {
let calendar = RcCalendar::new(Arc::new(WeekdayCalendar::new()));
let settlement = RcDateRule::new(Arc::new(BusinessDays::new_step(calendar, step)));
Currency::new("GBP", settlement)
}
pub fn sample_settlement(step: u32) -> RcDateRule {
let calendar = RcCalendar::new(Arc::new(WeekdayCalendar::new()));
RcDateRule::new(Arc::new(BusinessDays::new_step(calendar, step)))
}
pub fn sample_equity(currency: RcCurrency, step: u32) -> Equity {
let settlement = sample_settlement(step);
Equity::new("BP.L", "LSE", currency, settlement)
}
pub fn sample_european() -> Arc<SpotStartingEuropean> {
let strike = 100.0;
let put_or_call = PutOrCall::Call;
let expiry = DateTime::new(
Date::from_ymd(2018, 06, 01), TimeOfDay::Close);
let currency = RcCurrency::new(Arc::new(sample_currency(2)));
let settlement = sample_settlement(2);
let equity = RcInstrument::new(Qrc::new(Arc::new(sample_equity(currency, 2))));
let european = SpotStartingEuropean::new("SampleSpotEuropean", "OPT",
equity.clone(), settlement, expiry,
strike, put_or_call, OptionSettlement::Cash).unwrap();
Arc::new(european)
}
pub fn sample_forward_european() -> Arc<ForwardStartingEuropean> {
let strike_fraction = 0.95;
let strike_date = DateTime::new(
Date::from_ymd(2017, 01, 02), TimeOfDay::Close);
let put_or_call = PutOrCall::Call;
let expiry = DateTime::new(
Date::from_ymd(2018, 06, 01), TimeOfDay::Close);
let currency = RcCurrency::new(Arc::new(sample_currency(2)));
let settlement = sample_settlement(2);
let equity = RcInstrument::new(Qrc::new(Arc::new(sample_equity(currency, 2))));
let european = ForwardStartingEuropean::new("SampleForwardEuropean", "OPT",
equity.clone(), settlement, expiry,
strike_fraction, strike_date, put_or_call, OptionSettlement::Cash).unwrap();
Arc::new(european)
}
pub fn create_sample_divstream() -> RcDividendStream {
let d = Date::from_ymd(2017, 01, 02);
let divs = [
Dividend::new(1.2, 0.0, d + 28, d + 30),
Dividend::new(0.8, 0.002, d + 210, d + 212),
Dividend::new(0.2, 0.008, d + 392, d + 394),
Dividend::new(0.0, 0.01, d + 574, d + 576)];
let points = [(d + 365 * 2, 0.002), (d + 365 * 3, 0.004),
(d + 365 * 5, 0.01), (d + 365 * 10, 0.015)];
let curve = RateCurveAct365::new(d + 365 * 2, &points,
Extrap::Zero, Extrap::Flat).unwrap();
let div_yield = RcRateCurve::new(Arc::new(curve));
RcDividendStream::new(Arc::new(DividendStream::new(&divs, div_yield)))
}
pub fn create_sample_rate() -> RcRateCurve {
let d = Date::from_ymd(2016, 12, 30);
let rate_points = [(d, 0.05), (d + 14, 0.08), (d + 182, 0.09),
(d + 364, 0.085), (d + 728, 0.082)];
RcRateCurve::new(Arc::new(RateCurveAct365::new(d, &rate_points,
Extrap::Flat, Extrap::Flat).unwrap()))
}
pub fn create_sample_borrow() -> RcRateCurve {
let d = Date::from_ymd(2016, 12, 30);
let borrow_points = [(d, 0.01), (d + 196, 0.012),
(d + 364, 0.0125), (d + 728, 0.012)];
RcRateCurve::new(Arc::new(RateCurveAct365::new(d, &borrow_points,
Extrap::Flat, Extrap::Flat).unwrap()))
}
pub fn create_sample_flat_vol() -> RcVolSurface {
let calendar = RcCalendar::new(Arc::new(WeekdayCalendar()));
let base_date = Date::from_ymd(2016, 12, 30);
let base = DateDayFraction::new(base_date, 0.2);
RcVolSurface::new(Arc::new(FlatVolSurface::new(0.3, calendar, base)))
}
pub fn sample_market_data() -> MarketData {
let spot_date = Date::from_ymd(2017, 01, 02);
let mut spots = HashMap::new();
spots.insert("BP.L".to_string(), 100.0);
spots.insert("GSK.L".to_string(), 200.0);
let mut dividends = HashMap::new();
dividends.insert("BP.L".to_string(), create_sample_divstream());
dividends.insert("GSK.L".to_string(), create_sample_divstream());
let mut yield_curves = HashMap::new();
yield_curves.insert("OPT".to_string(), create_sample_rate());
yield_curves.insert("LSE".to_string(), create_sample_rate());
let mut borrow_curves = HashMap::new();
borrow_curves.insert("BP.L".to_string(), create_sample_borrow());
borrow_curves.insert("GSK.L".to_string(), create_sample_borrow());
let mut vol_surfaces = HashMap::new();
vol_surfaces.insert("BP.L".to_string(), create_sample_flat_vol());
vol_surfaces.insert("GSK.L".to_string(), create_sample_flat_vol());
MarketData::new(spot_date, spots, yield_curves,
borrow_curves, dividends, vol_surfaces)
}
#[test]
fn european_unbumped_price() {
let market_data = sample_market_data();
let european = sample_european();
let val_date = DateTime::new(Date::from_ymd(2017, 01, 02), TimeOfDay::Open);
let price = european.price(&market_data, val_date).unwrap();
assert_approx(price, 16.710717400832973, 1e-12);
}
#[test]
fn european_bumped_price() {
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 mut mut_data = market_data.clone();
let mut save = SavedData::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);
}
#[test]
fn forward_european_tests() {
let market_data = sample_market_data();
let european = sample_forward_european();
let val_date = DateTime::new(Date::from_ymd(2017, 01, 02), TimeOfDay::Open);
let unbumped_price = european.price(&market_data, val_date).unwrap();
assert_approx(unbumped_price, 19.059001770739144, 1e-12);
let mut mut_data = market_data.clone();
let mut save = SavedData::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, 19.264143625005346, 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, 19.462049109434098, 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);
}
#[test]
fn serde_market_data_roundtrip() {
let market_data = sample_market_data();
let european = sample_european();
let val_date = DateTime::new(Date::from_ymd(2017, 01, 02), TimeOfDay::Open);
let price = european.price(&market_data, val_date).unwrap();
let serialized = serde_json::to_string_pretty(&market_data).unwrap();
print!("serialized: {}\n", serialized);
let deserialized: MarketData = serde_json::from_str(&serialized).unwrap();
let serde_price = european.price(&deserialized, val_date).unwrap();
assert_approx(serde_price, price, 1e-12);
}
fn assert_approx(value: f64, expected: f64, tolerance: f64) {
assert!(approx_eq(value, expected, tolerance),
"value={} expected={}", value, expected);
}
}