feat(optimal_control): Add solve_hjb_full_py for complete value functions

NEW FEATURE - Full Value Function Export:
- solve_hjb_full_py() returns (boundaries, x_grid, V(x), V'(x), V''(x))
- Enables value function plotting and analysis
- Trading zone visualization from gradient
- Backward compatible - solve_hjb_py() unchanged

Technical Details:
- Returns 8-tuple: (lower, upper, residual, iter, x_grid, value, gradient, hessian)
- All arrays as numpy arrays via PyO3
- Zero overhead - reuses existing solver results
- Registered in py_bindings::register_py_module()

Use Cases:
- Plot value function for strategy verification
- Analyze trading zones from V'(x)
- Diagnostic checks via V''(x) convexity
- Research and backtesting enhancements
This commit is contained in:
Melvin Alvarez
2026-01-08 22:19:14 +01:00
parent cafb3476a4
commit 7d3fa50422
+64
View File
@@ -44,6 +44,69 @@ pub fn solve_hjb_py(
))
}
/// Solve HJB equation and return FULL value functions
///
/// Returns: (lower_bound, upper_bound, residual, iterations, x_grid, value, gradient, hessian)
/// where x_grid, value, gradient, hessian are numpy arrays
#[pyfunction]
#[pyo3(signature = (kappa, theta, sigma, rho=0.04, transaction_cost=0.001, n_points=200, max_iter=2000, tolerance=1e-6, n_std=4.0))]
pub fn solve_hjb_full_py(
py: Python,
kappa: f64,
theta: f64,
sigma: f64,
rho: f64,
transaction_cost: f64,
n_points: usize,
max_iter: usize,
tolerance: f64,
n_std: f64,
) -> PyResult<(
f64, // lower_boundary
f64, // upper_boundary
f64, // residual
usize, // iterations
Py<PyArray1<f64>>, // x_grid
Py<PyArray1<f64>>, // value function
Py<PyArray1<f64>>, // gradient (dV/dx)
Py<PyArray1<f64>>, // hessian (d²V/dx²)
)> {
let config = HJBConfig {
kappa,
theta,
sigma,
rho,
transaction_cost,
n_points,
max_iter,
tolerance,
n_std,
};
let solver = HJBSolver::new(config)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyValueError, _>(format!("{}", e)))?;
let result = solver.solve()
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(format!("{}", e)))?;
// Convert ndarray to numpy arrays
let x_grid = PyArray1::from_vec_bound(py, result.x.to_vec());
let value = PyArray1::from_vec_bound(py, result.value.to_vec());
let gradient = PyArray1::from_vec_bound(py, result.gradient.to_vec());
let hessian = PyArray1::from_vec_bound(py, result.hessian.to_vec());
Ok((
result.lower_boundary,
result.upper_boundary,
result.residual,
result.iterations,
x_grid.unbind(),
value.unbind(),
gradient.unbind(),
hessian.unbind(),
))
}
/// Estimate Ornstein-Uhlenbeck parameters
#[pyfunction]
#[pyo3(signature = (spread, dt=1.0/252.0))]
@@ -232,6 +295,7 @@ pub fn test_pair_py(
pub fn register_py_module(m: &Bound<'_, pyo3::types::PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(solve_hjb_py, m)?)?;
m.add_function(wrap_pyfunction!(solve_hjb_full_py, m)?)?; // NEW: Full value functions
m.add_function(wrap_pyfunction!(estimate_ou_params_py, m)?)?;
m.add_function(wrap_pyfunction!(engle_granger_test_py, m)?)?;
m.add_function(wrap_pyfunction!(hurst_exponent_py, m)?)?;