arena_core/
feed.rs

1use rand::thread_rng;
2use rand_distr::{Distribution, Normal};
3
4/// Represents an arbitrary price feed.
5pub trait Feed {
6    /// Returns the current value of the feed.
7    fn current_value(&self) -> f64;
8
9    /// Advances the feed by one step and returns the new value.
10    fn step(&mut self) -> f64;
11}
12
13#[derive(Debug)]
14/// Implementation of an Ornstein-Uhlenbeck process using a Euler-Maruyama discretization scheme.
15pub struct OrnsteinUhlenbeck {
16    current_value: f64,
17
18    /// Mean reversion rate.
19    theta: f64,
20
21    /// Long-term mean.
22    mu: f64,
23
24    /// Volatility.
25    sigma: f64,
26
27    /// Time step.
28    dt: f64,
29}
30
31impl OrnsteinUhlenbeck {
32    /// Public constructor function for a new [`OrnsteinUhlenbeck`].
33    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)]
62/// Implementation of a geometric Brownian motion using a Euler-Maruyama discretization scheme.
63pub struct GeometricBrownianMotion {
64    /// The initial value of the process.
65    pub initial_value: f64,
66
67    /// The current value of the process.
68    pub current_value: f64,
69
70    /// The current time in the process, incremented with each step by the time step `dt`.
71    pub current_time: f64,
72
73    /// The drift coefficient.
74    pub mu: f64,
75
76    /// The volatility coefficient.
77    pub sigma: f64,
78
79    /// The time step size used for advancing the process.
80    pub dt: f64,
81}
82
83impl GeometricBrownianMotion {
84    /// Public constructor function for a new [`GeometricBrownianMotion`].
85    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}