Skip to main content

solver/analytical/avellaneda/approximations/
avellaneda_stoikov.rs

1use crate::analytical::traits::AnalyticalSolution;
2use crate::models::traits::ControlOutput;
3
4/// The asymptotic approximation (infinite horizon) for the Avellaneda-Stoikov model.
5/// Based on the Avellaneda-Stoikov paper (2008) approximation using Taylor expansion.
6///
7/// Reference: Avellaneda, M., & Stoikov, S. (2008). High-frequency trading in a limit order book.
8pub 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    /// Avellaneda-Stoikov (2008) approximation for optimal spreads
28    /// delta_bid = 1/gamma * ln(1 + gamma/kappa) + (1 + 2q) * gamma * sigma^2 * (T - t) / 2
29    /// delta_ask = 1/gamma * ln(1 + gamma/kappa) + (1 - 2q) * gamma * sigma^2 * (T - t) / 2
30    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        // Value function approximation is not implemented here, only controls
45        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}