Skip to main content

solver/numeric/
solver.rs

1use super::result::{GridSolution, SpreadResult};
2use crate::core::grid::Grid;
3use crate::models::traits::Model;
4
5/// Common interface for all numerical HJB solvers.
6///
7/// Provides a unified API for solving the HJB equation using different numerical methods:
8/// - **Policy Iteration**: Finite difference grid method combining Howard's algorithm with operator splitting.
9/// - **BSDE**: Deep learning or regression-based Monte Carlo approach for high-dimensional problems.
10///
11/// This trait abstracts the storage medium (Grid vs Neural Net vs Monte Carlo Paths)
12/// allowing the rest of the application to just ask for solutions.
13pub trait Solver {
14    /// Solve the HJB equation and return the generic solution at a specific state.
15    ///
16    /// This is the primary method. It returns a `GridSolution` containing the
17    /// value function and raw control intensities, without any market-making
18    /// interpretation. Callers that need spreads should use `solve_with_spreads`
19    /// or call `GridSolution::to_spreads()` directly.
20    ///
21    /// # Arguments
22    /// * `grid` - The discretization grid (Optional: irrelevant for grid-free methods like BSDE)
23    /// * `model` - The model defining stochastic dynamics (drift, volatility) and objective (utility).
24    /// * `time_steps` - Number of time steps for the backward induction or simulation.
25    /// * `query_state` - The specific state vector (e.g. [Inventory, Price]) to evaluate.
26    fn solve<const N: usize, M: Model<N> + Sync>(
27        &self,
28        grid: Option<&Grid<N>>,
29        model: &M,
30        time_steps: usize,
31        query_state: &[f64; N],
32    ) -> GridSolution<N>;
33
34    /// Solve and convert to market-making spreads using the model's fill-rate parameters.
35    ///
36    /// Convenience wrapper that calls `solve` then `GridSolution::to_spreads`.
37    ///
38    /// # Returns
39    /// `SpreadResult` containing the Value Function, optimal spreads delta_pm,
40    /// and optimal intensities lambda_pm.
41    fn solve_with_spreads<const N: usize, M: Model<N> + Sync>(
42        &self,
43        grid: Option<&Grid<N>>,
44        model: &M,
45        time_steps: usize,
46        query_state: &[f64; N],
47    ) -> SpreadResult {
48        let solution = self.solve(grid, model, time_steps, query_state);
49        solution.to_spreads(model.fill_rate_base(query_state), model.fill_rate_decay())
50    }
51}