Skip to content

Commit

Permalink
feat: add hull_white and hull_white_2f
Browse files Browse the repository at this point in the history
  • Loading branch information
dancixx committed Oct 2, 2024
1 parent 5c851ed commit 40f9e28
Show file tree
Hide file tree
Showing 41 changed files with 478 additions and 159 deletions.
6 changes: 3 additions & 3 deletions src/ai/fou/fou_lstm_datasets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use ndarray::{s, Array1};
use ndarray_rand::RandomExt;
use rand_distr::Uniform;

use crate::stochastic::{diffusion::fou::Fou, Sampling};
use crate::stochastic::{diffusion::fou::FOU, Sampling};

pub fn test_vasicek_1_d(
epoch_size: usize,
Expand All @@ -34,7 +34,7 @@ pub fn test_vasicek_1_d(
for idx in 0..epoch_size {
let hurst = hursts[idx];
let theta = thetas[idx];
let fou = Fou::new(&Fou {
let fou = FOU::new(&FOU {
hurst,
mu,
sigma,
Expand Down Expand Up @@ -88,7 +88,7 @@ pub fn test_vasicek_2_d(
for idx in 0..epoch_size {
let hurst = hursts[idx];
let theta = thetas[idx];
let fou = Fou::new(&Fou {
let fou = FOU::new(&FOU {
hurst,
mu,
sigma,
Expand Down
6 changes: 3 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use stochastic_rs::stochastic::{noise::fgn::Fgn, Sampling};
use stochastic_rs::stochastic::{noise::fgn::FGN, Sampling};

fn main() {
let fbm = Fgn::new(0.9, 10000, None, Some(10000));
let fbm = FGN::new(0.9, 10000, None, Some(10000));

let start = std::time::Instant::now();
for _ in 0..10000 {
Expand All @@ -14,7 +14,7 @@ fn main() {
duration.as_secs_f32()
);

let fbm = Fgn::new(0.9, 10000, None, Some(10000));
let fbm = FGN::new(0.9, 10000, None, Some(10000));

let start = std::time::Instant::now();
for _ in 0..10000 {
Expand Down
2 changes: 2 additions & 0 deletions src/quant.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pub mod bonds;
pub mod options;
pub mod r#trait;
pub mod volatility;
pub mod yahoo;

Expand Down
3 changes: 3 additions & 0 deletions src/quant/bonds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod cir;
pub mod hull_white;
pub mod vasicek;
40 changes: 40 additions & 0 deletions src/quant/bonds/cir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::quant::r#trait::Price;

/// CIR model for zero-coupon bond pricing
/// dR(t) = theta(mu - R(t))dt + sigma * sqrt(R(t))dW(t)
/// where R(t) is the short rate.
#[derive(Default, Debug)]
pub struct CIR {
/// Initial short rate
pub r_t: f64,
/// Long-term mean of the short rate
pub theta: f64,
/// Mean reversion speed
pub mu: f64,
/// Volatility
pub sigma: f64,
/// Maturity of the bond in days
pub tau: f64,
/// Evaluation date
pub eval: Option<chrono::NaiveDate>,
/// Expiration date
pub expiration: Option<chrono::NaiveDate>,
}

impl Price for CIR {
fn price(&self) -> f64 {
todo!()
}

fn tau(&self) -> Option<f64> {
Some(self.tau)
}

fn eval(&self) -> Option<chrono::NaiveDate> {
self.eval
}

fn expiration(&self) -> Option<chrono::NaiveDate> {
self.expiration
}
}
40 changes: 40 additions & 0 deletions src/quant/bonds/hull_white.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::quant::r#trait::Price;

/// Hull-White model for zero-coupon bond pricing
/// dR(t) = (theta(t) - aR(t))dt + sigma(t)dW(t)
/// where R(t) is the short rate.
#[derive(Debug)]
pub struct HullWhite {
/// Short rate
pub r_t: f64,
/// Long-term mean of the short rate
pub theta: fn(f64) -> f64,
/// Mean reversion speed
pub alpha: f64,
/// Volatility
pub sigma: f64,
/// Maturity of the bond in days
pub tau: f64,
/// Evaluation date
pub eval: Option<chrono::NaiveDate>,
/// Expiration date
pub expiration: Option<chrono::NaiveDate>,
}

impl Price for HullWhite {
fn price(&self) -> f64 {
0.0
}

fn tau(&self) -> Option<f64> {
Some(self.tau)
}

fn eval(&self) -> Option<chrono::NaiveDate> {
self.eval
}

fn expiration(&self) -> Option<chrono::NaiveDate> {
self.expiration
}
}
42 changes: 42 additions & 0 deletions src/quant/bonds/vasicek.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crate::quant::r#trait::Price;

/// Vasicek model for zero-coupon bond pricing
/// dR(t) = theta(mu - R(t))dt + sigma dW(t)
/// where R(t) is the short rate.
#[derive(Default, Debug)]
pub struct Vasicek {
/// Short rate
pub r_t: f64,
/// Long-term mean of the short rate
pub theta: f64,
/// Mean reversion speed
pub mu: f64,
/// Volatility
pub sigma: f64,
/// Maturity of the bond in days
pub tau: f64,
/// Evaluation date
pub eval: Option<chrono::NaiveDate>,
/// Expiration date
pub expiration: Option<chrono::NaiveDate>,
}

impl Price for Vasicek {
fn price(&self) -> f64 {
// Itt definiálhatod a Vasicek modell árképzési képletét
// Placeholder érték visszaadása
0.0
}

fn tau(&self) -> Option<f64> {
Some(self.tau)
}

fn eval(&self) -> Option<chrono::NaiveDate> {
self.eval
}

fn expiration(&self) -> Option<chrono::NaiveDate> {
self.expiration
}
}
66 changes: 40 additions & 26 deletions src/quant/options/bsm.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use statrs::distribution::{Continuous, ContinuousCDF, Normal};

use crate::quant::OptionType;
use crate::quant::{r#trait::Price, OptionType};

#[derive(Default, Debug, Clone, Copy)]
pub enum BsmCoc {
pub enum BSMCoc {
/// Black-Scholes-Merton 1973 (stock option)
/// Cost of carry = risk-free rate
#[default]
Expand All @@ -24,7 +24,7 @@ pub enum BsmCoc {

/// Black-Scholes-Merton model
#[derive(Default, Debug)]
pub struct Bsm {
pub struct BSM {
/// Underlying price
pub s: f64,
/// Volatility
Expand All @@ -48,10 +48,40 @@ pub struct Bsm {
/// Option type
pub option_type: OptionType,
/// Cost of carry
pub b: BsmCoc,
pub b: BSMCoc,
}

impl Bsm {
impl Price for BSM {
/// Calculate the option price
#[must_use]
fn price(&self) -> f64 {
let (d1, d2) = self.d1_d2();
let n = Normal::default();
let tau = self.tau();

if self.option_type == OptionType::Call {
self.s * ((self.b() - self.r) * tau).exp() * n.cdf(d1)
- self.k * (-self.r * tau).exp() * n.cdf(d2)
} else {
-self.s * ((self.b() - self.r) * tau).exp() * n.cdf(-d1)
+ self.k * (-self.r * tau).exp() * n.cdf(-d2)
}
}

fn tau(&self) -> Option<f64> {
self.tau
}

fn eval(&self) -> Option<chrono::NaiveDate> {
self.eval
}

fn expiration(&self) -> Option<chrono::NaiveDate> {
self.expiration
}
}

impl BSM {
/// Create a new BSM model
#[must_use]
pub fn new(params: &Self) -> Self {
Expand Down Expand Up @@ -85,22 +115,6 @@ impl Bsm {
}
}

/// Calculate the option price
#[must_use]
pub fn price(&mut self) -> f64 {
let (d1, d2) = self.d1_d2();
let n = Normal::default();
let tau = self.tau();

if self.option_type == OptionType::Call {
self.s * ((self.b() - self.r) * tau).exp() * n.cdf(d1)
- self.k * (-self.r * tau).exp() * n.cdf(d2)
} else {
-self.s * ((self.b() - self.r) * tau).exp() * n.cdf(-d1)
+ self.k * (-self.r * tau).exp() * n.cdf(-d2)
}
}

/// Calculate d1
fn d1_d2(&self) -> (f64, f64) {
let d1 = (1.0 / (self.v * self.tau().sqrt()))
Expand All @@ -113,11 +127,11 @@ impl Bsm {
/// Calculate b (cost of carry)
fn b(&self) -> f64 {
match self.b {
BsmCoc::BSM1973 => self.r,
BsmCoc::MERTON1973 => self.r - self.q.unwrap(),
BsmCoc::BLACK1976 => 0.0,
BsmCoc::ASAY1982 => 0.0,
BsmCoc::GARMAN1983 => self.r_d.unwrap() - self.r_f.unwrap(),
BSMCoc::BSM1973 => self.r,
BSMCoc::MERTON1973 => self.r - self.q.unwrap(),
BSMCoc::BLACK1976 => 0.0,
BSMCoc::ASAY1982 => 0.0,
BSMCoc::GARMAN1983 => self.r_d.unwrap() - self.r_f.unwrap(),
}
}

Expand Down
52 changes: 52 additions & 0 deletions src/quant/trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use chrono::Local;
use nalgebra::DVector;

/// Pricer trait.
pub(crate) trait Pricer {
/// Calculate the price of an option.
fn calculate_price(&mut self);
/// Update the parameters.
fn update_params(&mut self, params: DVector<f64>);
/// Update strike price.
fn update_strike(&mut self, k: f64);
/// Prices.
fn prices(&self) -> (f64, f64);
/// Derivatives.
fn derivates(&self) -> Vec<f64>;
}

/// Price an instrument.
pub trait Price {
/// Calculate the price of an instrument.
fn price(&self) -> f64;

/// Calculate the valuation date of an instrument.
fn calculate_tau_in_days(&self) -> f64 {
if let Some(tau) = self.tau() {
tau
} else {
let eval = self
.eval()
.unwrap_or_else(|| Local::now().naive_local().into());
let expiration = self.expiration().unwrap();
(expiration - eval).num_days() as f64
}
}

/// Calculate the valuation date of an instrument.
fn calculate_tau_in_years(&self) -> f64 {
if let Some(tau) = self.tau() {
tau
} else {
let eval = self
.eval()
.unwrap_or_else(|| Local::now().naive_local().into());
let expiration = self.expiration().unwrap();
(expiration - eval).num_days() as f64 / 365.0
}
}

fn tau(&self) -> Option<f64>;
fn eval(&self) -> Option<chrono::NaiveDate>;
fn expiration(&self) -> Option<chrono::NaiveDate>;
}
16 changes: 1 addition & 15 deletions src/quant/volatility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,7 @@ use std::cell::RefCell;
use levenberg_marquardt::LeastSquaresProblem;
use nalgebra::{DMatrix, DVector, Dyn, Owned};

use super::OptionType;

/// Pricer trait.
pub(crate) trait Pricer {
/// Calculate the price of an option.
fn calculate_price(&mut self);
/// Update the parameters.
fn update_params(&mut self, params: DVector<f64>);
/// Update strike price.
fn update_strike(&mut self, k: f64);
/// Prices.
fn prices(&self) -> (f64, f64);
/// Derivatives.
fn derivates(&self) -> Vec<f64>;
}
use super::{r#trait::Pricer, OptionType};

/// A calibrator.
pub(crate) struct Calibrator<'a, P>
Expand Down
4 changes: 1 addition & 3 deletions src/quant/volatility/heston.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ use num_complex::Complex64;
use quadrature::double_exponential;

use crate::{
quant::{volatility::Calibrator, OptionType},
quant::{r#trait::Pricer, volatility::Calibrator, OptionType},
stats::mle::nmle_heston,
};

use super::Pricer;

#[derive(Default, Clone)]
pub struct HestonPricer {
/// Initial stock price
Expand Down
Loading

0 comments on commit 40f9e28

Please sign in to comment.