cvxrisk: Convex Optimization for Portfolio Risk Management¶
📋 Overview¶
cvxrisk is a Python library for portfolio risk management using convex optimization. It provides a flexible framework for implementing various risk models and solves optimization problems directly with the Clarabel conic solver — no cvxpy required.
The library is built around an abstract Model class that standardizes
the interface for different risk models, making it easy to swap between
them in your optimization problems.
🚀 Installation¶
# Install from PyPI
pip install cvxrisk
# For development installation
git clone https://github.com/cvxgrp/cvxrisk.git
cd cvxrisk
make install
# For experimenting with the notebooks (after cloning)
make marimo
🔧 Quick Start¶
cvxrisk makes it easy to formulate and solve portfolio optimization problems:
import numpy as np
from cvx.risk.sample import SampleCovariance
from cvx.risk.portfolio import minrisk_problem
from cvx.core import Variable
# Create a risk model
riskmodel = SampleCovariance(num=2)
# Update the model with data
riskmodel.update(
cov = np.array([[1.0, 0.5], [0.5, 2.0]]),
lower_assets = np.zeros(2),
upper_assets = np.ones(2)
)
# Define portfolio weights variable
weights = Variable(2)
# Create and solve the optimization problem
problem = minrisk_problem(riskmodel, weights)
problem.solve()
print(problem.status)
print(np.array2string(np.round(weights.value, 2), separator=" "))
📊 Features¶
cvxrisk provides several risk models:
Sample Covariance¶
The simplest risk model based on the sample covariance matrix:
from cvx.risk.sample import SampleCovariance
import numpy as np
riskmodel = SampleCovariance(num=2)
riskmodel.update(
cov=np.array([[1.0, 0.5], [0.5, 2.0]]),
lower_assets=np.zeros(2),
upper_assets=np.ones(2),
)
# Reconstruct covariance from Cholesky factor and print
cov_est = riskmodel.parameter["chol"].value.T @ riskmodel.parameter["chol"].value
print(np.array2string(cov_est, precision=1))
Factor Risk Models¶
Factor models reduce dimensionality by projecting asset returns onto a smaller set of factors:
import numpy as np
from cvx.risk.factor import FactorModel
from cvx.linalg import pca
# Create some sample returns data
a = 100
m = 25
returns = np.random.randn(a, m)
# Compute principal components (deterministic using a fixed seed for reproducibility)
np.random.seed(0)
factors = pca(returns, n_components=10)
# Create and update the factor model
model = FactorModel(assets=m, k=10)
model.update(
cov=factors.cov,
exposure=factors.exposure,
idiosyncratic_risk=factors.idiosyncratic.std(axis=0, ddof=1),
lower_assets=np.zeros(m),
upper_assets=np.ones(m),
lower_factors=-0.1*np.ones(10),
upper_factors=0.1*np.ones(10),
)
# Verify the model has the correct dimensions
print(model.parameter["exposure"].value.shape)
Factor risk models use the projection of the weight vector into a lower dimensional subspace, e.g. each asset is the linear combination of $k$ factors.
$$r_i = \sum_{j=1}^k f_j \beta_{ji} + \epsilon_i$$
The factor time series are $f_1, \ldots, f_k$. The loadings are the coefficients $\beta_{ji}$. The residual returns $\epsilon_i$ are assumed to be uncorrelated with the factors.
Any position $w$ in weight space projects to a position $y = \beta^T w$ in factor space. The variance for a position $w$ is the sum of the variance of the systematic returns explained by the factors and the variance of the idiosyncratic returns.
$$Var® = Var(\beta^T w) + Var(\epsilon w)$$
We assume the residual returns are uncorrelated and hence
$$Var® = y^T \Sigma_f y + \sum_i w_i^2 Var(\epsilon_i)$$
where $\Sigma_f$ is the covariance matrix of the factors and $Var(\epsilon_i)$ is the variance of the idiosyncratic returns.
Conditional Value at Risk (CVaR)¶
CVaR measures the expected loss in the worst-case scenarios:
import numpy as np
from cvx.risk.cvar import CVar
# Create some sample historical returns (deterministic)
np.random.seed(0)
historical_returns = np.random.randn(50, 14)
# Create and update the CVaR model
model = CVar(alpha=0.95, n=50, m=14)
model.update(
returns=historical_returns,
lower_assets=np.zeros(14),
upper_assets=np.ones(14),
)
# Verify the model parameters
print(model.alpha)
print(model.parameter["R"].value.shape)
📚 Documentation¶
For more detailed documentation and examples, visit our documentation site.
🛠️ Development¶
cvxrisk uses modern Python development tools:
# Install development dependencies
make install
# Run tests
make test
# Format code
make fmt
# Start interactive notebooks
make marimo
📄 License¶
cvxrisk is licensed under the Apache License 2.0. See LICENSE for details.
👥 Contributing¶
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
For more information, see CONTRIBUTING.md.