solver/analytical/drift/
exact.rs1use crate::analytical::traits::AnalyticalSolution;
2use crate::models::traits::ControlOutput;
3use crate::numeric::ode::LinearSpectralSolver;
4
5pub struct AvellanedaDriftExact {
8 pub gamma: f64,
9 pub sigma: f64,
10 pub kappa: f64,
11 pub a: f64,
12 pub mu: f64, pub phi: f64, pub terminal_time: f64,
15 pub q_max: usize,
16}
17
18impl AvellanedaDriftExact {
19 #[allow(clippy::too_many_arguments)]
20 pub fn new(
21 gamma: f64,
22 sigma: f64,
23 kappa: f64,
24 a: f64,
25 mu: f64,
26 phi: f64,
27 terminal_time: f64,
28 q_max: usize,
29 ) -> Self {
30 Self {
31 gamma,
32 sigma,
33 kappa,
34 a,
35 mu,
36 phi,
37 terminal_time,
38 q_max,
39 }
40 }
41
42 pub fn exact_spreads(&self, t: f64, q: f64) -> (f64, f64) {
44 let time_remaining = self.terminal_time - t;
45 if time_remaining <= 1e-9 {
46 return (f64::INFINITY, f64::INFINITY);
47 }
48
49 let n = 2 * self.q_max + 1;
50 let alpha = self.kappa * (0.5 * self.gamma * self.sigma.powi(2) + self.phi);
52 let beta = self.kappa * self.mu;
53 let eta = self.a * (1.0 + self.gamma / self.kappa).powf(-(1.0 + self.kappa / self.gamma));
54
55 let mut d = vec![0.0; n];
56 let mut e = vec![0.0; n];
57
58 for i in 0..n {
59 let q_val = (i as i32) - (self.q_max as i32);
60 let q_f = q_val as f64;
61
62 d[i] = -alpha * q_f.powi(2) + beta * q_f;
72 if i < n - 1 {
73 e[i] = eta;
74 }
75 }
76
77 let y0 = vec![1.0; n];
79
80 let v_t = LinearSpectralSolver::solve_tridiagonal(d, e, &y0, time_remaining);
82
83 let const_term = (1.0 / self.gamma) * (1.0 + self.gamma / self.kappa).ln();
84 let q_idx = (q.round() as i32 + self.q_max as i32) as usize;
85
86 if q_idx == 0 || q_idx >= n - 1 {
87 return (999.0, 999.0);
88 }
89
90 let bid = (1.0 / self.kappa) * (v_t[q_idx] / v_t[q_idx + 1]).ln() + const_term;
91 let ask = (1.0 / self.kappa) * (v_t[q_idx] / v_t[q_idx - 1]).ln() + const_term;
92
93 (bid, ask)
94 }
95}
96
97impl AnalyticalSolution<2> for AvellanedaDriftExact {
98 fn value_function(&self, _t: f64, _state: &[f64; 2]) -> f64 {
99 0.0 }
101
102 fn optimal_controls(&self, t: f64, state: &[f64; 2]) -> ControlOutput<2> {
103 let q = state[0];
104 let (d_bid, d_ask) = self.exact_spreads(t, q);
105
106 let lambda_bid = self.a * (-self.kappa * d_bid).exp();
107 let lambda_ask = self.a * (-self.kappa * d_ask).exp();
108
109 let flow = lambda_bid * d_bid + lambda_ask * d_ask;
110
111 ControlOutput {
112 lambda_plus: [lambda_bid, 0.0],
113 lambda_minus: [lambda_ask, 0.0],
114 flow,
115 }
116 }
117}