use dates::Date;
use dates::rules::RcDateRule;
use math::interpolation::Interpolate;
use data::divstream::DividendBootstrap;
use data::divstream::DividendStream;
use data::curves::RcRateCurve;
use data::curves::RateCurve;
use core::qm;
pub trait Forward : Interpolate<Date> + Sync + Send {
fn as_interp(&self) -> &Interpolate<Date>;
fn forward(&self, date: Date) -> Result<f64, qm::Error>;
fn fixed_divs_after(&self, _date: Date) -> Result<f64, qm::Error> {
Ok(0.0)
}
}
impl<T> Interpolate<Date> for T where T: Forward {
fn interpolate(&self, date: Date) -> Result<f64, qm::Error> {
self.forward(date)
}
}
pub struct DriftlessForward {
value: f64
}
impl Forward for DriftlessForward {
fn as_interp(&self) -> &Interpolate<Date> { self }
fn forward(&self, _date: Date) -> Result<f64, qm::Error> {
Ok(self.value)
}
}
impl DriftlessForward {
pub fn new(value: f64) -> DriftlessForward {
DriftlessForward { value: value }
}
}
pub struct InterpolatedForward {
interp: Box<Interpolate<Date>>
}
impl Forward for InterpolatedForward {
fn as_interp(&self) -> &Interpolate<Date> { self }
fn forward(&self, date: Date) -> Result<f64, qm::Error> {
self.interp.interpolate(date)
}
}
impl InterpolatedForward {
pub fn new(interp: Box<Interpolate<Date>>) -> InterpolatedForward {
InterpolatedForward { interp: interp }
}
}
pub struct EquityForward {
settlement: RcDateRule,
rate: RcRateCurve,
borrow: RcRateCurve,
div_yield: RcRateCurve,
bootstrap: DividendBootstrap,
reference_spot: f64,
base_log_discount: f64
}
impl Forward for EquityForward {
fn as_interp(&self) -> &Interpolate<Date> { self }
fn forward(&self, date: Date) -> Result<f64, qm::Error> {
let divs = self.bootstrap.discounted_sum_from_base(date)?;
let pay_date = self.settlement.apply(date);
let log_df = log_discount_with_borrow(&*self.rate, &*self.borrow,
pay_date)?;
let log_div_yield = self.div_yield.rt(pay_date)?;
let growth = (self.base_log_discount + log_div_yield - log_df).exp();
Ok((self.reference_spot - divs) * growth)
}
fn fixed_divs_after(&self, date: Date) -> Result<f64, qm::Error> {
self.bootstrap.discounted_cash_divs_after(date)
}
}
impl EquityForward {
pub fn new(
base_date: Date,
spot: f64,
settlement: RcDateRule,
rate: RcRateCurve,
borrow: RcRateCurve,
divs: &DividendStream,
high_water_mark: Date) -> Result<EquityForward, qm::Error> {
let base_log_discount = log_discount_with_borrow(
&*rate, &*borrow, base_date)?;
let spot_date = settlement.apply(base_date);
let spot_df = discount_with_borrow(
&*rate, &*borrow, base_log_discount, spot_date)?;
let reference_spot = spot * spot_df;
let bootstrap = DividendBootstrap::new(&divs, &*rate, &*borrow,
reference_spot, base_date, high_water_mark)?;
Ok(EquityForward {
settlement: settlement,
rate: rate,
borrow: borrow,
div_yield: divs.div_yield(),
bootstrap: bootstrap,
reference_spot: reference_spot,
base_log_discount: base_log_discount })
}
}
pub fn discount_with_borrow(rate: &RateCurve, borrow: &RateCurve,
base_qt_minus_rt: f64, date: Date) -> Result<f64, qm::Error> {
let log_discount = log_discount_with_borrow(rate, borrow, date)?;
Ok((log_discount - base_qt_minus_rt).exp())
}
pub fn log_discount_with_borrow(rate: &RateCurve, borrow: &RateCurve,
date: Date) -> Result<f64, qm::Error> {
let rt = rate.rt(date)?;
let qt = borrow.rt(date)?;
Ok(qt - rt)
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use math::numerics::approx_eq;
use math::interpolation::Extrap;
use math::interpolation::CubicSpline;
use data::curves::RateCurveAct365;
use data::divstream::Dividend;
use dates::calendar::WeekdayCalendar;
use dates::rules::BusinessDays;
use dates::calendar::RcCalendar;
#[test]
fn driftless_forward() {
let d = Date::from_ymd(2018, 05, 25);
let fwd = DriftlessForward::new(123.4);
assert_match(fwd.forward(d), 123.4);
assert_match(fwd.forward(d+100), 123.4);
}
#[test]
fn interpolated_forward() {
let d = Date::from_ymd(2018, 05, 25);
let points = [(d, 100.0), (d+30, 103.0), (d+60, 97.0), (d+90, 99.0),
(d+120, 105.0)];
let cs = Box::new(CubicSpline::new(&points,
Extrap::Natural, Extrap::Natural).unwrap());
let fwd = InterpolatedForward::new(cs);
assert_match(fwd.forward(d), 100.0);
assert_match(fwd.forward(d+30), 103.0);
assert_match(fwd.forward(d+60), 97.0);
assert_match(fwd.forward(d+90), 99.0);
assert_match(fwd.forward(d+120), 105.0);
}
#[test]
fn equity_forward() {
let d = Date::from_ymd(2017, 01, 02);
let spot = 97.0;
let divs = create_sample_divstream();
let rate = create_sample_rate();
let borrow = create_sample_borrow();
let calendar = RcCalendar::new(Arc::new(WeekdayCalendar{}));
let settlement = RcDateRule::new(Arc::new(BusinessDays::new_step(calendar, 2)));
let fwd = EquityForward::new(d, spot, settlement, rate, borrow, &divs,
d + 1500).unwrap();
assert_match(fwd.forward(d), 97.0);
assert_match(fwd.forward(d+27), 97.55511831033844);
assert_match(fwd.forward(d+28), 96.35511831033844);
assert_match(fwd.forward(d+60), 97.02249458204768);
assert_match(fwd.forward(d+90), 97.61947501213612);
assert_match(fwd.forward(d+120), 98.242454295387);
assert_match(fwd.forward(d+150), 98.96059291192566);
assert_match(fwd.forward(d+180), 99.64145192620384);
assert_match(fwd.forward(d+209), 100.18493401723067);
assert_match(fwd.forward(d+210), 99.18464159368386);
assert_match(fwd.forward(d+240), 99.75346887674715);
assert_match(fwd.forward(d+270), 100.34720455926485);
assert_match(fwd.forward(d+300), 100.87344226359396);
assert_match(fwd.forward(d+600), 104.51748569914179);
assert_match(fwd.forward(d+900), 110.7100396163593);
assert_match(fwd.forward(d+1200), 117.8483691785027);
assert_match(fwd.forward(d+1500), 125.93011849243018);
}
fn create_sample_divstream() -> DividendStream {
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));
DividendStream::new(&divs, div_yield)
}
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()))
}
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()))
}
fn assert_match(result: Result<f64, qm::Error>, expected: f64) {
let v = result.unwrap();
assert!(approx_eq(v, expected, 1e-12),
"result={} expected={}", v, expected);
}
}