Skip to content

Error Handling Best Practices

Exception Hierarchy

All exceptions inherit from PortfolioManagementError:

  • DataError - Data loading, validation, matching
  • PortfolioConstructionError - Portfolio construction, strategies, constraints
  • BacktestError - Backtesting, performance calculation
  • ConfigurationError - Configuration validation

Raising Exceptions

Always use custom exceptions with context:

# Good
from portfolio_management.core.exceptions import DataLoadError

def load_returns(path: Path) -> pd.DataFrame:
    if not path.exists():
        raise DataLoadError(path, "File does not exist")
    # ...

Error Messages

Include actionable context:

  • File paths
  • Asset symbols
  • Invalid values
  • Expected values
  • Suggestions for fixing
# Good error message
raise StrategyError(
    "mean_variance",
    "Optimization failed: covariance matrix is singular. "
    "Try removing assets with zero variance."
)

# Bad error message
raise ValueError("Optimization failed")

Error Recovery

Use retry patterns for transient errors:

from portfolio_management.core.error_handling import retry_on_error

result = retry_on_error(
    lambda: load_data(path),
    max_attempts=3,
    exceptions=(IOError,),
)

Validation Patterns

Validate early and provide clear feedback:

def validate_config(config: dict) -> None:
    """Validate configuration with clear error messages."""
    if "strategy" not in config:
        raise ConfigurationError(
            None,
            "Missing required field 'strategy'. "
            "Must be one of: equal_weight, risk_parity, mean_variance"
        )
    # ...