-
-
Notifications
You must be signed in to change notification settings - Fork 134
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore:
Curve
struct and impl, with placeholder modules for various …
…curves.
- Loading branch information
avhz
authored and
avhz
committed
Jul 14, 2024
1 parent
4cab13f
commit a3a4fee
Showing
26 changed files
with
634 additions
and
589 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// RustQuant: A Rust library for quantitative finance tools. | ||
// Copyright (C) 2023 https://github.com/avhz | ||
// Dual licensed under Apache 2.0 and MIT. | ||
// See: | ||
// - LICENSE-APACHE.md | ||
// - LICENSE-MIT.md | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
// use crate::error::RustQuantError; | ||
use crate::math::{ | ||
interpolation::{Interpolator, LinearInterpolator}, | ||
InterpolationIndex, | ||
}; | ||
use std::{collections::BTreeMap, hash::Hash}; | ||
|
||
/// Curve index trait. | ||
pub trait CurveIndex: Ord + Hash + InterpolationIndex + Clone + Copy {} | ||
impl<T> CurveIndex for T where T: Ord + Hash + InterpolationIndex + Clone + Copy {} | ||
|
||
/// Curve data structure. | ||
pub struct Curve<C> | ||
where | ||
C: CurveIndex, | ||
{ | ||
/// The nodes of the curve. | ||
pub nodes: BTreeMap<C, f64>, | ||
} | ||
|
||
macro_rules! impl_curve { | ||
($index:ty) => { | ||
impl Curve<$index> { | ||
/// Create a new curve. | ||
pub fn new() -> Self { | ||
Self { | ||
nodes: BTreeMap::new(), | ||
} | ||
} | ||
|
||
/// Get the first key in the curve. | ||
pub fn first_key(&self) -> Option<&$index> { | ||
self.nodes.keys().next() | ||
} | ||
|
||
/// Get the last key in the curve. | ||
pub fn last_key(&self) -> Option<&$index> { | ||
self.nodes.keys().next_back() | ||
} | ||
|
||
/// Get the first value in the curve. | ||
pub fn first_value(&self) -> Option<&f64> { | ||
self.nodes.values().next() | ||
} | ||
|
||
/// Get the last value in the curve. | ||
pub fn last_value(&self) -> Option<&f64> { | ||
self.nodes.values().next_back() | ||
} | ||
|
||
/// Add a node to the curve. | ||
pub fn insert(&mut self, index: $index, value: f64) { | ||
self.nodes.insert(index, value); | ||
} | ||
|
||
/// Get a value for a specific index. | ||
pub fn get(&self, index: $index) -> Option<&f64> { | ||
self.nodes.get(&index) | ||
} | ||
|
||
/// Get a mutable reference to a value for a specific index. | ||
pub fn get_mut(&mut self, index: $index) -> Option<&mut f64> { | ||
self.nodes.get_mut(&index) | ||
} | ||
|
||
/// Create a Curve from a vector of indices and values. | ||
pub fn new_from_slice(indices: &[$index], values: &[f64]) -> Self { | ||
let mut curve = Self::new(); | ||
|
||
for (index, value) in indices.iter().zip(values.iter()) { | ||
curve.insert(*index, *value); | ||
} | ||
|
||
curve | ||
} | ||
|
||
/// Create a Curve from a function. | ||
pub fn new_from_function<F>(f: F, indices: &[$index]) -> Self | ||
where | ||
F: Fn($index) -> f64, | ||
{ | ||
let mut curve = Self::new(); | ||
|
||
for index in indices { | ||
curve.insert(*index, f(*index)); | ||
} | ||
|
||
curve | ||
} | ||
|
||
/// Create a Curve from a constant value. | ||
pub fn new_from_constant(value: f64, indices: &[$index]) -> Self { | ||
let mut curve = Self::new(); | ||
|
||
for index in indices { | ||
curve.insert(*index, value); | ||
} | ||
|
||
curve | ||
} | ||
|
||
/// Get the bracketing indices for a specific index. | ||
pub fn get_brackets(&self, index: $index) -> ($index, $index) { | ||
let first = self.first_key().unwrap(); | ||
let last = self.last_key().unwrap(); | ||
|
||
if index <= *first { | ||
return (*first, *first); | ||
} | ||
|
||
if index >= *last { | ||
return (*last, *last); | ||
} | ||
|
||
let left = self.nodes.range(..index).next_back().unwrap().0; | ||
let right = self.nodes.range(index..).next().unwrap().0; | ||
|
||
return (*left, *right); | ||
} | ||
|
||
/// Shift the curve by a constant value. | ||
pub fn shift(&mut self, shift: f64) { | ||
for value in self.nodes.values_mut() { | ||
*value += shift; | ||
} | ||
} | ||
|
||
/// Interpolate the curve at a specific index. | ||
/// | ||
/// Note: This method modifies the curve by adding the interpolated value. | ||
pub fn interpolate(&mut self, index: $index) { | ||
let xs: Vec<$index> = self.nodes.keys().cloned().collect(); | ||
let ys: Vec<f64> = self.nodes.values().cloned().collect(); | ||
|
||
let interpolator = LinearInterpolator::new(xs, ys).unwrap(); | ||
|
||
self.insert(index, interpolator.interpolate(index).unwrap()); | ||
} | ||
|
||
/// Interpolate the curve at multiple indices. | ||
/// | ||
/// Note: This method modifies the curve by adding the interpolated values. | ||
pub fn interpolate_many(&mut self, indices: &[$index]) { | ||
let xs: Vec<$index> = self.nodes.keys().cloned().collect(); | ||
let ys: Vec<f64> = self.nodes.values().cloned().collect(); | ||
|
||
let interpolator = LinearInterpolator::new(xs, ys).unwrap(); | ||
|
||
for index in indices { | ||
self.insert(*index, interpolator.interpolate(*index).unwrap()); | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
|
||
// Implement the Curve for temporal types. | ||
impl_curve!(time::Date); | ||
impl_curve!(time::Time); | ||
impl_curve!(time::OffsetDateTime); | ||
impl_curve!(time::PrimitiveDateTime); | ||
|
||
// THE FOLLOWING CANNOT BE IMPLEMENTED DUE TO RESTRICTIONS WITHIN THE INTERPOLATION MODULE. | ||
|
||
// Implement the Curve for unsigned integer types. | ||
// impl_curve!(u64); | ||
// impl_curve!(u32); | ||
// impl_curve!(u16); | ||
// impl_curve!(u8); | ||
// impl_curve!(usize); | ||
|
||
// Implement the Curve for signed integer types. | ||
// impl_curve!(i64); | ||
// impl_curve!(i32); | ||
// impl_curve!(i16); | ||
// impl_curve!(i8); | ||
// impl_curve!(isize); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// RustQuant: A Rust library for quantitative finance tools. | ||
// Copyright (C) 2023 https://github.com/avhz | ||
// Dual licensed under Apache 2.0 and MIT. | ||
// See: | ||
// - LICENSE-APACHE.md | ||
// - LICENSE-MIT.md | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
use time::Date; | ||
|
||
/// A trait for curve models. | ||
#[allow(clippy::module_name_repetitions)] | ||
pub trait CurveModel { | ||
/// Returns the forward rate for a given date. | ||
fn forward_rate(&self, date: Date) -> f64; | ||
|
||
/// Returns the spot rate for a given date. | ||
fn spot_rate(&self, date: Date) -> f64; | ||
|
||
/// Returns the discount factor for a given date. | ||
fn discount_factor(&self, date: Date) -> f64; | ||
|
||
// /// Calibrates the model to a set of market rates. | ||
// #[must_use] | ||
// fn calibrate<C: Curve>(&self, curve: C) -> Self; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// RustQuant: A Rust library for quantitative finance tools. | ||
// Copyright (C) 2023 https://github.com/avhz | ||
// Dual licensed under Apache 2.0 and MIT. | ||
// See: | ||
// - LICENSE-APACHE.md | ||
// - LICENSE-MIT.md | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
/// Base trait for all curves to implement. | ||
pub trait Curve { | ||
/// Initial date of the curve. | ||
fn initial_date(&self) -> Date; | ||
|
||
/// Final date of the curve. | ||
fn terminal_date(&self) -> Date; | ||
|
||
/// Updates the rate for the given date. | ||
fn update_rate(&mut self, date: Date, rate: f64); | ||
|
||
/// Create a new curve from a set of dates and rates. | ||
fn from_dates_and_rates(dates: &[Date], rates: &[f64]) -> Self; | ||
|
||
/// Create a new curve from an initial date, a set of rates, and a set of | ||
/// durations. | ||
/// The dates are calculated as the initial date plus the duration, thus | ||
/// there must be: | ||
/// - One initial date | ||
/// - n rates | ||
/// - n-1 durations | ||
fn from_initial_date_rates_and_durations( | ||
initial_date: Date, | ||
rates: &[f64], | ||
durations: &[Duration], | ||
) -> Self; | ||
|
||
/// Function to find the interval of dates that contains the given date. | ||
/// The interval is defined by the two dates that are closest to the given | ||
/// date, just before and just after. | ||
fn find_date_interval(&self, date: Date) -> (Date, Date); | ||
|
||
/// Returns the rate for the given date, using linear interpolation for | ||
/// dates between the curve's initial and terminal dates. | ||
/// If the date is outside the curve's range, we panic. | ||
/// | ||
/// We use the following formula for the interpolation: | ||
/// | ||
/// $$ | ||
/// y = \frac{y_0 (x_1 - x) + y_1 (x - x_0)}{x_1 - x_0} | ||
/// $$ | ||
/// | ||
/// Note: there must be at least two points in the curve, otherwise | ||
/// we consider the curve to be a flat rate, and return the same rate | ||
/// for all dates. | ||
fn rate(&self, date: Date) -> f64; | ||
|
||
/// Returns the discount factor for the given date. | ||
/// This is a convenience function that calls [`rate`](Curve::rate) to get the rate for | ||
/// the given date, and then calculates the discount factor using the | ||
/// formula: | ||
/// $$ | ||
/// p(t) = e^{- r \cdot t} | ||
/// $$ | ||
fn discount_factor(&self, date: Date) -> f64 { | ||
let t = DayCountConvention::default().day_count_factor(self.initial_date(), date); | ||
|
||
f64::exp(-self.rate(date) * t) | ||
} | ||
|
||
/// Returns multiple discount factors for the given dates. | ||
/// This is a convenience function that calls [`discount_factor`](Curve::discount_factor) for each | ||
/// date. | ||
fn discount_factors(&self, dates: &[Date]) -> Vec<f64> { | ||
dates | ||
.iter() | ||
.map(|date| self.discount_factor(*date)) | ||
.collect::<Vec<f64>>() | ||
} | ||
} | ||
|
||
#[allow(clippy::module_name_repetitions)] | ||
/// Yield curve struct. | ||
pub struct YieldCurve { | ||
/// Map of dates and rates. | ||
/// The dates are the keys and the rates are the values. | ||
/// The reason for using a [BTreeMap] is that it is sorted by date, | ||
/// which makes sense for a term structure. | ||
pub rates: BTreeMap<Date, f64>, | ||
// /// A model for the curve. | ||
// pub model: Option<M>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// RustQuant: A Rust library for quantitative finance tools. | ||
// Copyright (C) 2023 https://github.com/avhz | ||
// Dual licensed under Apache 2.0 and MIT. | ||
// See: | ||
// - LICENSE-APACHE.md | ||
// - LICENSE-MIT.md | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// RustQuant: A Rust library for quantitative finance tools. | ||
// Copyright (C) 2023 https://github.com/avhz | ||
// Dual licensed under Apache 2.0 and MIT. | ||
// See: | ||
// - LICENSE-APACHE.md | ||
// - LICENSE-MIT.md | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// RustQuant: A Rust library for quantitative finance tools. | ||
// Copyright (C) 2023 https://github.com/avhz | ||
// Dual licensed under Apache 2.0 and MIT. | ||
// See: | ||
// - LICENSE-APACHE.md | ||
// - LICENSE-MIT.md | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
// RustQuant: A Rust library for quantitative finance tools. | ||
// Copyright (C) 2023 https://github.com/avhz | ||
// Dual licensed under Apache 2.0 and MIT. | ||
// See: | ||
// - LICENSE-APACHE.md | ||
// - LICENSE-MIT.md | ||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
/// Basic curve data structure. | ||
pub mod curve; | ||
pub use curve::*; | ||
|
||
/// Curve model trait. | ||
pub mod curve_model; | ||
pub use curve_model::*; |
File renamed without changes.
File renamed without changes.
Oops, something went wrong.