Risk parity portfolio optimization with weight constraints.
Project description
Risk Parity Portfolio Optimization with Weight Constraints
Python package for long-only risk parity portfolio construction with two solvers:
CCDSolverfor the unconstrained baseline problemSCASolverfor risk parity with per-asset upper bounds
The project is designed as a compact, testable implementation for numerical finance coursework and lightweight experimentation.
Features
- Unconstrained risk parity via cyclical coordinate descent (CCD)
- Constrained risk parity via successive convex approximation (SCA)
- Utility functions for absolute and relative risk contributions
- NumPy-only core dependency
- Pytest test suite and GitHub Actions CI
Installation
Install from PyPI:
pip install risk-parity-constrained
Or install locally from source:
git clone https://github.com/mercuryyujia/MATH5030_group_project.git
cd MATH5030_group_project
pip install -e .
Install test dependencies:
pip install -e .[test]
Install notebook/demo dependencies:
pip install -e .[demo]
Quick Start
import numpy as np
from riskparity import (
CCDSolver,
SCASolver,
relative_risk_contributions,
risk_contribution_gap,
)
Sigma = np.array([
[0.04, 0.01, 0.00],
[0.01, 0.09, 0.02],
[0.00, 0.02, 0.16],
])
w_ccd = CCDSolver(Sigma).solve()
print("CCD weights:", w_ccd)
print("CCD relative RC:", relative_risk_contributions(Sigma, w_ccd))
print("CCD gap:", risk_contribution_gap(Sigma, w_ccd))
w_sca = SCASolver(Sigma, w_max=0.5).solve()
print("SCA weights:", w_sca)
print("SCA relative RC:", relative_risk_contributions(Sigma, w_sca))
print("SCA gap:", risk_contribution_gap(Sigma, w_sca))
What the Solvers Do
Risk parity aims to allocate weights so that each asset contributes equally to total portfolio risk. In practice, portfolio construction often requires additional constraints such as:
- long-only weights
- weights summing to one
- per-asset caps like
w_i <= 10%
This package provides:
CCDSolver: an unconstrained long-only baseline solverSCASolver: a constrained solver with box constraints of the form0 <= w_i <= w_max
Public API
Solvers
CCDSolver(Sigma, tol=1e-8, max_iter=1000)
Computes an unconstrained long-only risk parity solution and normalizes weights to sum to 1.
| Parameter | Type | Description |
|---|---|---|
Sigma |
np.ndarray |
Symmetric covariance matrix of shape (n, n) |
tol |
float |
Convergence tolerance |
max_iter |
int |
Maximum CCD iterations |
Key attributes after .solve():
n_iter_converged_objective_risk_contribution_gap_
SCASolver(Sigma, w_max=1.0, tol=1e-6, max_iter=200)
Computes a constrained risk parity portfolio under:
sum(w) = 10 <= w_i <= w_max
| Parameter | Type | Description |
|---|---|---|
Sigma |
np.ndarray |
Symmetric covariance matrix of shape (n, n) |
w_max |
float |
Per-asset upper bound in (0, 1] |
tol |
float |
Convergence tolerance |
max_iter |
int |
Maximum outer SCA iterations |
Key attributes after .solve():
n_iter_converged_objective_risk_contribution_gap_
Utility Functions
risk_contributions(Sigma, w)
Returns per-asset risk contributions:
w * (Sigma @ w)
relative_risk_contributions(Sigma, w)
Normalizes risk contributions to sum to 1.
risk_contribution_gap(Sigma, w)
Returns the maximum absolute deviation from equal risk contributions.
Testing
The test suite is organized around correctness, validation, and robustness:
- Correct implementation and validation: covariance input validation, CCD solver checks, diagonal covariance closed-form cases, two-asset sanity checks, long-only weights that sum to one, and equal risk contribution checks.
- Robustness testing: constrained solver feasibility, infeasible
w_maxerrors, randomized covariance matrices, stability across different portfolio sizes, and boundary parameter cases.
Run the test suite with:
pytest
CI currently tests Python 3.10, 3.11, and 3.12.
Demo Notebook
The repository includes a demonstration notebook in notebooks/demo.ipynb.
Project Structure
.
├── .github/workflows/
│ ├── ci.yml
│ └── publish.yml
├── notebooks/
│ ├── demo.html
│ ├── demo.ipynb
│ └── demo.pdf
├── riskparity/
│ ├── __init__.py
│ └── _core.py
├── tests/
│ └── test_riskparity.py
├── LICENSE
├── pyproject.toml
└── README.md
License
MIT License. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file risk_parity_constrained-0.1.0.tar.gz.
File metadata
- Download URL: risk_parity_constrained-0.1.0.tar.gz
- Upload date:
- Size: 10.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7fcf0c9fd499979fcc08f31d3251a6e6d35ac45a71892f36b45c90a01787af41
|
|
| MD5 |
97e9816997aa10dea79948fccf24067e
|
|
| BLAKE2b-256 |
4a7e16d6fd0bb1c0924f4934981788688970dd0b48e53d806093237b73e5abb0
|
Provenance
The following attestation bundles were made for risk_parity_constrained-0.1.0.tar.gz:
Publisher:
publish.yml on mercuryyujia/MATH5030_group_project
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
risk_parity_constrained-0.1.0.tar.gz -
Subject digest:
7fcf0c9fd499979fcc08f31d3251a6e6d35ac45a71892f36b45c90a01787af41 - Sigstore transparency entry: 1397996303
- Sigstore integration time:
-
Permalink:
mercuryyujia/MATH5030_group_project@638f5be3bb6b0b3bcd7523ef7a0b6848afb2ee9b -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mercuryyujia
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@638f5be3bb6b0b3bcd7523ef7a0b6848afb2ee9b -
Trigger Event:
release
-
Statement type:
File details
Details for the file risk_parity_constrained-0.1.0-py3-none-any.whl.
File metadata
- Download URL: risk_parity_constrained-0.1.0-py3-none-any.whl
- Upload date:
- Size: 7.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ff06d378d872adb37bb76117a9a7fd6c658b1fa87f5464ff5ced00b5166e469
|
|
| MD5 |
928e097c23d996206485e9f3652670b2
|
|
| BLAKE2b-256 |
cd1eaa4cb41ace043a821bed96f12686ef1d8bd8cbbe6c7a967a1623cf0a9904
|
Provenance
The following attestation bundles were made for risk_parity_constrained-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on mercuryyujia/MATH5030_group_project
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
risk_parity_constrained-0.1.0-py3-none-any.whl -
Subject digest:
5ff06d378d872adb37bb76117a9a7fd6c658b1fa87f5464ff5ced00b5166e469 - Sigstore transparency entry: 1397996323
- Sigstore integration time:
-
Permalink:
mercuryyujia/MATH5030_group_project@638f5be3bb6b0b3bcd7523ef7a0b6848afb2ee9b -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mercuryyujia
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@638f5be3bb6b0b3bcd7523ef7a0b6848afb2ee9b -
Trigger Event:
release
-
Statement type: