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