Metadata-Version: 2.4
Name: torc-portfolio
Version: 0.1.0
Summary: Portfolio constraint feasibility checker with LP/MIP/CVaR support
Author-email: Carmen Esteban <caresment@gmail.com>
License: All Rights Reserved
Keywords: portfolio,optimization,feasibility,CVaR,MIP,LP,constraints
Classifier: Development Status :: 4 - Beta
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: scipy>=1.11.0
Requires-Dist: torc-sat>=0.1.1
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Dynamic: license-file

# portfolio-optimizer

Portfolio constraint feasibility checker with formal verification. Determines whether a set of portfolio constraints (cardinality, sector limits, position bounds, budget, CVaR risk) can be simultaneously satisfied — and if not, identifies exactly which constraints conflict and suggests fixes.

**1,143 tests.** Exhaustive brute-force verification, property-based fuzzing, adversarial edge cases, and formal solution certification on every result.

## Install

```bash
pip install portfolio-optimizer
```

## Quick Start

```python
from portfolio_optimizer import PortfolioChecker

pc = PortfolioChecker(n_assets=500)

# Cardinality: select 15-20 assets
pc.add_cardinality(min_assets=15, max_assets=20)

# Sector weight limits
pc.add_weight_limit("tech", assets=[0,1,2,3,4], max_pct=0.30)
pc.add_weight_limit("energy", assets=[5,6,7], min_pct=0.10, max_pct=0.25)

# Position bounds and budget
pc.add_position_bounds(min_w=0.02, max_w=0.10)
pc.add_budget(total=1.0)

# Mutual exclusions
pc.add_exclusion(asset_a=0, asset_b=1)  # Can't hold both

# CVaR risk constraint
import numpy as np
returns = np.random.normal(0.01, 0.05, (200, 500))
pc.add_cvar_limit(confidence=0.95, max_cvar=0.15, returns=returns)

# Check feasibility
result = pc.check_feasibility()
print(result.feasible)              # True/False
print(result.method)                # "quick", "sat", "lp", "mip", "cvar"
print(result.infeasible_constraints) # Which constraints conflict
print(result.suggestion)            # How to fix it
```

## Architecture: 5-Step Pipeline

Each step is more expensive than the previous. The pipeline stops at the first conclusive result.

| Step | Method | What it checks | When used |
|------|--------|----------------|-----------|
| 1 | **Quick checks** | Arithmetic impossibilities | Always |
| 2 | **SAT** (torc-sat) | Binary constraints (cardinality, exclusions, sectors) | Binary only |
| 3 | **LP** (scipy.linprog/HiGHS) | Weight constraints (sectors, budget, bounds) | Weight only |
| 4 | **MIP** (scipy.milp/HiGHS) | Mixed binary + weight constraints | Mixed |
| 5 | **CVaR** (Rockafellar-Uryasev LP) | Risk constraints | When CVaR present |

## Diagnostics: IIS Extraction

When constraints are infeasible, portfolio-optimizer identifies the **Irreducible Infeasible Subsystem** (IIS) — the minimal set of constraints that cannot be simultaneously satisfied. Analogous to MUS (Minimal Unsatisfiable Subset) in SAT solving.

```python
result = pc.check_feasibility()
if not result.feasible:
    print(result.infeasible_constraints)
    # ['position_bounds(max_w=0.1)', 'budget(total=1.0)']
    # → max 10 assets * 0.1 = 1.0, but cardinality says min 15

    print(result.suggestion)
    # "Relax position_bounds(max_w) or budget(total)"
```

## Verification (1,143 Tests)

| Suite | Tests | What it verifies |
|-------|-------|------------------|
| **Exhaustive** | 87 | Brute-force all C(n,k) subsets for n<=7, compare with MIP |
| **Fuzzing** | 246 | Random constraints, verify LP>=MIP monotonicity, solution certification |
| **Adversarial** | 35 | Near-boundary, degenerate pivots, 1000 assets, overlapping sectors |
| **Certification** | 341 | Every feasible solution satisfies ALL constraints; every infeasible is brute-force confirmed |
| **Parametrized** | 320 | Systematic parameter sweeps across all constraint types |
| **Unit** | 114 | Individual module tests |

```bash
pytest tests/ -v  # All 1,143 tests in ~7 seconds
```

## Constraint Types

- **CardinalityConstraint**: min/max number of selected assets
- **ExclusionConstraint**: two assets cannot both be selected
- **SectorMinConstraint**: minimum assets from a sector
- **DiversificationConstraint**: at least 1 asset from each sector
- **WeightLimitConstraint**: sector weight min/max percentage
- **BudgetConstraint**: total weight must equal target (default 1.0)
- **PositionBoundsConstraint**: per-asset weight min/max when selected
- **CVaRConstraint**: Conditional Value at Risk limit (Rockafellar-Uryasev 2000)

## Dependencies

- `numpy`
- `scipy >= 1.11.0` (HiGHS MIP solver)
- `torc-sat >= 0.1.1` (topological SAT preprocessor)

## License & Patent

**All Rights Reserved.** Carmen Esteban / IAFISCAL & PARTNERS.

This software implements methods protected by patent applications before the Spanish Patent and Trademark Office (OEPM). The topological SAT preprocessing method used in this package (via torc-sat) is the subject of a pending patent application.

- **Free for academic research and education.**
- **Commercial use requires a written license** from the author.
- Contact: caresment@gmail.com

Any use of this software or its methods in commercial products, SaaS platforms, or for-profit services without a license agreement constitutes patent infringement under Spanish and EU law.
