solver/analytical/avellaneda/approximations/
gueant.rs1use crate::analytical::traits::AnalyticalSolution;
2use crate::models::traits::ControlOutput;
3
4pub struct AvellanedaGueant {
9 pub gamma: f64,
10 pub sigma: f64,
11 pub kappa: f64,
12 pub a: f64,
13}
14
15impl AvellanedaGueant {
16 pub fn new(gamma: f64, sigma: f64, kappa: f64, a: f64) -> Self {
17 Self {
18 gamma,
19 sigma,
20 kappa,
21 a,
22 }
23 }
24
25 pub fn approximate_spreads(&self, q: f64) -> (f64, f64) {
35 let risk_factor = (self.sigma.powi(2) * self.gamma / (2.0 * self.kappa * self.a)).sqrt();
36 let const_term = (1.0 / self.gamma) * (1.0 + self.gamma / self.kappa).ln();
37 let correction =
38 (1.0 + self.gamma / self.kappa).powf(0.5 * (1.0 + self.kappa / self.gamma));
39
40 let delta_bid = const_term + (2.0 * q + 1.0) / 2.0 * risk_factor * correction;
41 let delta_ask = const_term - (2.0 * q - 1.0) / 2.0 * risk_factor * correction;
42
43 (delta_bid, delta_ask)
44 }
45}
46
47impl AnalyticalSolution<2> for AvellanedaGueant {
48 fn value_function(&self, _t: f64, _state: &[f64; 2]) -> f64 {
49 0.0
51 }
52
53 fn optimal_controls(&self, _t: f64, state: &[f64; 2]) -> ControlOutput<2> {
54 let q = state[0];
55 let (d_bid, d_ask) = self.approximate_spreads(q);
56
57 let lambda_bid = self.a * (-self.kappa * d_bid).exp();
58 let lambda_ask = self.a * (-self.kappa * d_ask).exp();
59
60 let flow = lambda_bid * d_bid + lambda_ask * d_ask;
61
62 ControlOutput {
63 lambda_plus: [lambda_bid, 0.0],
64 lambda_minus: [lambda_ask, 0.0],
65 flow,
66 }
67 }
68}