Files
optimiz-rs/src/portfolio_optimization/traits.rs
T
ThotDjehuty e67b0f8376 feat(portfolio): add CARA, convex, mean-variance & ERC portfolio optimization module
New Rust portfolio_optimization module with PyO3 bindings:
- CARA/CRRA utility maximization via projected gradient descent
- General-purpose convex objective solver on simplex (ProjectedGradientSolver)
- Mean-variance optimization (max Sharpe, target return, min variance)
- Equal Risk Contribution (ERC) portfolio allocation
- Python bindings: cara_optimal_weights, mean_variance_optimal_weights,
  min_variance_weights, erc_weights
- 6/6 unit tests passing

Convergence fix: removed gradient-norm criterion on simplex boundary
(projected gradient never vanishes at constrained optimum).
Default learning rate increased from 0.005 to 0.1.
2026-04-15 03:08:08 +02:00

76 lines
2.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Core traits for portfolio utility and convex optimisation.
use crate::core::OptimizrError;
/// A utility function U(x) mapping wealth → utility.
pub trait UtilityFunction: Send + Sync {
/// U(x) — the utility of wealth level x.
fn utility(&self, x: f64) -> f64;
/// U'(x) — first derivative (marginal utility).
fn marginal_utility(&self, x: f64) -> f64;
/// (U')^{-1}(y) — inverse marginal utility (used in duality).
fn inverse_marginal(&self, y: f64) -> f64;
/// Risk-aversion coefficient A(x) = -U''(x) / U'(x).
fn risk_aversion(&self, x: f64) -> f64;
/// Name identifier for logging / serialisation.
fn name(&self) -> &str;
}
/// Convex objective f(w) over portfolio weights w ∈ ^n.
///
/// Used by convex solvers (projected gradient, ADMM, etc.).
pub trait ConvexObjective: Send + Sync {
/// f(w) — objective value.
fn value(&self, w: &[f64]) -> f64;
/// ∇f(w) — gradient vector.
fn gradient(&self, w: &[f64]) -> Vec<f64>;
/// Dimension of the weight vector.
fn dim(&self) -> usize;
}
/// Convex constraint g(w) ≤ 0.
pub trait ConvexConstraint: Send + Sync {
/// g(w) — constraint value (feasible when ≤ 0).
fn value(&self, w: &[f64]) -> f64;
/// ∇g(w) — gradient of constraint function.
fn gradient(&self, w: &[f64]) -> Vec<f64>;
}
/// Result of a portfolio optimisation.
#[derive(Debug, Clone)]
pub struct PortfolioResult {
pub weights: Vec<f64>,
pub utility: f64,
pub expected_return: f64,
pub portfolio_variance: f64,
pub iterations: usize,
pub converged: bool,
}
impl PortfolioResult {
pub fn sharpe_ratio(&self, risk_free: f64) -> f64 {
let vol = self.portfolio_variance.sqrt();
if vol < 1e-15 {
return 0.0;
}
(self.expected_return - risk_free) / vol
}
}
/// Generic portfolio optimiser trait.
pub trait PortfolioOptimizer: Send + Sync {
fn optimize(
&self,
mu: &[f64],
cov: &[Vec<f64>],
max_weight: f64,
) -> Result<PortfolioResult, OptimizrError>;
}