1use rand::thread_rng;
2use rand_distr::{Distribution, Normal};
3
4pub trait Feed {
6 fn current_value(&self) -> f64;
8
9 fn step(&mut self) -> f64;
11}
12
13#[derive(Debug)]
14pub struct OrnsteinUhlenbeck {
16 current_value: f64,
17
18 theta: f64,
20
21 mu: f64,
23
24 sigma: f64,
26
27 dt: f64,
29}
30
31impl OrnsteinUhlenbeck {
32 pub fn new(initial_value: f64, theta: f64, mu: f64, sigma: f64, dt: f64) -> Self {
34 OrnsteinUhlenbeck {
35 current_value: initial_value,
36 theta,
37 mu,
38 sigma,
39 dt,
40 }
41 }
42}
43
44impl Feed for OrnsteinUhlenbeck {
45 fn current_value(&self) -> f64 {
46 self.current_value
47 }
48
49 fn step(&mut self) -> f64 {
50 let mut rng = thread_rng();
51 let normal = Normal::new(0.0, 1.0).unwrap();
52
53 let drift = self.theta * (self.mu - self.current_value) * self.dt;
54 let randomness = self.sigma * self.dt.sqrt() * normal.sample(&mut rng);
55
56 self.current_value += drift + randomness;
57 self.current_value
58 }
59}
60
61#[derive(Debug)]
62pub struct GeometricBrownianMotion {
64 pub initial_value: f64,
66
67 pub current_value: f64,
69
70 pub current_time: f64,
72
73 pub mu: f64,
75
76 pub sigma: f64,
78
79 pub dt: f64,
81}
82
83impl GeometricBrownianMotion {
84 pub fn new(initial_value: f64, mu: f64, sigma: f64, dt: f64) -> Self {
86 GeometricBrownianMotion {
87 initial_value,
88 current_value: initial_value,
89 current_time: 0.0,
90 mu,
91 sigma,
92 dt,
93 }
94 }
95}
96
97impl Feed for GeometricBrownianMotion {
98 fn current_value(&self) -> f64 {
99 self.current_value
100 }
101
102 fn step(&mut self) -> f64 {
103 let mut rng = thread_rng();
104 let normal = Normal::new(0.0, 1.0).unwrap();
105
106 let wiener_process = normal.sample(&mut rng) * self.dt.sqrt();
107
108 let drift = (self.mu - 0.5 * self.sigma.powi(2)) * self.dt;
109
110 let volatility = self.sigma * wiener_process;
111
112 let change = drift + volatility;
113
114 self.current_value *= (change).exp();
115 self.current_time += self.dt;
116 self.current_value
117 }
118}