Skip to content

covariance

Multi-asset, portfolio-level risk. This is the one category with no equivalent in empyrical or quantstats - both operate on a single, already-aggregated return stream, never a weighted book of assets.

covariance

Covariance/correlation matrices and portfolio-level risk decomposition.

covariance_matrix

covariance_matrix(
    returns: DataFrame,
    annualized: bool = False,
    periods_per_year: int = TRADING_DAYS_PER_YEAR,
) -> pd.DataFrame

Sample covariance matrix of asset returns (ddof=1), optionally annualized by linear scaling.

Parameters:

Name Type Description Default
returns DataFrame

Periodic returns, one column per asset.

required
annualized bool

If True, scale the covariance matrix by periods_per_year.

False
periods_per_year int

Number of return periods per year, used when annualized is True.

TRADING_DAYS_PER_YEAR

Returns:

Type Description
DataFrame

The (optionally annualized) covariance matrix, indexed and columned

DataFrame

by asset.

Source code in src/portpy/metrics/covariance.py
def covariance_matrix(
    returns: pd.DataFrame, annualized: bool = False, periods_per_year: int = TRADING_DAYS_PER_YEAR
) -> pd.DataFrame:
    """
    Sample covariance matrix of asset returns (ddof=1), optionally annualized
    by linear scaling.

    Args:
        returns: Periodic returns, one column per asset.
        annualized: If True, scale the covariance matrix by `periods_per_year`.
        periods_per_year: Number of return periods per year, used when
            `annualized` is True.

    Returns:
        The (optionally annualized) covariance matrix, indexed and columned
        by asset.
    """
    cov = returns.cov()
    return cov * periods_per_year if annualized else cov

correlation_matrix

correlation_matrix(returns: DataFrame) -> pd.DataFrame

Pairwise Pearson correlation matrix of asset returns.

Parameters:

Name Type Description Default
returns DataFrame

Periodic returns, one column per asset.

required

Returns:

Type Description
DataFrame

The correlation matrix, indexed and columned by asset.

Source code in src/portpy/metrics/covariance.py
def correlation_matrix(returns: pd.DataFrame) -> pd.DataFrame:
    """
    Pairwise Pearson correlation matrix of asset returns.

    Args:
        returns: Periodic returns, one column per asset.

    Returns:
        The correlation matrix, indexed and columned by asset.
    """
    return returns.corr()

portfolio_variance

portfolio_variance(
    weights: Series | ndarray,
    cov_matrix: DataFrame,
    as_result: bool = False,
) -> float | MetricResult

Portfolio return variance: w^T @ Cov @ w.

The result's time-scale (periodic vs. annualized) matches whatever cov_matrix was passed in.

Parameters:

Name Type Description Default
weights Series | ndarray

Portfolio weights, one per asset.

required
cov_matrix DataFrame

Covariance matrix of asset returns, columns matching the assets in weights.

required
as_result bool

If True, return a MetricResult instead of a plain float.

False

Returns:

Type Description
float | MetricResult

The portfolio variance, or a MetricResult wrapping it.

Source code in src/portpy/metrics/covariance.py
def portfolio_variance(weights: pd.Series | np.ndarray, cov_matrix: pd.DataFrame, as_result: bool = False) -> float | MetricResult:
    """
    Portfolio return variance: w^T @ Cov @ w.

    The result's time-scale (periodic vs. annualized) matches whatever
    `cov_matrix` was passed in.

    Args:
        weights: Portfolio weights, one per asset.
        cov_matrix: Covariance matrix of asset returns, columns matching
            the assets in `weights`.
        as_result: If True, return a `MetricResult` instead of a plain float.

    Returns:
        The portfolio variance, or a `MetricResult` wrapping it.
    """
    w = _align_weights(weights, cov_matrix)
    cov = cov_matrix.to_numpy() if isinstance(cov_matrix, pd.DataFrame) else np.asarray(cov_matrix)
    value = float(w @ cov @ w)
    return MetricResult(value, "portfolio_variance") if as_result else value

portfolio_volatility

portfolio_volatility(
    weights: Series | ndarray,
    cov_matrix: DataFrame,
    as_result: bool = False,
) -> float | MetricResult

Portfolio return standard deviation: sqrt(w^T @ Cov @ w).

The result's time-scale (periodic vs. annualized) matches whatever cov_matrix was passed in.

Parameters:

Name Type Description Default
weights Series | ndarray

Portfolio weights, one per asset.

required
cov_matrix DataFrame

Covariance matrix of asset returns, columns matching the assets in weights.

required
as_result bool

If True, return a MetricResult instead of a plain float.

False

Returns:

Type Description
float | MetricResult

The portfolio volatility, or a MetricResult wrapping it.

Source code in src/portpy/metrics/covariance.py
def portfolio_volatility(weights: pd.Series | np.ndarray, cov_matrix: pd.DataFrame, as_result: bool = False) -> float | MetricResult:
    """
    Portfolio return standard deviation: sqrt(w^T @ Cov @ w).

    The result's time-scale (periodic vs. annualized) matches whatever
    `cov_matrix` was passed in.

    Args:
        weights: Portfolio weights, one per asset.
        cov_matrix: Covariance matrix of asset returns, columns matching
            the assets in `weights`.
        as_result: If True, return a `MetricResult` instead of a plain float.

    Returns:
        The portfolio volatility, or a `MetricResult` wrapping it.
    """
    value = float(np.sqrt(portfolio_variance(weights, cov_matrix)))
    return MetricResult(value, "portfolio_volatility") if as_result else value

diversification_ratio

diversification_ratio(
    weights: Series | ndarray,
    cov_matrix: DataFrame,
    as_result: bool = False,
) -> float | MetricResult

Weighted-average standalone asset volatility divided by actual portfolio volatility - a measure of how much diversification is buying you.

Parameters:

Name Type Description Default
weights Series | ndarray

Portfolio weights, one per asset.

required
cov_matrix DataFrame

Covariance matrix of asset returns, columns matching the assets in weights.

required
as_result bool

If True, return a MetricResult instead of a plain float.

False

Returns:

Type Description
float | MetricResult

The diversification ratio, or a MetricResult wrapping it. NaN if

float | MetricResult

portfolio volatility is ~0.

Source code in src/portpy/metrics/covariance.py
def diversification_ratio(weights: pd.Series | np.ndarray, cov_matrix: pd.DataFrame, as_result: bool = False) -> float | MetricResult:
    """
    Weighted-average standalone asset volatility divided by actual portfolio
    volatility - a measure of how much diversification is buying you.

    Args:
        weights: Portfolio weights, one per asset.
        cov_matrix: Covariance matrix of asset returns, columns matching
            the assets in `weights`.
        as_result: If True, return a `MetricResult` instead of a plain float.

    Returns:
        The diversification ratio, or a `MetricResult` wrapping it. NaN if
        portfolio volatility is ~0.
    """
    w = _align_weights(weights, cov_matrix)
    cov = cov_matrix.to_numpy() if isinstance(cov_matrix, pd.DataFrame) else np.asarray(cov_matrix)
    asset_vols = np.sqrt(np.diag(cov))
    weighted_avg_vol = float(w @ asset_vols)
    port_vol = float(np.sqrt(w @ cov @ w))
    value = weighted_avg_vol / port_vol if port_vol > 1e-15 else np.nan
    return MetricResult(value, "diversification_ratio") if as_result else value

marginal_contribution_to_risk

marginal_contribution_to_risk(
    weights: Series | ndarray, cov_matrix: DataFrame
) -> np.ndarray

Marginal Contribution to Risk (MCTR): how much portfolio volatility changes per unit change in each asset's weight.

Parameters:

Name Type Description Default
weights Series | ndarray

Portfolio weights, one per asset.

required
cov_matrix DataFrame

Covariance matrix of asset returns, columns matching the assets in weights.

required

Returns:

Type Description
ndarray

An array of MCTR values, one per asset, in weights' order.

Source code in src/portpy/metrics/covariance.py
def marginal_contribution_to_risk(weights: pd.Series | np.ndarray, cov_matrix: pd.DataFrame) -> np.ndarray:
    """
    Marginal Contribution to Risk (MCTR): how much portfolio volatility
    changes per unit change in each asset's weight.

    Args:
        weights: Portfolio weights, one per asset.
        cov_matrix: Covariance matrix of asset returns, columns matching
            the assets in `weights`.

    Returns:
        An array of MCTR values, one per asset, in `weights`' order.
    """
    w = _align_weights(weights, cov_matrix)
    cov = cov_matrix.to_numpy() if isinstance(cov_matrix, pd.DataFrame) else np.asarray(cov_matrix)
    port_vol = float(np.sqrt(w @ cov @ w))
    if port_vol < 1e-15:
        return np.zeros_like(w)
    return (cov @ w) / port_vol

component_contribution_to_risk

component_contribution_to_risk(
    weights: Series | ndarray, cov_matrix: DataFrame
) -> np.ndarray

Component Contribution to Risk (CCTR): each asset's slice of total portfolio volatility, computed as weight_i * MCTR_i. Components sum exactly to portfolio volatility.

Parameters:

Name Type Description Default
weights Series | ndarray

Portfolio weights, one per asset.

required
cov_matrix DataFrame

Covariance matrix of asset returns, columns matching the assets in weights.

required

Returns:

Type Description
ndarray

An array of CCTR values, one per asset, in weights' order.

Source code in src/portpy/metrics/covariance.py
def component_contribution_to_risk(weights: pd.Series | np.ndarray, cov_matrix: pd.DataFrame) -> np.ndarray:
    """
    Component Contribution to Risk (CCTR): each asset's slice of total
    portfolio volatility, computed as `weight_i * MCTR_i`. Components sum
    exactly to portfolio volatility.

    Args:
        weights: Portfolio weights, one per asset.
        cov_matrix: Covariance matrix of asset returns, columns matching
            the assets in `weights`.

    Returns:
        An array of CCTR values, one per asset, in `weights`' order.
    """
    w = _align_weights(weights, cov_matrix)
    mctr = marginal_contribution_to_risk(w, cov_matrix)
    return w * mctr