Portfolio analytics

The abaquant.portfolio namespace provides allocation, risk metrics, efficient frontiers, rebalanced backtests, stress testing, and scenario analysis.

Input data

Most high-level workflows start from a pandas DataFrame of periodic returns:

import pandas as pd
from abaquant.portfolio import PortfolioAllocator

returns = pd.DataFrame(
    {
        "ALPHA": [0.01, -0.002, 0.006, 0.004],
        "BETA": [0.003, 0.005, -0.001, 0.002],
        "GAMMA": [-0.002, 0.007, 0.004, 0.006],
    }
)
allocator = PortfolioAllocator(returns, annual_risk_free_rate=0.02)

Rows are time observations. Columns are assets.

Allocation families

allocations = {
    "equal_weight": allocator.mean_variance.equal_weight(),
    "minimum_variance": allocator.mean_variance.minimum_variance(),
    "maximum_sharpe": allocator.mean_variance.maximum_sharpe(),
    "risk_parity": allocator.risk_based.risk_parity(),
    "inverse_volatility": allocator.risk_based.inverse_volatility(),
    "minimum_cvar": allocator.downside_risk.minimum_cvar(alpha=0.05),
}

Family

Methods

Objective

Mean-variance

equal weight, min variance, max Sharpe, max return

Optimize return/variance tradeoff.

Risk-based

risk parity, inverse vol, inverse variance, max diversification, HRP

Allocate by risk contribution or covariance geometry.

Downside-risk

min CVaR, min CDaR, max Sortino, max Calmar, max Omega

Focus on drawdown or lower-tail outcomes.

Efficient frontier

from abaquant.portfolio import markowitz_frontier, monte_carlo_portfolios

mean_returns = returns.mean() * 252
covariance = returns.cov() * 252
frontier = markowitz_frontier(mean_returns, covariance, n_points=25)
cloud = monte_carlo_portfolios(mean_returns, covariance, n_portfolios=1000, rf=0.02, seed=42)

The Markowitz frontier solves constrained mean-variance problems across return targets. Monte Carlo portfolios sample feasible weights and evaluate risk/return profiles.

Risk metrics

from abaquant.portfolio import portfolio_returns, compute_all_metrics

weights = [0.4, 0.35, 0.25]
series = portfolio_returns(returns, weights)
metrics = compute_all_metrics(series, rf=0.02)

Common metrics include:

Metric

Interpretation

Annualized return

Compounded or scaled return estimate.

Annualized volatility

Scaled standard deviation of returns.

Sharpe ratio

Excess return per unit of total volatility.

Sortino ratio

Excess return per unit of downside deviation.

Max drawdown

Worst peak-to-trough decline.

Calmar ratio

Return divided by max drawdown magnitude.

Historical VaR/CVaR

Empirical tail-loss thresholds.

CDaR

Conditional drawdown-at-risk.

Backtesting

backtest = allocator.backtest(
    weights="inverse_volatility",
    rebalance="monthly",
    transaction_cost_bps=5.0,
    slippage_bps=1.0,
    benchmark="equal_weight",
    lookback=10,
)

summary = backtest.summary()
equity_curve = backtest.equity_curve
report = backtest.report()

Backtests are deterministic historical simulations. They model rebalancing, turnover, transaction costs, slippage, benchmark comparison, rolling metrics, and drawdowns.

Stress testing

from abaquant.portfolio import run_all_scenarios

stress = run_all_scenarios(prices, weights)

Stress testing applies predefined or custom shocks to estimate portfolio sensitivity under adverse scenarios.

Visualization

fig = allocator.visualize(chart="correlation")
fig = allocator.visualize(weights=allocations["maximum_sharpe"], chart="weights")

Backtest objects also expose visualization methods for equity curves, drawdowns, rolling metrics, calendar returns, and contribution diagnostics.

Failure modes

Portfolio outputs are highly sensitive to:

  • expected-return estimation error;

  • covariance estimation error;

  • short history length;

  • unstable correlation regimes;

  • constraints and bounds;

  • transaction-cost assumptions;

  • sampling frequency;

  • survivorship and look-ahead bias in input data.