Skip to content

markowitz.estimators

markowitz.estimators

Estimators for expected returns and asset covariance matrices.

AmbiguousFrequencyError

Bases: EstimatorError

Raised when annualization is requested but periods-per-year is undetermined.

CAPMReturns(*, risk_free_rate: float = 0.0, market_premium: float | None = None, annualize: bool = True, periods_per_year: int | None = None)

Bases: _BaseMean

CAPM-implied expected returns from per-asset OLS betas.

Source code in src/markowitz/estimators/means.py
def __init__(
    self,
    *,
    risk_free_rate: float = 0.0,
    market_premium: float | None = None,
    annualize: bool = True,
    periods_per_year: int | None = None,
) -> None:
    self.risk_free_rate = float(risk_free_rate)
    self.market_premium = market_premium
    self.annualize = bool(annualize)
    self.periods_per_year = periods_per_year

CovarianceEstimator

Bases: Protocol

Protocol for an estimator that produces a covariance matrix.

DimensionMismatchError

Bases: EstimatorError

Raised when input arrays have incompatible shapes.

EWMACovariance(*, halflife_years: float | None = None, lam: float | None = None, burn_in: int | Literal['auto'] = 'auto', annualize: bool = True, periods_per_year: int | None = None)

Bases: _BaseCov

Exponentially weighted covariance (RiskMetrics recursion).

Source code in src/markowitz/estimators/covariance.py
def __init__(
    self,
    *,
    halflife_years: float | None = None,
    lam: float | None = None,
    burn_in: int | Literal["auto"] = "auto",
    annualize: bool = True,
    periods_per_year: int | None = None,
) -> None:
    if halflife_years is not None and lam is not None:
        raise EstimatorConfigError("Provide at most one of halflife_years or lam, not both.")
    self.halflife_years = halflife_years
    self.lam = lam
    self.burn_in = burn_in
    self.annualize = bool(annualize)
    self.periods_per_year = periods_per_year

EWMAMean(*, halflife_years: float | None = None, lam: float | None = None, annualize: bool = True, periods_per_year: int | None = None)

Bases: _BaseMean

Exponentially weighted mean using RiskMetrics-style decay.

Source code in src/markowitz/estimators/means.py
def __init__(
    self,
    *,
    halflife_years: float | None = None,
    lam: float | None = None,
    annualize: bool = True,
    periods_per_year: int | None = None,
) -> None:
    if (halflife_years is None) == (lam is None):
        raise EstimatorConfigError("Exactly one of halflife_years or lam must be provided.")
    self.halflife_years = halflife_years
    self.lam = lam
    self.annualize = bool(annualize)
    self.periods_per_year = periods_per_year

EstimatorConfigError

Bases: EstimatorError

Raised when an estimator is constructed with an invalid configuration.

EstimatorError

Bases: Exception

Base class for all estimator-related errors.

ImpliedReturns(*, delta: float = 2.5)

Bases: _BaseMean

Reverse-optimization implied returns μ = δ · Σ · w.

Source code in src/markowitz/estimators/means.py
def __init__(self, *, delta: float = 2.5) -> None:
    if delta <= 0.0:
        raise EstimatorConfigError(f"delta must be positive; got {delta}")
    self.delta = float(delta)

JorionBayesStein(*, base_cov: np.ndarray | None = None, annualize: bool = True, periods_per_year: int | None = None)

Bases: _BaseMean

Bayes-Stein shrinkage of the sample mean toward the minimum-variance mean.

Source code in src/markowitz/estimators/means.py
def __init__(
    self,
    *,
    base_cov: np.ndarray | None = None,
    annualize: bool = True,
    periods_per_year: int | None = None,
) -> None:
    self.base_cov = base_cov
    self.annualize = bool(annualize)
    self.periods_per_year = periods_per_year

LedoitWolfShrinkage(*, target: Literal['identity', 'constant_corr'] = 'identity', annualize: bool = True, periods_per_year: int | None = None)

Bases: _BaseCov

Ledoit-Wolf shrinkage covariance, sklearn-compatible for the identity target.

Source code in src/markowitz/estimators/covariance.py
def __init__(
    self,
    *,
    target: Literal["identity", "constant_corr"] = "identity",
    annualize: bool = True,
    periods_per_year: int | None = None,
) -> None:
    if target not in ("identity", "constant_corr"):
        raise EstimatorConfigError(
            f"target must be 'identity' or 'constant_corr'; got {target!r}"
        )
    self.target = target
    self.annualize = bool(annualize)
    self.periods_per_year = periods_per_year

MeanEstimator

Bases: Protocol

Protocol for an estimator that produces an expected-returns vector.

NotFittedError

Bases: EstimatorError

Raised when accessing fitted attributes before calling .fit.

SampleCovariance(*, annualize: bool = True, periods_per_year: int | None = None, ddof: int = 1)

Bases: _BaseCov

Plain sample covariance np.cov(returns.T, ddof=ddof).

Source code in src/markowitz/estimators/covariance.py
def __init__(
    self,
    *,
    annualize: bool = True,
    periods_per_year: int | None = None,
    ddof: int = 1,
) -> None:
    if ddof < 0:
        raise EstimatorConfigError(f"ddof must be >= 0; got {ddof}")
    self.annualize = bool(annualize)
    self.periods_per_year = periods_per_year
    self.ddof = int(ddof)

SampleMean(*, annualize: bool = True, periods_per_year: int | None = None)

Bases: _BaseMean

Plain sample mean μ̂ = mean(returns, axis=0).

Source code in src/markowitz/estimators/means.py
def __init__(
    self,
    *,
    annualize: bool = True,
    periods_per_year: int | None = None,
) -> None:
    self.annualize = bool(annualize)
    self.periods_per_year = periods_per_year