use crate::error::{DigiFiError, ErrorTitle};
use crate::utilities::time_value_utils::{CompoundingType, Compounding, forward_rate};
use crate::financial_instruments::{FinancialInstrument, FinancialInstrumentId};
use crate::portfolio_applications::{AssetHistData, PortfolioInstrument};
use crate::stochastic_processes::StochasticProcess;
pub struct ForwardRateAgreement {
agreed_fixed_rate: f64,
current_forward_rate: f64,
time_to_maturity: f64,
principal: f64,
initial_price: f64,
compounding_type: CompoundingType,
financial_instrument_id: FinancialInstrumentId,
asset_historical_data: AssetHistData,
stochastic_model: Option<Box<dyn StochasticProcess>>,
}
impl ForwardRateAgreement {
pub fn new(
agreed_fixed_rate: f64, current_forward_rate: f64, time_to_maturity: f64, principal: f64, initial_price: f64,
compounding_type: CompoundingType, financial_instrument_id: FinancialInstrumentId, asset_historical_data: AssetHistData,
stochastic_model: Option<Box<dyn StochasticProcess>>
) -> Self {
Self {
agreed_fixed_rate, current_forward_rate, time_to_maturity, principal, initial_price, compounding_type, financial_instrument_id,
asset_historical_data, stochastic_model: stochastic_model,
}
}
pub fn update_forward_rate(&mut self, new_forward_rate: f64) -> () {
self.current_forward_rate = new_forward_rate;
}
pub fn latest_time_maturity(&mut self, new_time_to_maturity: f64) -> () {
self.time_to_maturity = new_time_to_maturity;
}
pub fn future_rate(&self, time_1: f64, future_compounding_term: &Compounding, time_2: f64) -> Result<f64, DigiFiError> {
let current_compounding_term: Compounding = Compounding::new(self.current_forward_rate, &self.compounding_type);
forward_rate(¤t_compounding_term, time_1, future_compounding_term, time_2)
}
pub fn rate_adjustment(&mut self, futures_rate: f64, convexity_adjustment: f64, in_place: bool) -> Result<f64, DigiFiError> {
if convexity_adjustment <= 0.0 {
return Err(DigiFiError::ParameterConstraint { title: Self::error_title(), constraint: "The argument `convexity_adjustment` must be positive.".to_owned(), });
}
let forward_rate: f64 = futures_rate - convexity_adjustment;
if in_place {
self.current_forward_rate = forward_rate;
}
Ok(forward_rate)
}
}
impl ErrorTitle for ForwardRateAgreement {
fn error_title() -> String {
String::from("Forward Rate Agreement")
}
}
impl FinancialInstrument for ForwardRateAgreement {
fn present_value(&self) -> Result<f64, DigiFiError> {
let discount_term: Compounding = Compounding::new(self.current_forward_rate, &self.compounding_type);
Ok((self.time_to_maturity * (self.current_forward_rate - self.agreed_fixed_rate) * self.principal) * discount_term.compounding_term(self.time_to_maturity))
}
fn net_present_value(&self) -> Result<f64, DigiFiError> {
Ok(-self.initial_price + self.present_value()?)
}
fn future_value(&self) -> Result<f64, DigiFiError> {
let discount_term: Compounding = Compounding::new(self.current_forward_rate, &self.compounding_type);
Ok(self.present_value()? / discount_term.compounding_term(self.time_to_maturity))
}
fn historical_data(&self) -> &AssetHistData {
&self.asset_historical_data
}
fn update_historical_data(&mut self, new_data: &AssetHistData) -> () {
self.asset_historical_data = new_data.clone();
}
fn stochastic_model(&mut self) -> &mut Option<Box<dyn StochasticProcess>> {
&mut self.stochastic_model
}
}
impl PortfolioInstrument for ForwardRateAgreement {
fn asset_name(&self) -> String {
self.financial_instrument_id.identifier.clone()
}
fn historical_data(&self) -> &AssetHistData {
&self.asset_historical_data
}
}
#[cfg(test)]
mod tests {
use ndarray::Array1;
use crate::utilities::TEST_ACCURACY;
#[test]
fn unit_test_forward_rate() -> () {
use crate::utilities::{Time, time_value_utils::CompoundingType};
use crate::financial_instruments::{FinancialInstrument, FinancialInstrumentId, FinancialInstrumentType, AssetClass};
use crate::financial_instruments::rates_and_swaps::ForwardRateAgreement;
use crate::portfolio_applications::AssetHistData;
let compounding_type: CompoundingType = CompoundingType::Continuous;
let financial_instrument_id: FinancialInstrumentId = FinancialInstrumentId {
instrument_type: FinancialInstrumentType::DerivativeInstrument, asset_class: AssetClass::ForeignExchangeInstrument,
identifier: String::from("32198407128904"),
};
let asset_historical_data: AssetHistData = AssetHistData::build(
Array1::from_vec(vec![0.4, 0.5]), Array1::from_vec(vec![0.0, 0.0]), Time::new(Array1::from_vec(vec![0.0, 1.0]))
).unwrap();
let forward_rate: ForwardRateAgreement = ForwardRateAgreement::new(
0.04, 0.05, 1.0, 1000.0, 10.0, compounding_type, financial_instrument_id, asset_historical_data, None
);
assert!((forward_rate.present_value().unwrap() - 1.0*0.01*1000.0*(-0.05*1.0_f64).exp()).abs() < TEST_ACCURACY);
}
}