Skip to content

Commit 81760f2

Browse files
committed
feat: add KOU model
1 parent afc456b commit 81760f2

File tree

4 files changed

+164
-0
lines changed

4 files changed

+164
-0
lines changed

src/stats.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod cir;
2+
pub mod double_exp;
23
pub mod fd;
34
pub mod mle;
45
pub mod non_central_chi_squared;

src/stats/double_exp.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use impl_new_derive::ImplNew;
2+
use rand::Rng;
3+
use rand_distr::Distribution;
4+
5+
#[derive(ImplNew)]
6+
pub struct DoubleExp {
7+
pub p: Option<f64>,
8+
pub lambda_plus: f64,
9+
pub lambda_minus: f64,
10+
}
11+
12+
impl Distribution<f64> for DoubleExp {
13+
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
14+
let u = rng.gen::<f64>();
15+
16+
if u < self.p.unwrap_or(0.5) {
17+
-rng.gen::<f64>().ln() / self.lambda_plus
18+
} else {
19+
rng.gen::<f64>().ln() / self.lambda_minus
20+
}
21+
}
22+
}

src/stochastic/jump.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub mod cgmy;
33
pub mod cts;
44
pub mod ig;
55
pub mod jump_fou;
6+
pub mod kou;
67
pub mod levy_diffusion;
78
pub mod merton;
89
pub mod nig;

src/stochastic/jump/kou.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
use impl_new_derive::ImplNew;
2+
use ndarray::Array1;
3+
use ndarray_rand::RandomExt;
4+
use rand_distr::{Distribution, Normal};
5+
6+
use crate::stochastic::{process::cpoisson::CompoundPoisson, Sampling, Sampling3D};
7+
8+
/// Kou process
9+
///
10+
/// https://www.columbia.edu/~sk75/MagSci02.pdf
11+
///
12+
#[derive(ImplNew)]
13+
pub struct KOU<D>
14+
where
15+
D: Distribution<f64> + Send + Sync,
16+
{
17+
pub alpha: f64,
18+
pub sigma: f64,
19+
pub lambda: f64,
20+
pub theta: f64,
21+
pub n: usize,
22+
pub x0: Option<f64>,
23+
pub t: Option<f64>,
24+
pub m: Option<usize>,
25+
pub cpoisson: CompoundPoisson<D>,
26+
}
27+
28+
impl<D> Sampling<f64> for KOU<D>
29+
where
30+
D: Distribution<f64> + Send + Sync,
31+
{
32+
fn sample(&self) -> Array1<f64> {
33+
let dt = self.t.unwrap_or(1.0) / (self.n - 1) as f64;
34+
let mut merton = Array1::<f64>::zeros(self.n);
35+
merton[0] = self.x0.unwrap_or(0.0);
36+
let gn = Array1::random(self.n - 1, Normal::new(0.0, dt.sqrt()).unwrap());
37+
38+
for i in 1..self.n {
39+
let [.., jumps] = self.cpoisson.sample();
40+
41+
merton[i] = merton[i - 1]
42+
+ (self.alpha * self.sigma.powf(2.0) / 2.0 - self.lambda * self.theta) * dt
43+
+ self.sigma * gn[i - 1]
44+
+ jumps.sum();
45+
}
46+
47+
merton
48+
}
49+
50+
/// Number of time steps
51+
fn n(&self) -> usize {
52+
self.n
53+
}
54+
55+
/// Number of samples for parallel sampling
56+
fn m(&self) -> Option<usize> {
57+
self.m
58+
}
59+
}
60+
61+
#[cfg(test)]
62+
mod tests {
63+
use crate::{
64+
plot_1d,
65+
stats::double_exp::DoubleExp,
66+
stochastic::{process::poisson::Poisson, N, S0, X0},
67+
};
68+
69+
use super::*;
70+
71+
#[test]
72+
fn kou_length_equals_n() {
73+
let kou = KOU::new(
74+
2.25,
75+
2.5,
76+
1.0,
77+
1.0,
78+
N,
79+
Some(X0),
80+
Some(1.0),
81+
None,
82+
CompoundPoisson::new(
83+
None,
84+
DoubleExp::new(None, 2.0, 2.5),
85+
Poisson::new(1.0, None, Some(1.0 / N as f64), None),
86+
),
87+
);
88+
89+
assert_eq!(kou.sample().len(), N);
90+
}
91+
92+
#[test]
93+
fn kou_starts_with_x0() {
94+
let kou = KOU::new(
95+
2.25,
96+
2.5,
97+
1.0,
98+
1.0,
99+
N,
100+
Some(X0),
101+
Some(1.0),
102+
None,
103+
CompoundPoisson::new(
104+
None,
105+
DoubleExp::new(None, 2.0, 2.5),
106+
Poisson::new(1.0, None, Some(1.0 / N as f64), None),
107+
),
108+
);
109+
110+
assert_eq!(kou.sample()[0], X0);
111+
}
112+
113+
#[test]
114+
fn kou_plot() {
115+
let kou = KOU::new(
116+
2.25,
117+
2.5,
118+
1.0,
119+
1.0,
120+
N,
121+
Some(S0),
122+
Some(1.0),
123+
None,
124+
CompoundPoisson::new(
125+
None,
126+
DoubleExp::new(None, 2.0, 20.5),
127+
Poisson::new(1.0, None, Some(1.0 / N as f64), None),
128+
),
129+
);
130+
131+
plot_1d!(kou.sample(), "KOU process");
132+
}
133+
134+
#[test]
135+
#[ignore = "Not implemented"]
136+
#[cfg(feature = "malliavin")]
137+
fn merton_malliavin() {
138+
unimplemented!()
139+
}
140+
}

0 commit comments

Comments
 (0)