Skip to content

Commit 28003c6

Browse files
committed
fix: cgmy models
1 parent 6e2f397 commit 28003c6

File tree

4 files changed

+118
-96
lines changed

4 files changed

+118
-96
lines changed

src/stochastic/jump/cgmy.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -75,28 +75,29 @@ impl Sampling<f64> for CGMY {
7575

7676
let U = Array1::<f64>::random(self.j, Uniform::new(0.0, 1.0));
7777
let E = Array1::<f64>::random(self.j, Exp::new(1.0).unwrap());
78+
let P = Poisson::new(1.0, Some(self.j), None, None);
79+
let P = P.sample();
7880
let tau = Array1::<f64>::random(self.j, Uniform::new(0.0, 1.0));
79-
let poisson = Poisson::new(1.0, Some(self.j), None, None);
80-
let poisson = poisson.sample();
8181

8282
for i in 1..self.n {
8383
let mut jump_component = 0.0;
8484
let t_1 = (i - 1) as f64 * dt;
8585
let t = i as f64 * dt;
8686

8787
for j in 1..self.j {
88-
let v_j = if rng.gen_bool(0.5) {
89-
self.lambda_plus
90-
} else {
91-
-self.lambda_minus
92-
};
93-
94-
let term1 = (self.alpha * poisson[j] / (2.0 * C * t_max)).powf(-1.0 / self.alpha);
95-
let term2 = E[j] * U[j].powf(1.0 / self.alpha) / v_j.abs();
96-
let jump_size =
97-
term1.min(term2) * (v_j / v_j.abs()) * if tau[j] > t_1 && tau[j] < t { 1.0 } else { 0.0 };
98-
99-
jump_component += jump_size;
88+
if tau[j] > t_1 && tau[j] <= t {
89+
let v_j = if rng.gen_bool(0.5) {
90+
self.lambda_plus
91+
} else {
92+
-self.lambda_minus
93+
};
94+
95+
let term1 = (self.alpha * P[j] / (2.0 * C * t_max)).powf(-1.0 / self.alpha);
96+
let term2 = E[j] * U[j].powf(1.0 / self.alpha) / v_j.abs();
97+
let jump_size = term1.min(term2) * (v_j / v_j.abs());
98+
99+
jump_component += jump_size;
100+
}
100101
}
101102

102103
x[i] = x[i - 1] + jump_component + b_t * dt;
@@ -137,7 +138,7 @@ mod tests {
137138

138139
#[test]
139140
fn cgmy_plot() {
140-
let cgmy = CGMY::new(25.46, 4.604, 0.52, 100, 1024, Some(2.0), Some(1.0), None);
141+
let cgmy = CGMY::new(25.46, 4.604, 0.52, 1000, 1024, Some(2.0), Some(1.0), None);
141142
plot_1d!(cgmy.sample(), "CGMY Process");
142143
}
143144

src/stochastic/jump/cts.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,19 @@ impl Sampling<f64> for CTS {
5959
let t = i as f64 * dt;
6060

6161
for j in 1..self.j {
62-
let v_j = if rng.gen_bool(0.5) {
63-
self.lambda_plus
64-
} else {
65-
-self.lambda_minus
66-
};
67-
68-
let term1 = (self.alpha * poisson[j] / C).powf(-1.0 / self.alpha);
69-
let term2 = E[j] * U[j].powf(1.0 / self.alpha) / v_j.abs();
70-
let jump_size =
71-
term1.min(term2) * (v_j / v_j.abs()) * if tau[j] > t_1 && tau[j] < t { 1.0 } else { 0.0 };
72-
73-
jump_component += jump_size;
62+
if tau[j] > t_1 && tau[j] <= t {
63+
let v_j = if rng.gen_bool(0.5) {
64+
self.lambda_plus
65+
} else {
66+
-self.lambda_minus
67+
};
68+
69+
let term1 = (self.alpha * poisson[j] / C).powf(-1.0 / self.alpha);
70+
let term2 = E[j] * U[j].powf(1.0 / self.alpha) / v_j.abs();
71+
let jump_size = term1.min(term2) * (v_j / v_j.abs());
72+
73+
jump_component += jump_size;
74+
}
7475
}
7576

7677
x[i] = x[i - 1] + jump_component + b_t * dt;
@@ -111,13 +112,13 @@ mod tests {
111112

112113
#[test]
113114
fn cts_plot() {
114-
let cts = CTS::new(25.46, 4.604, 0.52, 1024, 1024, Some(2.0), Some(1.0), None);
115+
let cts = CTS::new(25.46, 4.604, 0.52, N, 1024, Some(2.0), Some(1.0), None);
115116
plot_1d!(cts.sample(), "CTS Process");
116117
}
117118

118119
#[test]
119120
fn cts_plot_multi() {
120-
let cts = CTS::new(25.46, 4.604, 0.52, N, 10000, Some(2.0), Some(1.0), Some(10));
121+
let cts = CTS::new(25.46, 4.604, 0.52, N, 1024, Some(2.0), Some(1.0), Some(10));
121122
plot_nd!(cts.sample_par(), "CTS Process");
122123
}
123124
}

src/stochastic/jump/rdts.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,19 @@ impl Sampling<f64> for RDTS {
5959
let t = i as f64 * dt;
6060

6161
for j in 1..self.j {
62-
let v_j = if rng.gen_bool(0.5) {
63-
self.lambda_plus
64-
} else {
65-
-self.lambda_minus
66-
};
67-
68-
let term1 = (self.alpha * poisson[j] / (2.0 * C * t_max)).powf(-1.0 / self.alpha);
69-
let term2 = 0.5 * E[j].powf(0.5) * U[j].powf(1.0 / self.alpha) / v_j.abs();
70-
let jump_size =
71-
term1.min(term2) * (v_j / v_j.abs()) * if tau[j] > t_1 && tau[j] < t { 1.0 } else { 0.0 };
72-
73-
jump_component += jump_size;
62+
if tau[j] > t_1 && tau[j] <= t {
63+
let v_j = if rng.gen_bool(0.5) {
64+
self.lambda_plus
65+
} else {
66+
-self.lambda_minus
67+
};
68+
69+
let term1 = (self.alpha * poisson[j] / (2.0 * C * t_max)).powf(-1.0 / self.alpha);
70+
let term2 = 0.5 * E[j].powf(0.5) * U[j].powf(1.0 / self.alpha) / v_j.abs();
71+
let jump_size = term1.min(term2) * (v_j / v_j.abs());
72+
73+
jump_component += jump_size;
74+
}
7475
}
7576

7677
x[i] = x[i - 1] + jump_component + b_t * dt;

src/stochastic/volatility/svcgmy.rs

Lines changed: 74 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ use crate::{
1010
stochastic::{process::poisson::Poisson, Sampling},
1111
};
1212

13+
/// CGMY Stochastic Volatility process
14+
///
15+
/// https://www.econstor.eu/bitstream/10419/239493/1/175133161X.pdf
1316
#[derive(ImplNew)]
1417
pub struct SVCGMY {
1518
/// Positive jump rate lambda_plus (corresponds to G)
@@ -18,19 +21,19 @@ pub struct SVCGMY {
1821
pub lambda_minus: f64, // M
1922
/// Jump activity parameter alpha (corresponds to Y), with 0 < alpha < 2
2023
pub alpha: f64,
21-
///
24+
/// Mean reversion rate
2225
pub kappa: f64,
23-
///
26+
/// Long-term volatility
2427
pub eta: f64,
25-
///
28+
/// Volatility of volatility
2629
pub zeta: f64,
2730
///
2831
pub rho: f64,
2932
/// Number of time steps
3033
pub n: usize,
3134
/// Jumps
3235
pub j: usize,
33-
///
36+
/// Initial value
3437
pub x0: Option<f64>,
3538
/// Initial value
3639
pub v0: Option<f64>,
@@ -45,80 +48,74 @@ impl Sampling<f64> for SVCGMY {
4548
let mut rng = rand::thread_rng();
4649

4750
let t_max = self.t.unwrap_or(1.0);
48-
let dt = self.t.unwrap_or(1.0) / (self.n - 1) as f64;
49-
let mut x = Array1::<f64>::zeros(self.n);
50-
x[0] = self.x0.unwrap_or(0.0);
51-
52-
let C = (gamma(2.0 - self.alpha)
53-
* (self.lambda_plus.powf(self.alpha - 2.0) + self.lambda_minus.powf(self.alpha - 2.0)))
54-
.powi(-1);
55-
let c = (2.0 * self.kappa) / ((1.0 - (-self.kappa * dt).exp()) * self.zeta.powi(2));
51+
let dt = t_max / (self.n - 1) as f64;
5652

53+
let mut x = Array1::<f64>::zeros(self.n);
5754
let mut v = Array1::<f64>::zeros(self.n);
55+
let mut y = Array1::<f64>::zeros(self.n);
56+
57+
x[0] = self.x0.unwrap_or(0.0);
5858
v[0] = self.v0.unwrap_or(0.0);
5959

60+
let C = 1.0
61+
/ (gamma(2.0 - self.alpha)
62+
* (self.lambda_plus.powf(self.alpha - 2.0) + self.lambda_minus.powf(self.alpha - 2.0)));
63+
let c = (2.0 * self.kappa) / ((1.0 - (-self.kappa * dt).exp()) * self.zeta.powi(2));
6064
let df = 4.0 * self.kappa * self.eta / self.zeta.powi(2);
6165

66+
// Volatilitás folyamat generálása
6267
for i in 1..self.n {
6368
let ncp = 2.0 * c * v[i - 1] * (-self.kappa * dt).exp();
6469
let xi = non_central_chi_squared::sample(df, ncp, &mut rng);
65-
6670
v[i] = xi / (2.0 * c);
6771
}
6872

6973
let U = Array1::<f64>::random(self.j, Uniform::new(0.0, 1.0));
70-
let E = Array1::<f64>::random(self.j, Exp::new(1.0).unwrap());
71-
let E_ = Array1::<f64>::random(self.j, Exp::new(1.0).unwrap());
74+
let E = Array1::random(self.j, Exp::new(1.0).unwrap());
7275
let P = Poisson::new(1.0, Some(self.j), None, None);
73-
let mut P = P.sample();
74-
75-
for (idx, p) in P.iter_mut().enumerate() {
76-
*p = *p + E_[idx];
77-
}
76+
let P = P.sample();
77+
let tau = Array1::<f64>::random(self.j, Uniform::new(0.0, 1.0)) * t_max;
7878

79-
let tau = Array1::<f64>::random(self.j, Uniform::new(0.0, t_max));
8079
let mut c_tau = Array1::<f64>::zeros(self.j);
81-
82-
for j in 1..self.j {
83-
let mut c_tau_ = 0.0;
84-
for i in 1..self.n {
85-
let t_1 = (i - 1) as f64 * dt;
86-
let t = i as f64 * dt;
87-
88-
if t_1 < tau[j] && tau[j] <= t {
89-
c_tau_ += v[i - 1]
90-
}
91-
}
92-
93-
c_tau[j] = C * c_tau_;
80+
for (idx, tau_j) in tau.iter().enumerate() {
81+
let k = ((tau_j / dt).ceil() as usize).min(self.n - 1);
82+
let v_k = if k == 0 { v[0] } else { v[k - 1] };
83+
c_tau[idx] = C * v_k;
9484
}
95-
println!("{:?}", c_tau);
85+
9686
for i in 1..self.n {
97-
let b = -((v[i]
98-
* (self.lambda_plus.powf(self.alpha - 1.0) - self.lambda_minus.powf(self.alpha - 1.0)))
99-
/ ((1.0 - self.alpha)
100-
* (self.lambda_plus.powf(self.alpha - 2.0) + self.lambda_minus.powf(self.alpha) - 2.0)));
87+
let numerator = v[i - 1]
88+
* (self.lambda_plus.powf(self.alpha - 1.0) - self.lambda_minus.powf(self.alpha - 1.0));
89+
let denominator = (1.0 - self.alpha)
90+
* (self.lambda_plus.powf(self.alpha - 2.0) + self.lambda_minus.powf(self.alpha - 2.0));
91+
let b = -numerator / denominator;
10192

10293
let mut jump_component = 0.0;
94+
10395
let t_1 = (i - 1) as f64 * dt;
10496
let t = i as f64 * dt;
10597

106-
for j in 1..self.j {
107-
let v_j = if rng.gen_bool(0.5) {
108-
self.lambda_plus
109-
} else {
110-
-self.lambda_minus
111-
};
112-
113-
let term1 = ((self.alpha * P[j]) / (2.0 * c_tau[j] * t_max)).powf(-1.0 / self.alpha);
114-
let term2 = E[j] * U[j].powf(1.0 / self.alpha) / v_j.abs();
115-
let jump_size =
116-
term1.min(term2) * (v_j / v_j.abs()) * if tau[j] > t_1 && tau[j] < t { 1.0 } else { 0.0 };
117-
118-
jump_component += jump_size;
98+
for j in 0..self.j {
99+
if tau[j] > t_1 && tau[j] <= t {
100+
let v_j = if rng.gen_bool(0.5) {
101+
self.lambda_plus
102+
} else {
103+
-self.lambda_minus
104+
};
105+
106+
let term1 = ((self.alpha * P[j]) / (2.0 * c_tau[j] * t_max)).powf(-1.0 / self.alpha);
107+
let term2 = E[j] * U[j].powf(1.0 / self.alpha) / v_j.abs();
108+
let min_term = term1.min(term2);
109+
let jump_size = min_term * (v_j / v_j.abs());
110+
jump_component += jump_size;
111+
}
119112
}
120113

121-
x[i] = x[i - 1] + jump_component + b * dt + self.rho * v[i - 1];
114+
y[i] = y[i - 1] + jump_component + b * dt;
115+
}
116+
117+
for i in 0..self.n {
118+
x[i] = y[i] + self.rho * v[i];
122119
}
123120

124121
x
@@ -135,8 +132,10 @@ impl Sampling<f64> for SVCGMY {
135132

136133
#[cfg(test)]
137134
mod tests {
135+
use ndarray::Axis;
136+
138137
use super::*;
139-
use crate::{plot_1d, stochastic::N};
138+
use crate::{plot_1d, plot_nd, stochastic::N};
140139

141140
#[test]
142141
fn svcgmy_length_equals_n() {
@@ -188,7 +187,7 @@ mod tests {
188187
0.0711,
189188
0.3443,
190189
-2.0280,
191-
100,
190+
1000,
192191
1024,
193192
Some(-0.25),
194193
Some(0.0064),
@@ -197,4 +196,24 @@ mod tests {
197196
);
198197
plot_1d!(svcgmy.sample(), "SVCGMY Process");
199198
}
199+
200+
#[test]
201+
fn svcgmy_plot_multi() {
202+
let svcgmy = SVCGMY::new(
203+
25.46,
204+
4.604,
205+
0.52,
206+
1.003,
207+
0.0711,
208+
0.3443,
209+
-2.0280,
210+
1000,
211+
1024,
212+
Some(-0.25),
213+
Some(0.0064),
214+
Some(1.0),
215+
Some(10),
216+
);
217+
plot_nd!(svcgmy.sample_par(), "SVCGMY Process");
218+
}
200219
}

0 commit comments

Comments
 (0)