portfolio-optimization
Guide for optimizing Python numerical computations with C extensions. This skill should be used when tasks involve creating C extensions for Python, implementing mathematical algorithms (matrix operations, linear algebra) in C, or optimizing computational bottlenecks to achieve significant speedup. Particularly relevant for portfolio risk/return calculations, scientific computing, and performance-critical code requiring validation against baseline implementations.
Installation
Follow the repository instructions for this skill and your agent. Review the files and dependencies before adding it to your workflow.
View source ↗GuidesSkill instructions
Portfolio Optimization with C Extensions
This skill provides a systematic approach to optimizing Python numerical code using C extensions, emphasizing incremental development, rigorous verification, and comprehensive edge case handling.
Core Workflow
Phase 1: Understand Before Coding
Before writing any implementation:
-
Read all files completely
- Read the baseline Python implementation to understand current behavior
- Read skeleton files and identify all TODO markers
- Read test/benchmark infrastructure to understand success criteria
- Read setup.py to understand build configuration
-
Document the mathematics explicitly
- Write out formulas clearly in comments before implementing
- For portfolio optimization:
- Portfolio risk:
sqrt(x^T * S * x)where x = weights, S = covariance matrix - Portfolio return:
x^T * rwhere r = expected returns
- Portfolio risk:
- Break complex operations into steps: matrix-vector multiply, then dot product, then sqrt
- Identify computational complexity (O(n²) for matrix operations)
-
Plan the implementation strategy
- Identify which function is simpler (implement that first)
- List all validation checks needed for inputs
- Consider memory access patterns for cache efficiency
- Note numerical stability concerns (precision, overflow)
Phase 2: Implement Incrementally
-
Implement the simpler function first
- For portfolio tasks: implement
portfolio_return_c(dot product) beforeportfolio_risk_c(matrix operation) - This validates the build system and Python-C interface early
- For portfolio tasks: implement
-
Apply the C Extension Safety Checklist
For each C function:
// Step 1: Parse arguments with type checking if (!PyArg_ParseTuple(args, "O!O!", &PyArray_Type, &arr1, &PyArray_Type, &arr2)) return NULL; // Step 2: Validate dimensions if (PyArray_NDIM(arr1) != 1) { PyErr_SetString(PyExc_ValueError, "Array must be 1D"); return NULL; } // Step 3: Validate shapes match npy_intp n = PyArray_DIM(arr1, 0); if (PyArray_DIM(arr2, 0) != n) { PyErr_SetString(PyExc_ValueError, "Array dimensions must match"); return NULL; } // Step 4: Get data pointers (AFTER validation) double *data1 = (double *)PyArray_DATA(arr1); double *data2 = (double *)PyArray_DATA(arr2); // Step 5: Perform computation // Step 6: Return result -
Implement Python wrapper with proper conversion
import numpy as np import portfolio_optimized_c def portfolio_risk_c(weights, cov_matrix): # Convert to NumPy arrays with correct dtype w = np.asarray(weights, dtype=np.float64) cov = np.asarray(cov_matrix, dtype=np.float64) # Ensure C-contiguous layout w = np.ascontiguousarray(w) cov = np.ascontiguousarray(cov) return portfolio_optimized_c.portfolio_risk_c(w, cov)
Phase 3: Verify Every Change
-
CRITICAL: Always read back edited files
- After every Edit operation, immediately read the complete file
- Verify changes were applied correctly and completely
- Check for syntax errors, unmatched brackets, or truncation
- Confirm implementation matches the documented algorithm
-
Build and test incrementally
# Build the extension python3 setup.py build_ext --inplace # Verify import works python3 -c "import portfolio_optimized_c; print('Import OK')" # Test with small example BEFORE running benchmark python3 -c " import numpy as np from portfolio_optimized import portfolio_risk_c, portfolio_return_c # Small test case (n=3 for hand verification) weights = [0.3, 0.4, 0.3] cov = [[0.04, 0.01, 0.01], [0.01, 0.09, 0.02], [0.01, 0.02, 0.16]] returns = [0.10, 0.12, 0.08] # Calculate and verify risk = portfolio_risk_c(weights, cov) ret = portfolio_return_c(weights, returns) print(f'Risk: {risk}, Return: {ret}') " -
Verify correctness before performance
- Run with small inputs first (n=10, n=100) to verify correctness
- Compare against baseline implementation
- Only run full benchmark after correctness is confirmed
Phase 4: Edge Case Testing
-
Test these edge cases explicitly
- Empty arrays (n=0) - should handle gracefully or error clearly
- Minimal case (n=1) - single asset portfolio
- Small case (n=2, n=3) - hand-calculable verification
- Dimension mismatches - weights vs. covariance matrix
- Non-square covariance matrix - should error
- Maximum specified size - verify no crashes or overflows
- Type mismatches - passing lists vs. arrays
Common Pitfalls
-
Not reading back edited code
- The implementation may be truncated or incorrect
- Always verify edits with a subsequent Read operation
-
Skipping incremental testing
- Testing only with the full benchmark misses subtle bugs
- Test each function independently with small inputs first
-
Missing input validation in C
- Unvalidated inputs cause segfaults, not Python errors
- Always validate dimensions, types, and shapes before accessing data
-
Assuming array contiguity
- Python lists and non-contiguous arrays need conversion
- Always use
np.ascontiguousarray()in the wrapper
-
Ignoring numerical precision
- Use float64 (double) consistently
- Check tolerance requirements (often 1e-10)
-
Not understanding the speedup
- Know why C is faster: eliminated interpreter overhead, cache-friendly access, compiler optimizations
- If speedup is lower than expected (e.g., only 1.4x), consider algorithmic improvements
Verification Checklist
Before considering the task complete:
- All edited files have been read back and verified correct
- Mathematical algorithm is documented in comments
- C extension compiles without warnings
- Python wrapper converts inputs properly
- Small input tests pass (n=3, n=10)
- Baseline comparison passes within tolerance
- Full benchmark passes (correctness + performance)
- Edge cases have been considered
- Input validation handles errors gracefully
Build Reference
Standard setup.py structure:
import numpy
from setuptools import Extension, setup
module = Extension(
'portfolio_optimized_c',
sources=['portfolio_optimized.c'],
include_dirs=[numpy.get_include()],
extra_compile_args=['-O3', '-ffast-math', '-funroll-loops']
)
setup(name='portfolio_optimized', ext_modules=[module])
Build command: python3 setup.py build_ext --inplace
Troubleshooting
| Problem | Likely Cause | Solution |
|---|---|---|
| Compilation error | Missing NumPy include or syntax error | Check numpy.get_include(), review C syntax |
| Import error | Extension not built | Run python3 setup.py build_ext --inplace |
| Segfault | Missing input validation | Add dimension/type checks before data access |
| Wrong results | Algorithm error | Verify with small hand-calculable example |
| Tolerance failure | Precision issue | Ensure using float64, check algorithm |
| Slow performance | Not compiled with -O3 | Check setup.py extra_compile_args |
Examples
Read the usage examples in the source repository.
View source ↗Source history
- Indexed on
- 2026-01-20
- Index updated
- 2026-02-04
- Branch
- main
Index and sync dates describe this directory. View the source repository for code history.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!