Portfolio Optimization and Quantitative Strategic Asset Allocation in Python
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Core portfolio optimization functionality providing classical mean-variance optimization, Black-Litterman models, factor models, worst-case optimization, and OWA (Ordered Weighted Averaging) optimization. The Portfolio class supports 4 objective functions and 24 risk measures for comprehensive portfolio construction.
Main portfolio optimization class with extensive configuration options and optimization methods.
class Portfolio:
def __init__(self, returns=None, sht=False, uppersht=0.2, upperlng=1,
budget=1, budgetsht=0.2, nea=None, card=None, factors=None,
B=None, alpha=0.05, a_sim=100, beta=None, b_sim=None,
kappa=0.30, kappa_g=None, n_max_kurt=50, kindbench=True,
allowTO=False, turnover=0.05, allowTE=False, TE=0.05,
benchindex=None, benchweights=None, ainequality=None,
binequality=None, arcinequality=None, brcinequality=None,
afrcinequality=None, bfrcinequality=None, b=None,
network_sdp=None, cluster_sdp=None, network_ip=None,
cluster_ip=None, graph_penalty=0.05, acentrality=None,
bcentrality=None, lowerret=None, upperdev=None, upperkt=None,
uppermad=None, uppergmd=None, uppersdev=None, upperskt=None,
upperflpm=None, upperslpm=None, upperCVaR=None, uppertg=None,
upperEVaR=None, upperRLVaR=None, upperwr=None, uppercvrg=None,
uppertgrg=None, upperevrg=None, upperrvrg=None, upperrg=None,
uppermdd=None, upperadd=None, upperCDaR=None, upperEDaR=None,
upperRLDaR=None, upperuci=None, p_1=2, p_2=3, p_3=4, p_4=10, p_5=50):
"""
Initialize Portfolio object with comprehensive configuration options.
Core Parameters:
- returns (DataFrame): Assets returns data (n_samples x n_assets)
- sht (bool): Allow short positions. Default: False
- uppersht (float): Maximum absolute value of short positions. Default: 0.2
- upperlng (float): Maximum value of long positions. Default: 1
- budget (float): Maximum sum of long and short positions. Default: 1
- budgetsht (float): Maximum sum of absolute short positions. Default: 0.2
Asset Selection:
- nea (int): Minimum number of effective assets. Default: None
- card (int): Maximum number of assets (requires MIP solver). Default: None
Factor Model:
- factors (DataFrame): Factor returns data. Default: None
- B (DataFrame): Factor loadings matrix (n_assets x n_factors). Default: None
Risk Parameters:
- alpha (float): Significance level for CVaR, EVaR, CDaR, EDaR. Default: 0.05
- a_sim (int): Number of CVaRs for Tail Gini approximation. Default: 100
- beta (float): Significance level for gains. Default: None (uses alpha)
- b_sim (int): Number of CVaRs for gains approximation. Default: None (uses a_sim)
- kappa (float): Deformation parameter for RLVaR/RLDaR (0-1). Default: 0.30
- kappa_g (float): Deformation parameter for RLVaR gains. Default: None
- n_max_kurt (int): Max assets for Kurtosis SDP formulation. Default: 50
Benchmark:
- kindbench (bool): True if benchmark is portfolio, False if index. Default: True
- benchindex (DataFrame): Benchmark index returns. Default: None
- benchweights (DataFrame): Benchmark weights. Default: equally weighted
Turnover/Tracking:
- allowTO (bool): Enable turnover constraints. Default: False
- turnover (float): Maximum turnover deviation. Default: 0.05
- allowTE (bool): Enable tracking error constraints. Default: False
- TE (float): Maximum tracking error deviation. Default: 0.05
Linear Constraints:
- ainequality/binequality: Linear constraint matrices A ≤ b
- arcinequality/brcinequality: Risk contribution constraint matrices
- afrcinequality/bfrcinequality: Factor risk contribution constraints
- b: Risk budgeting constraint vector
Network/Graph Constraints:
- network_sdp/cluster_sdp: SDP-based network/cluster constraint matrices
- network_ip/cluster_ip: IP-based network/cluster constraint matrices
- graph_penalty (float): SDP network constraint weight. Default: 0.05
- acentrality/bcentrality: Centrality constraint matrices
Risk Upper Bound Constraints:
- lowerret: Minimum expected return constraint
- upperdev: Max standard deviation, upperkt: Max sqrt kurtosis
- uppermad: Max MAD, uppergmd: Max GMD, uppersdev: Max semi-deviation
- upperskt: Max semi-kurtosis, upperflpm/upperslpm: Max partial moments
- upperCVaR: Max CVaR, uppertg: Max Tail Gini, upperEVaR: Max EVaR
- upperRLVaR: Max RLVaR, upperwr: Max worst realization
- uppercvrg/uppertgrg/upperevrg/upperrvrg: Max range measures
- upperrg: Max range, uppermdd: Max drawdown, upperadd: Max avg drawdown
- upperCDaR: Max CDaR, upperEDaR: Max EDaR, upperRLDaR: Max RLDaR
- upperuci: Max Ulcer Index
P-norm Parameters (for GMD, TG, TGRG approximation):
- p_1, p_2, p_3, p_4, p_5: P-norm values. Defaults: 2, 3, 4, 10, 50
"""Methods for calculating portfolio statistics using various estimation methods.
def assets_stats(self, method_mu='hist', method_cov='hist', **kwargs):
"""
Calculate expected returns and covariance matrix.
Parameters:
- method_mu (str): Method for expected returns ('hist', 'ewma1', 'ewma2')
- method_cov (str): Method for covariance ('hist', 'ewma1', 'ewma2', 'ledoit', 'oas')
"""
def blacklitterman_stats(self, P, Q, delta=None, eq=True, **kwargs):
"""
Calculate Black-Litterman expected returns and covariance.
Parameters:
- P (DataFrame): Matrix of views
- Q (DataFrame): Vector of views
- delta (float): Risk aversion parameter
- eq (bool): Use equilibrium returns
"""
def factors_stats(self, method_mu='hist', method_cov='hist', **kwargs):
"""
Calculate factor model statistics.
Parameters:
- method_mu (str): Method for expected returns
- method_cov (str): Method for covariance matrix
"""
def wc_stats(self, box='S', ellip='E', **kwargs):
"""
Calculate worst-case statistics.
Parameters:
- box (str): Box uncertainty set type
- ellip (str): Elliptical uncertainty set type
"""Core optimization methods for different portfolio models and objectives.
def optimization(self, model='Classic', rm='MV', obj='Sharpe', kelly=None, rf=0, l=2, hist=True):
"""
Optimize portfolio weights.
Parameters:
- model (str): Optimization model ('Classic', 'BL', 'FM', 'FC', 'WC', 'OWA')
- rm (str): Risk measure code
- obj (str): Objective function ('MinRisk', 'Utility', 'Sharpe', 'MaxRet')
- kelly (str): Mean return calculation (None, 'approx', 'exact')
- rf (float): Risk-free rate
- l (float): Risk aversion parameter (default: 2)
- hist (bool): Use historical scenarios
Returns:
DataFrame: Optimal portfolio weights
"""
def rp_optimization(self, model='Classic', rm='MV', rf=0, b=None, hist=True):
"""
Risk parity optimization.
Parameters:
- model (str): Optimization model
- rm (str): Risk measure
- rf (float): Risk-free rate
- b (DataFrame): Risk budgeting vector
- hist (bool): Use historical scenarios
Returns:
DataFrame: Risk parity portfolio weights
"""
def rrp_optimization(self, model='Classic', rm='MV', rf=0, b=None, hist=True):
"""
Relaxed risk parity optimization.
Parameters:
- model (str): Optimization model
- rm (str): Risk measure
- rf (float): Risk-free rate
- b (DataFrame): Risk budgeting vector
- hist (bool): Use historical scenarios
Returns:
DataFrame: Relaxed risk parity weights
"""
def wc_optimization(self, obj='Sharpe', rm='MV', rf=0, l=0):
"""
Worst-case optimization.
Parameters:
- obj (str): Objective function
- rm (str): Risk measure
- rf (float): Risk-free rate
- l (float): Risk aversion parameter
Returns:
DataFrame: Worst-case optimal weights
"""
def frc_optimization(self, rm='MV', rf=0, b=None):
"""
Factor risk constraint optimization.
Parameters:
- rm (str): Risk measure
- rf (float): Risk-free rate
- b (DataFrame): Factor risk budgets
Returns:
DataFrame: Factor risk constrained weights
"""
def owa_optimization(self, obj='Sharpe', owa_w=None, rm='MV', rf=0, l=0):
"""
OWA (Ordered Weighted Averaging) optimization.
Parameters:
- obj (str): Objective function
- owa_w (DataFrame): OWA weights vector
- rm (str): Risk measure
- rf (float): Risk-free rate
- l (float): Risk aversion parameter
Returns:
DataFrame: OWA optimal weights
"""Methods for generating and analyzing efficient frontiers.
def efficient_frontier(self, model='Classic', rm='MV', points=20, rf=0, hist=True):
"""
Generate efficient frontier.
Parameters:
- model (str): Optimization model
- rm (str): Risk measure
- points (int): Number of frontier points
- rf (float): Risk-free rate
- hist (bool): Use historical scenarios
Returns:
DataFrame: Efficient frontier weights matrix
"""
def frontier_limits(self, model='Classic', rm='MV', rf=0, hist=True):
"""
Calculate efficient frontier limits.
Parameters:
- model (str): Optimization model
- rm (str): Risk measure
- rf (float): Risk-free rate
- hist (bool): Use historical scenarios
Returns:
tuple: (min_risk_weights, max_return_weights)
"""Key properties for accessing and modifying portfolio data and constraints.
@property
def returns(self):
"""Get/set assets returns DataFrame."""
@property
def factors(self):
"""Get/set risk factors DataFrame."""
@property
def assetslist(self):
"""Get list of asset names."""
@property
def numassets(self):
"""Get number of assets."""
@property
def B(self):
"""Get/set factor loadings matrix."""
@property
def benchweights(self):
"""Get/set benchmark weights."""
@property
def ainequality(self):
"""Get/set inequality constraints matrix A."""
@property
def binequality(self):
"""Get/set inequality constraints vector b."""
@property
def kappa(self):
"""Get/set RLVaR/RLDaR deformation parameter."""Usage Example:
import riskfolio as rp
import pandas as pd
# Load returns data
returns = pd.read_csv('returns.csv', index_col=0, parse_dates=True)
# Create portfolio
port = rp.Portfolio(returns=returns)
# Calculate statistics
port.assets_stats(method_mu='hist', method_cov='hist')
# Optimize for maximum Sharpe ratio
w_sharpe = port.optimization(model='Classic', rm='MV', obj='Sharpe', rf=0.02)
# Generate efficient frontier
frontier = port.efficient_frontier(model='Classic', rm='MV', points=20)
# Risk parity portfolio
w_rp = port.rp_optimization(model='Classic', rm='MV', rf=0.02)
print("Sharpe Optimal Weights:")
print(w_sharpe.T)
print("\nRisk Parity Weights:")
print(w_rp.T)Install with Tessl CLI
npx tessl i tessl/pypi-riskfolio-lib