Portfolio research¶
Import public construction and backtesting functions, policies, and result types from
persistra.portfolio.
Continuous optimization¶
persistra.portfolio.optimization
¶
Continuous portfolio optimization with explicit objectives and constraints.
_DIAGNOSTIC_COLUMNS = ['value', 'lower', 'upper', 'residual', 'binding']
module-attribute
¶
OptimizationFailurePolicy = Literal['raise', 'hold_previous']
¶
PortfolioConstraint = WeightBounds | GrossExposureConstraint | NetExposureConstraint | TurnoverConstraint | FactorExposureConstraint | LinearExposureConstraint | GroupedExposureConstraint | RiskBudgetConstraint | ConditionalValueAtRiskConstraint | TrackingErrorConstraint
¶
PortfolioPenalty = LinearTransactionCostPenalty | AsymmetricTransactionCostPenalty | QuadraticTransactionCostPenalty
¶
_Array = NDArray[np.float64]
¶
AnalysisError
¶
Bases: PersistraError, ValueError
Raised when data violates a mathematical assumption.
ActiveMeanVarianceObjective
dataclass
¶
Maximize expected active return against tracking-error variance.
AsymmetricTransactionCostPenalty
dataclass
¶
Separate linear buy and sell cost rates with one objective multiplier.
ConditionalValueAtRiskConstraint
dataclass
¶
Maximum empirical loss CVaR at the requested confidence level.
ConditionalValueAtRiskObjective
dataclass
¶
Minimize empirical loss CVaR at the requested confidence level.
CovariancePolicy
dataclass
¶
Explicit diagonal shrinkage and eigenvalue-floor conditioning policy.
EllipsoidalExpectedReturnUncertainty
dataclass
¶
Ellipsoidal expected-return uncertainty matrix and radius.
FactorExposureConstraint
dataclass
¶
Lower and upper bounds for every supplied factor exposure.
GrossExposureConstraint
dataclass
¶
Upper bound on total absolute risky-asset weight.
GroupedExposureConstraint
dataclass
¶
Bounds for stable or dated caller-defined asset groups.
LinearExposureConstraint
dataclass
¶
Bound caller-defined linear loadings without assigning column semantics.
LinearTransactionCostPenalty
dataclass
¶
Linear risky-asset trading-cost rates and objective multiplier.
MeanVarianceObjective
dataclass
¶
Maximize expected return against an explicit variance penalty.
MinimumTrackingErrorObjective
dataclass
¶
Minimize variance relative to supplied benchmark weights.
MinimumVarianceObjective
dataclass
¶
Minimize total portfolio variance.
NetExposureConstraint
dataclass
¶
Lower and upper bounds on signed risky-asset weight.
PortfolioOptimizationPathResult
dataclass
¶
Ordered point-in-time portfolio optimization steps and aligned targets.
PortfolioOptimizationResult
dataclass
¶
Optimal weights with objective, risk, exposure, and constraint diagnostics.
PortfolioOptimizationStep
dataclass
¶
One dated optimized or explicitly held portfolio in a path.
PortfolioProblem
dataclass
¶
One solver-independent continuous portfolio optimization problem.
QuadraticTransactionCostPenalty
dataclass
¶
Asset-specific quadratic market-impact rates and objective multiplier.
RiskBudgetConstraint
dataclass
¶
Target or cap asset and grouped fractional contributions to variance.
RiskParityObjective
dataclass
¶
Minimize squared differences between realized and requested risk budgets.
RobustMeanVarianceObjective
dataclass
¶
Mean variance with an ellipsoidal worst-case expected-return penalty.
TrackingErrorConstraint
dataclass
¶
Upper bound on portfolio volatility relative to a benchmark.
TurnoverConstraint
dataclass
¶
Upper bound on one-way turnover from current risky and residual-cash weights.
WeightBounds
dataclass
¶
Per-asset lower and upper portfolio-weight bounds.
PortfolioSolver
¶
Bases: Protocol
Solve one continuous solver-neutral portfolio problem.
PortfolioSolverProblem
dataclass
¶
One differentiable continuous problem ready for a numerical backend.
ScipySlsqpSolver
dataclass
¶
SolverConstraint
dataclass
¶
One equality or nonnegative inequality in solver-neutral form.
FactorRiskModel
dataclass
¶
Factor and idiosyncratic components of one asset covariance estimate.
manifest_parameters: Mapping[str, Any]
property
¶
Return portable covariance settings for a research manifest.
_Layout
dataclass
¶
_Inputs
dataclass
¶
require_integer(value: object, *, name: str, minimum: int | None = None) -> int
¶
Return a normalized integer after enforcing an optional inclusive minimum.
optimize_portfolio(problem: PortfolioProblem, *, tolerance: float = 1e-09, maximum_iterations: int = 1000, initial_weights: pd.Series | None = None, solver: PortfolioSolver | None = None) -> PortfolioOptimizationResult
¶
Solve one continuous portfolio problem and validate the returned constraints.
Covariance values use the caller's return frequency. Expected returns, variance,
tracking error, and cost rates must use a compatible scale. The residual cash weight is
always 1 - sum(weights). A NetExposureConstraint(1, 1) therefore expresses a
fully invested risky portfolio.
The optimizer raises AnalysisError when the problem is infeasible, the numerical
solver fails, or the returned point violates a requested constraint beyond tolerance.
_objective_feature(objective: MinimumVarianceObjective | MeanVarianceObjective | MinimumTrackingErrorObjective | ActiveMeanVarianceObjective | RiskParityObjective | ConditionalValueAtRiskObjective | RobustMeanVarianceObjective) -> str
¶
_penalty_feature(penalty: PortfolioPenalty) -> str
¶
_constraint_feature(constraint: PortfolioConstraint) -> str
¶
optimize_portfolio_path(problems: tuple[PortfolioProblem, ...], *, failure_policy: OptimizationFailurePolicy = 'raise', tolerance: float = 1e-09, maximum_iterations: int = 1000, solver: PortfolioSolver | None = None) -> PortfolioOptimizationPathResult
¶
Solve ordered dated problems while carrying the preceding portfolio forward.
_problem_inputs(problem: PortfolioProblem) -> _Inputs
¶
_covariance(frame: pd.DataFrame, policy: CovariancePolicy) -> tuple[_Array, pd.Series]
¶
_aligned_series(values: pd.Series | None, assets: pd.Index, *, name: str, default: float) -> _Array
¶
_factor_exposure_frame(exposures: pd.DataFrame | None, assets: pd.Index) -> pd.DataFrame | None
¶
_scenario_return_frame(scenarios: pd.DataFrame | None, assets: pd.Index) -> pd.DataFrame | None
¶
_uncertainty_matrix(uncertainty: EllipsoidalExpectedReturnUncertainty, assets: pd.Index) -> _Array
¶
_validate_constraint_types(constraints: tuple[PortfolioConstraint, ...]) -> None
¶
resolve_grouped_exposure(constraint: GroupedExposureConstraint, assets: pd.Index, *, as_of: pd.Timestamp | None = None) -> LinearExposureConstraint
¶
Return validated linear loadings and bounds for one grouped constraint.
_dated_group_loadings(memberships: pd.DataFrame, assets: pd.Index, *, as_of: pd.Timestamp | None) -> pd.DataFrame
¶
_group_bounds(values: float | pd.Series, groups: pd.Index, *, name: str) -> pd.Series
¶
_linear_bounds(constraint: LinearExposureConstraint, assets: pd.Index) -> tuple[_Array, _Array]
¶
_weight_bounds(constraints: tuple[PortfolioConstraint, ...], assets: pd.Index) -> tuple[_Array, _Array]
¶
_bound_values(values: float | pd.Series, assets: pd.Index, *, name: str) -> _Array
¶
_transaction_costs(penalties: tuple[PortfolioPenalty, ...], assets: pd.Index) -> tuple[_Array, _Array, _Array]
¶
_nonnegative_values(values: float | pd.Series, assets: pd.Index, *, name: str) -> _Array
¶
_risk_budget_values(values: pd.Series | None, labels: pd.Index, *, name: str, default_equal: bool = False) -> _Array
¶
_validate_risk_budget_constraint(constraint: RiskBudgetConstraint, assets: pd.Index) -> None
¶
_factor_bounds(constraint: FactorExposureConstraint, exposures: pd.DataFrame | None) -> tuple[_Array, _Array]
¶
_layout(assets: int, *, use_gross: bool, use_trades: bool) -> _Layout
¶
_initial_point(inputs: _Inputs, layout: _Layout, *, initial_weights: pd.Series | None) -> _Array
¶
_problem_assets(problem: PortfolioProblem) -> pd.Index
¶
_bounds(inputs: _Inputs, layout: _Layout) -> list[tuple[float | None, float | None]]
¶
_solver_constraints(inputs: _Inputs, layout: _Layout) -> list[SolverConstraint]
¶
_inequality(function: Callable[[_Array], _Array | float]) -> SolverConstraint
¶
_equality(function: Callable[[_Array], _Array | float]) -> SolverConstraint
¶
_objective_functions(inputs: _Inputs, layout: _Layout) -> tuple[Callable[[_Array], float], Callable[[_Array], _Array]]
¶
_base_objective(inputs: _Inputs, weights: _Array) -> tuple[float, _Array]
¶
_fractional_risk_contributions(weights: _Array, covariance: _Array) -> _Array
¶
_risk_contribution_values_and_jacobian(weights: _Array, covariance: _Array) -> tuple[_Array, _Array]
¶
_empirical_cvar(weights: _Array, scenario_returns: _Array, confidence_level: float) -> tuple[float, _Array, _Array]
¶
_constraint_diagnostics(inputs: _Inputs, weights: _Array, *, tolerance: float) -> pd.DataFrame
¶
_diagnostic_row(rows: list[dict[str, float | bool]], names: list[str], *, name: str, value: float, lower: float, upper: float, tolerance: float) -> None
¶
_turnover(weights: _Array, current: _Array) -> float
¶
_factor_exposure(exposures: pd.DataFrame | None, weights: _Array) -> pd.Series
¶
_linear_exposure(constraints: tuple[LinearExposureConstraint, ...], weights: _Array) -> pd.Series
¶
_risk_contributions(inputs: _Inputs, weights: _Array) -> pd.Series
¶
_risk_budget_diagnostics(inputs: _Inputs, contributions: pd.Series, *, tolerance: float) -> pd.DataFrame
¶
_downside_diagnostics(inputs: _Inputs, weights: _Array) -> tuple[pd.Series, pd.DataFrame]
¶
_objective_breakdown(inputs: _Inputs, weights: _Array) -> pd.Series
¶
_positive_float(value: float, *, name: str) -> float
¶
Solver boundary¶
persistra.portfolio.solver
¶
Solver-neutral optimization boundaries and supported backends.
SolverArray = NDArray[np.float64]
¶
SolverFeature = str
¶
PortfolioSolverStatus = Literal['optimal', 'feasible', 'infeasible', 'unbounded', 'iteration_limit', 'solver_error']
¶
PortfolioSolverCapabilities
dataclass
¶
Features that one portfolio solver backend accepts exactly.
unsupported(problem: PortfolioSolverProblem) -> tuple[str, ...]
¶
Return stable descriptions of problem features the backend rejects.
SolverConstraint
dataclass
¶
One equality or nonnegative inequality in solver-neutral form.
PortfolioSolverProblem
dataclass
¶
One differentiable continuous problem ready for a numerical backend.
PortfolioSolverResult
dataclass
¶
Normalized numerical result returned by a portfolio solver backend.
PortfolioSolver
¶
Bases: Protocol
Solve one continuous solver-neutral portfolio problem.
DiscretePortfolioSolverProblem
dataclass
¶
Convex mixed-integer quadratic problem in nonnegative integer lots.
DiscretePortfolioSolver
¶
Bases: Protocol
Solve one explicit mixed-integer portfolio problem without relaxation.
_ScipyResult
¶
Bases: Protocol
ScipySlsqpSolver
dataclass
¶
CvxpySolver
dataclass
¶
Solve compatible convex quadratic problems through optional CVXPY.
name: str
property
¶
Return the stable backend and concrete solver identity.
capabilities: PortfolioSolverCapabilities
property
¶
Advertise the affine-constrained convex problem subset.
solve(problem: PortfolioSolverProblem) -> PortfolioSolverResult
¶
Canonicalize a quadratic problem and solve it through CVXPY.
CvxpyMixedIntegerSolver
dataclass
¶
Solve convex mixed-integer quadratic portfolios with optional SCIP.
require_integer(value: object, *, name: str, minimum: int | None = None) -> int
¶
Return a normalized integer after enforcing an optional inclusive minimum.
_require_supported(name: str, capabilities: PortfolioSolverCapabilities, problem: PortfolioSolverProblem) -> None
¶
_cvxpy_module(backend: str) -> Any
¶
_solver_error(values: SolverArray, error: Exception) -> PortfolioSolverResult
¶
_mixed_integer_statistics(model: Any) -> tuple[dict[str, float | int | str], float | None, float | None]
¶
_cvxpy_status(status: str) -> PortfolioSolverStatus
¶
_quadratic_objective(problem: PortfolioSolverProblem) -> tuple[SolverArray, SolverArray, float]
¶
Recover the exact quadratic form exposed by the neutral gradient boundary.
_affine_constraint(constraint: SolverConstraint, size: int) -> tuple[SolverArray, SolverArray]
¶
Recover an affine constraint matrix from its solver-neutral function.
Target construction and schedules¶
persistra.portfolio.construction
¶
Transparent target-weight construction for portfolio research.
PortfolioConfiguration = Literal['long_only', 'long_short']
¶
WeightingMethod = Literal['equal', 'signal_proportional']
¶
AnalysisError
¶
Bases: PersistraError, ValueError
Raised when data violates a mathematical assumption.
PortfolioConstraints
dataclass
¶
Hard exposure, position, and one-way turnover limits.
PortfolioConstructionResult
dataclass
¶
Transparent unconstrained and final target portfolio weights.
PortfolioRiskControl
dataclass
¶
Annualized volatility target and ceiling based on supplied covariance.
require_integer(value: object, *, name: str, minimum: int | None = None) -> int
¶
Return a normalized integer after enforcing an optional inclusive minimum.
asset_panel(frame: pd.DataFrame, *, name: str) -> pd.DataFrame
¶
Copy a numeric date-by-asset panel with a fixed universe.
datetime_index(index: pd.Index, *, name: str) -> pd.DatetimeIndex
¶
Validate a sorted, unique datetime index.
finite_scalar(value: float, *, name: str, minimum: float | None = None) -> float
¶
Validate one finite scalar and an optional inclusive lower bound.
rebalance_schedule(index: pd.DatetimeIndex, *, frequency: Literal['daily', 'weekly', 'monthly', 'quarterly'] | int, anchor: Literal['start', 'end'] = 'end') -> pd.DatetimeIndex
¶
Select deterministic observation dates for a rebalance schedule.
Calendar schedules choose the first or last supplied observation in each calendar
bucket. An integer schedule chooses every frequency observations starting with the
first observation; its anchor must be "start".
construct_portfolio(signals: pd.DataFrame, *, weighting: WeightingMethod = 'equal', configuration: PortfolioConfiguration = 'long_only', gross_target: float = 1.0, net_target: float | None = None, constraints: PortfolioConstraints | None = None, covariances: Mapping[pd.Timestamp, pd.DataFrame] | None = None, risk_control: PortfolioRiskControl | None = None, initial_weights: pd.Series | None = None) -> PortfolioConstructionResult
¶
Construct date-by-asset targets from an explicit signal panel.
Equal weighting ignores signal magnitude. In long-only mode, every observed asset is eligible. In long-short mode, signal signs define sides. Signal-proportional weighting uses positive signals for long-only portfolios and absolute signal magnitude within each side for long-short portfolios.
Position limits use deterministic capped redistribution within each side. Volatility
controls scale the whole risky portfolio and never change relative asset weights. A
turnover limit blends each desired target with the preceding target. Infeasible exposure,
side-capacity, covariance, or interacting risk constraints raise AnalysisError.
_validate_requested_exposure(gross: float, net: float, *, configuration: PortfolioConfiguration, constraints: PortfolioConstraints) -> None
¶
_initial_weights(initial: pd.Series | None, columns: pd.Index, *, constraints: PortfolioConstraints) -> np.ndarray
¶
_unconstrained_row(signals: np.ndarray, *, weighting: WeightingMethod, configuration: PortfolioConfiguration, gross: float, net: float, date: pd.Timestamp) -> np.ndarray
¶
_allocate_uncapped(scores: np.ndarray, mask: np.ndarray, *, budget: float, side: str, date: pd.Timestamp) -> np.ndarray
¶
_constrained_sides(raw: np.ndarray, *, gross: float, net: float, position_limit: float, tolerance: float, date: pd.Timestamp) -> np.ndarray
¶
_capped_allocation(scores: np.ndarray, *, budget: float, limit: float, tolerance: float, side: str, date: pd.Timestamp) -> np.ndarray
¶
_covariance_matrix(covariance: pd.DataFrame, columns: pd.Index, *, tolerance: float, date: pd.Timestamp) -> np.ndarray
¶
_apply_risk_control(weights: np.ndarray, covariance: np.ndarray, *, constraints: PortfolioConstraints, risk_control: PortfolioRiskControl, date: pd.Timestamp) -> np.ndarray
¶
_scale_bounds(weights: np.ndarray, *, constraints: PortfolioConstraints) -> tuple[float, float]
¶
_annualized_volatility(weights: np.ndarray, covariance: np.ndarray, *, periods_per_year: float) -> float
¶
_validate_final_constraints(weights: np.ndarray, *, constraints: PortfolioConstraints, date: pd.Timestamp | None) -> None
¶
_turnover(previous: np.ndarray, target: np.ndarray, previous_cash: float, target_cash: float) -> float
¶
_exposures(weights: np.ndarray, cash: float) -> dict[str, float]
¶
_constraint_utilization(weights: np.ndarray, *, exposures: dict[str, float], turnover: float, predicted_volatility: float, constraints: PortfolioConstraints, risk_control: PortfolioRiskControl | None) -> dict[str, float]
¶
Vectorized backtesting¶
persistra.portfolio.backtest
¶
Portfolio-level vectorized backtesting with explicit timing and accounting.
MissingCostPolicy = Literal['error', 'zero']
¶
ReturnAdjustment = Literal['unadjusted', 'adjusted']
¶
AnalysisError
¶
Bases: PersistraError, ValueError
Raised when data violates a mathematical assumption.
BacktestPolicies
dataclass
¶
Explicit missing-return and nontradeable-asset behavior.
BacktestResult
dataclass
¶
Reconciled portfolio-level backtest paths and benchmark comparisons.
BacktestTiming
dataclass
¶
Signal-to-decision and decision-to-holding timing policy.
BorrowPolicy
dataclass
¶
Missing-rate and unavailable-short behavior for portfolio backtests.
CorporateAction
dataclass
¶
One dated, sourced portfolio-security lifecycle event.
MarketImpactModel
dataclass
¶
Nonlinear participation impact calibrated in basis points.
MultiCurrencyPolicy
dataclass
¶
Base currency, asset currencies, and bounded missing-FX behavior.
PortfolioConstructionResult
dataclass
¶
Transparent unconstrained and final target portfolio weights.
_TimingPlan
dataclass
¶
_Simulation
dataclass
¶
asset_panel(frame: pd.DataFrame, *, name: str) -> pd.DataFrame
¶
Copy a numeric date-by-asset panel with a fixed universe.
finite_scalar(value: float, *, name: str, minimum: float | None = None) -> float
¶
Validate one finite scalar and an optional inclusive lower bound.
backtest_portfolio(target_weights: PortfolioConstructionResult | pd.DataFrame, *, returns: pd.DataFrame | None = None, prices: pd.DataFrame | None = None, timing: BacktestTiming | None = None, policies: BacktestPolicies | None = None, transaction_cost_bps: float | pd.Series | pd.DataFrame = 0.0, buy_cost_bps: float | pd.Series | pd.DataFrame | None = None, sell_cost_bps: float | pd.Series | pd.DataFrame | None = None, missing_cost: MissingCostPolicy = 'error', market_impact: MarketImpactModel | None = None, liquidity: pd.DataFrame | None = None, shortable: pd.DataFrame | None = None, borrow_rates: float | pd.Series | pd.DataFrame = 0.0, borrow_policy: BorrowPolicy | None = None, fx_rates: pd.DataFrame | None = None, multi_currency: MultiCurrencyPolicy | None = None, corporate_actions: Sequence[CorporateAction] = (), return_adjustment: ReturnAdjustment = 'unadjusted', cash_returns: float | pd.Series = 0.0, tradeable: pd.DataFrame | None = None, benchmarks: Mapping[str, pd.DataFrame | pd.Series] | None = None, initial_equity: float = 1.0, tolerance: float = 1e-10) -> BacktestResult
¶
Simulate rebalances to supplied targets over returns or prices.
Target row dates are signal-observation dates. BacktestTiming maps each observation
to a decision date and first holding period. The default applies a target one period after
its signal observation. Zero total lag is rejected unless the caller asserts that the
signal was available before the assumed trade.
Trade costs may be scalar, asset-specific, or dated and asymmetric. Optional impact uses supplied liquidity, while borrow fees accrue separately on beginning short weights. Cash is the residual needed to make beginning weights sum to one; it can be greater than one after short sales or negative under net leverage. Returns, cash, holdings, and every cost component reconcile in the returned result.
Sourced corporate actions can add dividend cash, normalize unadjusted split returns, or replace a missing observation with a terminal return and liquidate the asset. Adjusted-return inputs explicitly suppress dividend and split application to prevent double counting.
A benchmark value can be a static weight series or a date-by-asset target panel. Static weights enter on the first strategy signal date and then drift. Panel benchmarks use the same timing and policies as the strategy. This supports explicit static and naive-signal comparisons without hiding their definitions.
_target_panel(targets: PortfolioConstructionResult | pd.DataFrame) -> pd.DataFrame
¶
_market_returns(*, returns: pd.DataFrame | None, prices: pd.DataFrame | None) -> pd.DataFrame
¶
_cost_rate_panel(costs: float | pd.Series | pd.DataFrame, index: pd.DatetimeIndex, columns: pd.Index, *, name: str, missing: MissingCostPolicy) -> tuple[np.ndarray, np.ndarray]
¶
_corporate_action_inputs(actions: Sequence[CorporateAction], local_returns: pd.DataFrame, *, return_adjustment: ReturnAdjustment) -> tuple[pd.DataFrame, np.ndarray, np.ndarray, pd.DataFrame]
¶
_resolved_fx_rates(fx_rates: pd.DataFrame, index: pd.DatetimeIndex, assets: pd.Index, policy: MultiCurrencyPolicy) -> tuple[pd.DataFrame, pd.DataFrame]
¶
_rate_panel(rates: float | pd.Series | pd.DataFrame, index: pd.DatetimeIndex, columns: pd.Index, *, name: str, missing: MissingCostPolicy, scale: float) -> tuple[np.ndarray, np.ndarray]
¶
_liquidity_panel(liquidity: pd.DataFrame | None, market_returns: pd.DataFrame, *, required: bool, missing: MissingCostPolicy) -> tuple[np.ndarray, np.ndarray]
¶
_cash_return_path(cash_returns: float | pd.Series, index: pd.DatetimeIndex) -> np.ndarray
¶
_tradeable_panel(tradeable: pd.DataFrame | None, market_returns: pd.DataFrame) -> np.ndarray
¶
_boolean_panel(values: pd.DataFrame | None, market_returns: pd.DataFrame, *, name: str) -> np.ndarray
¶
_timing_plan(targets: pd.DataFrame, return_index: pd.DatetimeIndex, timing: BacktestTiming) -> _TimingPlan
¶
_simulate(targets: pd.DataFrame, market_returns: pd.DataFrame, *, local_returns: np.ndarray, fx_returns: np.ndarray, dividend_yields: np.ndarray, delistings: np.ndarray, plan: _TimingPlan, policies: BacktestPolicies, buy_cost_rates: np.ndarray, sell_cost_rates: np.ndarray, market_impact: MarketImpactModel | None, liquidity: np.ndarray, borrow_rates: np.ndarray, shortable: np.ndarray, borrow_policy: BorrowPolicy, cash_returns: np.ndarray, tradeable: np.ndarray, tolerance: float) -> _Simulation
¶
_impact_costs(trades: np.ndarray, liquidity: np.ndarray, model: MarketImpactModel | None, *, date: object, columns: pd.Index, tolerance: float) -> np.ndarray
¶
_exposures(weights: np.ndarray, cash: float) -> dict[str, float]
¶
_run_benchmarks(benchmarks: Mapping[str, pd.DataFrame | pd.Series] | None, *, strategy_targets: pd.DataFrame, market_returns: pd.DataFrame, local_returns: np.ndarray, fx_returns: np.ndarray, dividend_yields: np.ndarray, delistings: np.ndarray, timing: BacktestTiming, policies: BacktestPolicies, buy_cost_rates: np.ndarray, sell_cost_rates: np.ndarray, market_impact: MarketImpactModel | None, liquidity: np.ndarray, borrow_rates: np.ndarray, shortable: np.ndarray, borrow_policy: BorrowPolicy, cash_returns: np.ndarray, tradeable: np.ndarray, initial_equity: float, tolerance: float) -> tuple[pd.DataFrame, pd.DataFrame]
¶
_compare_benchmarks(strategy: pd.Series, benchmarks: pd.DataFrame) -> pd.DataFrame
¶
Performance reporting¶
persistra.portfolio.performance
¶
Transparent, reconciled portfolio performance reporting.
BacktestResult
dataclass
¶
Reconciled portfolio-level backtest paths and benchmark comparisons.
PortfolioPerformanceReport
dataclass
¶
Performance metrics, benchmark comparisons, and accounting aggregates.
finite_scalar(value: float, *, name: str, minimum: float | None = None) -> float
¶
Validate one finite scalar and an optional inclusive lower bound.
portfolio_performance_report(result: BacktestResult, *, periods_per_year: float, risk_free_returns: float | pd.Series) -> PortfolioPerformanceReport
¶
Summarize a backtest with explicit annualization and risk-free assumptions.
_risk_free_path(values: float | pd.Series, index: pd.Index) -> pd.Series
¶
_annualized_geometric(returns: np.ndarray, periods_per_year: float) -> float
¶
_annualized_deviation(returns: np.ndarray, periods_per_year: float) -> float
¶
_maximum_drawdown_duration(drawdown: pd.Series) -> int
¶
_attribution(result: BacktestResult) -> pd.Series
¶
_coverage(result: BacktestResult) -> pd.Series
¶
_benchmark_report(result: BacktestResult, periods_per_year: float) -> pd.DataFrame
¶
Policies and results¶
persistra.portfolio.model
¶
Typed policies and results for portfolio research.
PortfolioSolverStatus = Literal['optimal', 'feasible', 'infeasible', 'unbounded', 'iteration_limit', 'solver_error']
¶
WeightingMethod = Literal['equal', 'signal_proportional']
¶
PortfolioConfiguration = Literal['long_only', 'long_short']
¶
MissingReturnPolicy = Literal['error', 'zero']
¶
NontradeablePolicy = Literal['error', 'hold']
¶
OptimizationFailurePolicy = Literal['raise', 'hold_previous']
¶
MissingMembershipPolicy = Literal['error', 'zero']
¶
OverlappingMembershipPolicy = Literal['error', 'allow']
¶
MissingCostPolicy = Literal['error', 'zero']
¶
UnavailableShortPolicy = Literal['error', 'cover']
¶
MissingFxPolicy = Literal['error', 'ffill']
¶
CorporateActionKind = Literal['cash_dividend', 'split', 'terminal_return']
¶
ReturnAdjustment = Literal['unadjusted', 'adjusted']
¶
PortfolioObjective = MinimumVarianceObjective | MeanVarianceObjective | MinimumTrackingErrorObjective | ActiveMeanVarianceObjective | RiskParityObjective | ConditionalValueAtRiskObjective | RobustMeanVarianceObjective
¶
PortfolioConstraint = WeightBounds | GrossExposureConstraint | NetExposureConstraint | TurnoverConstraint | FactorExposureConstraint | LinearExposureConstraint | GroupedExposureConstraint | RiskBudgetConstraint | ConditionalValueAtRiskConstraint | TrackingErrorConstraint
¶
PortfolioPenalty = LinearTransactionCostPenalty | AsymmetricTransactionCostPenalty | QuadraticTransactionCostPenalty
¶
FactorRiskModel
dataclass
¶
Factor and idiosyncratic components of one asset covariance estimate.
manifest_parameters: Mapping[str, Any]
property
¶
Return portable covariance settings for a research manifest.
PortfolioConstraints
dataclass
¶
Hard exposure, position, and one-way turnover limits.
PortfolioRiskControl
dataclass
¶
Annualized volatility target and ceiling based on supplied covariance.
MinimumVarianceObjective
dataclass
¶
Minimize total portfolio variance.
MeanVarianceObjective
dataclass
¶
Maximize expected return against an explicit variance penalty.
MinimumTrackingErrorObjective
dataclass
¶
Minimize variance relative to supplied benchmark weights.
ActiveMeanVarianceObjective
dataclass
¶
Maximize expected active return against tracking-error variance.
RiskParityObjective
dataclass
¶
Minimize squared differences between realized and requested risk budgets.
ConditionalValueAtRiskObjective
dataclass
¶
Minimize empirical loss CVaR at the requested confidence level.
EllipsoidalExpectedReturnUncertainty
dataclass
¶
Ellipsoidal expected-return uncertainty matrix and radius.
RobustMeanVarianceObjective
dataclass
¶
Mean variance with an ellipsoidal worst-case expected-return penalty.
WeightBounds
dataclass
¶
Per-asset lower and upper portfolio-weight bounds.
GrossExposureConstraint
dataclass
¶
Upper bound on total absolute risky-asset weight.
NetExposureConstraint
dataclass
¶
Lower and upper bounds on signed risky-asset weight.
TurnoverConstraint
dataclass
¶
Upper bound on one-way turnover from current risky and residual-cash weights.
FactorExposureConstraint
dataclass
¶
Lower and upper bounds for every supplied factor exposure.
LinearExposureConstraint
dataclass
¶
Bound caller-defined linear loadings without assigning column semantics.
GroupedExposureConstraint
dataclass
¶
Bounds for stable or dated caller-defined asset groups.
RiskBudgetConstraint
dataclass
¶
Target or cap asset and grouped fractional contributions to variance.
ConditionalValueAtRiskConstraint
dataclass
¶
Maximum empirical loss CVaR at the requested confidence level.
TrackingErrorConstraint
dataclass
¶
Upper bound on portfolio volatility relative to a benchmark.
LinearTransactionCostPenalty
dataclass
¶
Linear risky-asset trading-cost rates and objective multiplier.
AsymmetricTransactionCostPenalty
dataclass
¶
Separate linear buy and sell cost rates with one objective multiplier.
QuadraticTransactionCostPenalty
dataclass
¶
Asset-specific quadratic market-impact rates and objective multiplier.
CovariancePolicy
dataclass
¶
Explicit diagonal shrinkage and eigenvalue-floor conditioning policy.
PortfolioProblem
dataclass
¶
One solver-independent continuous portfolio optimization problem.
DiscretePortfolioProblem
dataclass
¶
Long-only portfolio problem expressed in integer trade lots.
DiscretePortfolioResult
dataclass
¶
Discrete holdings, bounds, and normalized mixed-integer diagnostics.
PortfolioOptimizationResult
dataclass
¶
Optimal weights with objective, risk, exposure, and constraint diagnostics.
PortfolioOptimizationStep
dataclass
¶
One dated optimized or explicitly held portfolio in a path.
PortfolioOptimizationPathResult
dataclass
¶
Ordered point-in-time portfolio optimization steps and aligned targets.
PortfolioConstructionResult
dataclass
¶
Transparent unconstrained and final target portfolio weights.
BacktestTiming
dataclass
¶
Signal-to-decision and decision-to-holding timing policy.
BacktestPolicies
dataclass
¶
Explicit missing-return and nontradeable-asset behavior.
MarketImpactModel
dataclass
¶
Nonlinear participation impact calibrated in basis points.
BorrowPolicy
dataclass
¶
Missing-rate and unavailable-short behavior for portfolio backtests.
MultiCurrencyPolicy
dataclass
¶
Base currency, asset currencies, and bounded missing-FX behavior.
CorporateAction
dataclass
¶
One dated, sourced portfolio-security lifecycle event.
BacktestResult
dataclass
¶
Reconciled portfolio-level backtest paths and benchmark comparisons.
require_integer(value: object, *, name: str, minimum: int | None = None) -> int
¶
Return a normalized integer after enforcing an optional inclusive minimum.
finite_scalar(value: float, *, name: str, minimum: float | None = None) -> float
¶
Validate one finite scalar and an optional inclusive lower bound.